当前位置: 首页 > news >正文

Redission分布式锁 watch dog 看门狗机制

为了避免Redis实现的分布式锁超时,Redisson中引入了watch dog的机制,他可以帮助我们在Redisson实例被关闭前,不断的延长锁的有效期。

  • 自动续租:当一个Redisson客户端实例获取到一个分布式锁时,如果没有指定锁的超时时间,Watchdog会基于Netty的时间轮启动一个后台任务,定期向Redis发送命令,重新设置锁的过期时间,通常是锁的租约时间的1/3。这确保了即使客户端处理时间较长,所持有的锁也不会过期。
  • 每次续期的时长:默认情况下,每10s钟做一次续期,续期时长是30s。
  • 停止续期:当锁被释放或者客户端实例被关闭时,Watchdog会自动停止对应锁的续租任务。

💖 底层实现

👨‍🏫 RedissonBaseLock.renewExpiration()

protected void scheduleExpirationRenewal(long threadId) {// 创建一个新的过期续期条目ExpirationEntry entry = new ExpirationEntry();// 尝试将新的过期续期条目放入到过期续期映射中,如果已存在则不替换ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);if (oldEntry != null) {// 如果已存在,则将线程ID添加到旧的过期续期条目中oldEntry.addThreadId(threadId);} else {// 如果是新的过期续期条目,则添加线程ID,并尝试续期entry.addThreadId(threadId);try {// 尝试续期renewExpiration();} finally {// 如果当前线程被中断,则取消续期if (Thread.currentThread().isInterrupted()) {cancelExpirationRenewal(threadId);}}}
}// 定时任务执行续期
private void renewExpiration() {// 从过期续期映射中获取当前的过期续期条目ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ee == null) {// 如果没有找到,则直接返回return;}// 创建一个新的定时任务,用于执行续期逻辑Timeout task = getServiceManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {// 再次检查过期续期条目是否仍然存在ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ent == null) {return;}// 获取线程IDLong threadId = ent.getFirstThreadId();if (threadId == null) {return;}// 使用LUA脚本异步续期CompletionStage<Boolean> future = renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) {// 如果有异常发生,记录错误并从映射中移除过期续期条目log.error("Can't update lock {} expiration", getRawName(), e);EXPIRATION_RENEWAL_MAP.remove(getEntryName());return;}if (res) {// 如果续期成功,则重新调度续期任务renewExpiration();} else {// 如果续期失败,则取消续期cancelExpirationRenewal(null);}});}}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);// 将定时任务与过期续期条目关联ee.setTimeout(task);
}// 使用LUA脚本,进行续期
protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {// 使用evalWriteAsync方法异步执行LUA脚本,用于续期return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return 1; " +"end; " +"return 0;",Collections.singletonList(getRawName()),internalLockLeaseTime, getLockName(threadId));
}

可以看到,上面的代码的主要逻辑就是用了一个TimerTask来实现了一个定时任务,设置了internalLockLeaseTime / 3的时长进行一次锁续期。默认的超时时长是30s,那么他会每10s进行一次续期,通过LUA脚本进行续期,再续30s

不过,这个续期也不是无脑续,他也是有条件的,其中ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());这个值得我们关注,他会从EXPIRATION_RENEWAL_MAP中尝试获取一个KV对,如果查不到,就不续期了。

EXPIRATION_RENEWAL_MAP这个东西,会在unlock的时候操作的,对他进行remove,所以一个锁如果被解了,那么就不会再继续续期了

@Override
public void unlock() {try {// 异步执行解锁操作get(unlockAsync(Thread.currentThread().getId()));} catch (RedisException e) {// 检查异常是否是由于线程没有持有锁导致的if (e.getCause() instanceof IllegalMonitorStateException) {// 如果是,则抛出原始的 IllegalMonitorStateException异常throw (IllegalMonitorStateException) e.getCause();} else {// 如果不是,则抛出原始的RedisException异常throw e;}}
}@Override
public RFuture<Void> unlockAsync(long threadId) {// 使用getServiceManager执行解锁操作return getServiceManager().execute(() -> unlockAsync0(threadId));
}private RFuture<Void> unlockAsync0(long threadId) {// 异步执行解锁操作CompletionStage<Boolean> future = unlockInnerAsync(threadId);// 处理异步操作的结果或异常CompletionStage<Void> f = future.handle((opStatus, e) -> {// 取消续期任务cancelExpirationRenewal(threadId);if (e != null) {// 如果有异常发生,抛出CompletionExceptionif (e instanceof CompletionException) {throw (CompletionException) e;}throw new CompletionException(e);}if (opStatus == null) {// 如果解锁操作失败,抛出IllegalMonitorStateExceptionIllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "+ id + " thread-id: " + threadId);throw new CompletionException(cause);}return null;});// 将CompletableFuture包装为RFuturereturn new CompletableFutureWrapper<>(f);
}protected void cancelExpirationRenewal(Long threadId) {// 从过期续期映射中获取过期续期条目ExpirationEntry task = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (task == null) {// 如果没有找到,则直接返回return;}if (threadId != null) {// 如果线程ID不为空,则从过期续期条目中移除该线程IDtask.removeThreadId(threadId);}if (threadId == null || task.hasNoThreads()) {// 如果线程ID为空或者过期续期条目中没有线程ID,则取消定时任务Timeout timeout = task.getTimeout();if (timeout != null) {timeout.cancel();}// 从过期续期映射中移除过期续期条目EXPIRATION_RENEWAL_MAP.remove(getEntryName()); // 取消续期关键点}
}

核心:EXPIRATION_RENEWAL_MAP.remove(getEntryName());

一次unlock过程中,对EXPIRATION_RENEWAL_MAP进行移除,进而取消下一次锁续期的实现细节。

并且在unlockAsync方法中,不管unlockInnerAsync是否执行成功,还是抛了异常,都不影响cancelExpirationRenewal的执行,也可以理解为,只要unlock方法被调用了,即使解锁未成功,那么也可以停止下一次的锁续期。

💖 续期

加锁代码

/*** 尝试异步获取分布式锁。** @param waitTime      等待获取锁的最大时间,如果设置为-1,则表示无限等待。* @param leaseTime     锁的过期时间,如果设置为-1,则表示使用默认的过期时间。* @param unit          时间单位,用于将leaseTime转换为毫秒。* @param threadId      当前线程的唯一标识符。* @return              一个RFuture对象,表示异步操作的结果,如果成功获取锁,则返回剩余的过期时间(毫秒)。* @throws InterruptedException 如果当前线程在等待过程中被中断。*/
private RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {// 尝试获取锁的异步方法RFuture<Long> ttlRemainingFuture;// 如果锁的过期时间大于0,则使用指定的过期时间if (leaseTime > 0) {ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else {// 如果锁的过期时间不大于0,则使用内部锁的过期时间ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);}// 处理没有同步获取锁的情况CompletionStage<Long> s = handleNoSync(threadId, ttlRemainingFuture);// 将处理后的CompletionStage包装为RFuturettlRemainingFuture = new CompletableFutureWrapper<>(s);// 当ttlRemainingFuture完成时,如果ttlRemaining为null,则表示锁已成功获取CompletionStage<Long> f = ttlRemainingFuture.thenApply(ttlRemaining -> {// 锁已获取if (ttlRemaining == null) {// 如果锁的过期时间大于0,则设置锁的过期时间if (leaseTime > 0) {internalLockLeaseTime = unit.toMillis(leaseTime);} else {// 如果锁的过期时间不大于0,则安排锁的过期时间续期scheduleExpirationRenewal(threadId);}}// 返回ttlRemaining,如果为null,则表示锁已获取return ttlRemaining;});// 将处理后的CompletionStage包装为RFuturereturn new CompletableFutureWrapper<>(f);
}

在这里插入图片描述

💖 停止续期

如果一个锁的unlock方法被调用了,那么就会停止续期。

那么,取消续期的核心代码如下:

/*** 取消与锁关联的自动续期任务。** @param threadId 如果不为null,则只取消与特定线程ID关联的续期任务。*/
protected void cancelExpirationRenewal(Long threadId) {// 从过期续期映射中获取当前的过期续期条目ExpirationEntry task = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (task == null) {// 如果没有找到对应的续期条目,则直接返回return;}if (threadId != null) {// 如果提供了线程ID,则从续期条目中移除该线程IDtask.removeThreadId(threadId);}if (threadId == null || task.hasNoThreads()) {// 如果没有提供线程ID,或者续期条目中没有其他线程ID,则取消定时任务Timeout timeout = task.getTimeout();if (timeout != null) {// 取消定时任务timeout.cancel();}// 从过期续期映射中移除过期续期条目EXPIRATION_RENEWAL_MAP.remove(getEntryName());}
}

主要就是通过 EXPIRATION_RENEWAL_MAP.remove来做的。那么cancelExpirationRenewal还有下面一处调用:

protected void scheduleExpirationRenewal(long threadId) {ExpirationEntry entry = new ExpirationEntry();ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);if (oldEntry != null) {oldEntry.addThreadId(threadId);} else {entry.addThreadId(threadId);try {renewExpiration();} finally {if (Thread.currentThread().isInterrupted()) {cancelExpirationRenewal(threadId);}}}
}

也就是说,在尝试开启续期的过程中,如果线程被中断了,那么就会取消续期动作了。

目前,Redisson是没有针对最大续期次数和最大续期时间的支持的。所以,正常情况下,如果没有解锁,是会一直续期下去的。


💖 客户端挂了,锁会不会一直续期?

Redission 是 redis 的客户端

在这里插入图片描述

相关文章:

Redission分布式锁 watch dog 看门狗机制

为了避免Redis实现的分布式锁超时&#xff0c;Redisson中引入了watch dog的机制&#xff0c;他可以帮助我们在Redisson实例被关闭前&#xff0c;不断的延长锁的有效期。 自动续租&#xff1a;当一个Redisson客户端实例获取到一个分布式锁时&#xff0c;如果没有指定锁的超时时…...

人脸识别系统架构

目录 1. 系统架构 1.1 采集子系统 1.2 解析子系统 1.3 存储子系统 1.4 比对子系统 1.5 决策子系统 1.6 管理子系统 1.7 应用开放接口 2. 业务流程 2.1 人脸注册 2.2 人脸验证 2.2.1 作用 2.2.2 特点 2.2.3 应用场景 2.3 人脸辨识 2.3.1 作用 2.3.2 特点 2.3.3…...

数塔问题(蛮力算法和动态规划)

题目&#xff1a;如下图是一个数塔&#xff0c;从顶部出发在每一个节点可以选择向左或者向右走&#xff0c;一直走到底层&#xff0c;要求找出一条路径&#xff0c;使得路径上的数字之和最大&#xff0c;及路径情况。(使用蛮力算法和动态规划算法分别实现&#xff09; #include…...

启动 Redis 服务和连接到 Redis 服务器

启动 Redis 服务和连接到 Redis 服务器的步骤通常依赖于你的操作系统和 Redis 的安装方式。以下是一些常见的步骤&#xff1a; ### 启动 Redis 服务 对于大多数 Linux 发行版&#xff0c;Redis 服务可以通过以下命令启动&#xff1a; 1. 如果 Redis 是通过包管理器安装的&am…...

我独自升级崛起在哪下载 我独自升级电脑PC端下载教程分享

将于5月8日在全球舞台闪亮登场的动作角色扮演游戏《我独自升级崛起》&#xff0c;灵感源自同名热门动画与网络漫画&#xff0c;承诺为充满激情的游戏玩家群体带来一场集深度探索与广阔体验于一身的奇幻旅程。该游戏以独特的网络武侠世界观为基底&#xff0c;展现了一位普通人踏…...

STM32F4xx开发学习—GPIO

GPIO 学习使用STM32F407VET6GPIO外设 寄存器和标准外设库 1. 寄存器 存储器映射 存储器本身是不具有地址的&#xff0c;是一块具有特定功能的内存单元&#xff0c;它的地址是由芯片厂商或用户分配&#xff0c;给存储器分配地址的过程就叫做存储区映射。给内存单元分配地址之后…...

引领农业新质生产力,鸿道(Intewell®)操作系统助力农业机器人创新发展

4月27日至29日&#xff0c;2024耒耜国际会议在江苏大学召开。科东软件作为特邀嘉宾出席此次盛会&#xff0c;并为江苏大学-科东软件“农业机器人操作系统”联合实验室揭牌。 校企联合实验室揭牌 在开幕式上&#xff0c;江苏大学、科东软件、上交碳中和动力研究院、遨博智能研究…...

扩展学习|一文读懂知识图谱

一、知识图谱的技术实现流程及相关应用 文献来源&#xff1a;曹倩,赵一鸣.知识图谱的技术实现流程及相关应用[J].情报理论与实践,2015, 38(12):127-132. &#xff08;一&#xff09;知识图谱的特征及功能 知识图谱是为了适应新的网络信息环境而产生的一种语义知识组织和服务的方…...

ubuntu中的docker记录(3)——如何安装nvidia-docker以更好地支持GPU加速计算应用程序的运行

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、nvidia-docker2的安装1. 安装docker2. 安装nvidia-docker2(1) 添加密钥(2) 更新软件列表(3) 安装nvidia-docker2(4) 测试nvidia-docker2 二、可能的报错及解…...

MLP实现fashion_mnist数据集分类(1)-模型构建、训练、保存与加载(tensorflow)

1、查看tensorflow版本 import tensorflow as tfprint(Tensorflow Version:{}.format(tf.__version__)) print(tf.config.list_physical_devices())2、fashion_mnist数据集下载与展示 (train_image,train_label),(test_image,test_label) tf.keras.datasets.fashion_mnist.l…...

ChatGPT-税收支持新质生产力

Prompt: 税收发展助力新质生产力 Response: 是的&#xff0c;税收发展可以促进新质生产力的发展。通过税收政策的调整和优化&#xff0c;政府可以提供更好的创新环境&#xff0c;激发企业投资研发&#xff0c;推动新技术、新产品的出现&#xff0c;从而推动经济结构升级和新…...

Linux下深度学习虚拟环境的搭建与模型训练

在深度学习实践中&#xff0c;环境配置是十分重要且免不了的一步。本文以 YOLOv4 模型&#xff0c;介绍在Linux下虚拟环境配置到模型训练的过程。 安装Miniconda&#xff1a; Miniconda是Anaconda的一个轻量级版本&#xff0c;非常适合用于科学计算和数据处理。 wget https:…...

Map-Reduce是个什么东东?

MapReduce是一种用于使用并行分布式算法在集群计算机上处理大型数据集的编程模型及其相关实现。这一概念首先由Google普及&#xff0c;并随后作为Apache Hadoop项目的一部分开源发布。 MapReduce的基本工作流程&#xff1a; 映射(Mapping)&#xff1a;这是第一阶段&#xff0c…...

上位机工作感想-从C#到Qt的转变-2

2.技术总结 语言方面 最大收获就是掌握了C Qt编程&#xff0c;自己也是粗看了一遍《深入理解计算机系统》&#xff0c;大致了解了计算机基本组成、虚拟内存、缓存命中率等基基础知识&#xff0c;那本书确实有的部分看起来很吃力&#xff0c;等这段时间忙完再研读一遍。对于封装…...

【C++】C++ 中 的 lambda 表达式(匿名函数)

C11 引入的匿名函数&#xff0c;通常被称为 Lambda 函数&#xff0c;是语言的一个重要增强&#xff0c;它允许程序员在运行时创建简洁的、一次性使用的函数对象。Lambda 函数的主要特点是它们没有名称&#xff0c;但可以捕获周围作用域中的变量&#xff0c;这使得它们非常适合在…...

OpenSSL实现AES-CBC加解密,可一次性加解密任意长度的明文字符串或字节流(QT C++环境)

本篇博文讲述如何在Qt C的环境中使用OpenSSL实现AES-CBC-Pkcs7加/解密&#xff0c;可以一次性加解密一个任意长度的明文字符串或者字节流&#xff0c;但不适合分段读取加解密的&#xff08;例如&#xff0c;一个4GB的大型文件需要加解密&#xff0c;要分段读取&#xff0c;每次…...

cURL:命令行下的网络工具

序言 在当今互联网时代&#xff0c;我们经常需要与远程服务器通信&#xff0c;获取数据、发送请求或下载文件。在这些情况下&#xff0c;cURL 是一个强大而灵活的工具&#xff0c;它允许我们通过命令行进行各种类型的网络交互。本文将深入探讨 cURL 的基本用法以及一些高级功能…...

Baumer工业相机堡盟工业相机如何通过NEOAPISDK查询和轮询相机设备事件函数(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPISDK查询和轮询相机设备事件函数&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机NEOAPI SDK和相机设备事件的技术背景Baumer工业相机通过NEOAPISDK在相机中查询和轮询相机设备事件函数功能1.引用合适的类文件2.通过NEOAPISDK…...

Day45代码随想录动态规划part07:70. 爬楼梯(进阶版)、322. 零钱兑换、279.完全平方数、139.单词拆分

Day45 动态规划part07 完全背包 70. 爬楼梯&#xff08;进阶版&#xff09; 卡码网链接&#xff1a;57. 爬楼梯&#xff08;第八期模拟笔试&#xff09; (kamacoder.com) 题意&#xff1a;假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬至多m (1 < m < n)个…...

土壤重金属含量分布、Cd镉含量、Cr、Pb、Cu、Zn、As和Hg、土壤采样点、土壤类型分布

土壤是人类赖以生存和发展的重要资源之一,也是陆地生态系统重要的组成部分。近年来, 随着我国城市化进程加快&#xff0c;矿产资源开发、金属加工冶炼、化工生产、污水灌溉以及不合理的化肥农药施用等因素导致重金属在农田土壤中不断富集。重金属作为土壤环境中一种具有潜在危害…...

力扣:100284. 有效单词(Java)

目录 题目描述&#xff1a;输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 有效单词 需要满足以下几个条件&#xff1a; 至少 包含 3 个字符。 由数字 0-9 和英文大小写字母组成。&#xff08;不必包含所有这类字符。&#xff09; 至少 包含一个 …...

如何快速掌握DDT数据驱动测试?

前言 网盗概念相同的测试脚本使用不同的测试数据来执行&#xff0c;测试数据和测试行为完全分离&#xff0c; 这样的测试脚本设计模式称为数据驱动。(网盗结束)当我们测试某个网站的登录功能时&#xff0c;我们往往会使用不同的用户名和密码来验证登录模块对系统的影响&#x…...

OpenCV如何实现背投(58)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV直方图比较(57) 下一篇&#xff1a;OpenCV如何模板匹配(59) 目标 在本教程中&#xff0c;您将学习&#xff1a; 什么是背投以及它为什么有用如何使用 OpenCV 函数 cv::calcBackP…...

5-在Linux上部署各类软件

1. MySQL 数据库安装部署 1.1 MySQL 5.7 版本在 CentOS 系统安装 注意&#xff1a;安装操作需要 root 权限 MySQL 的安装我们可以通过前面学习的 yum 命令进行。 1.1.1 安装 配置 yum 仓库 # 更新密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022# 安装Mysql…...

【Jenkins】持续集成与交付 (八):Jenkins凭证管理(实现使用 SSH 、HTTP克隆Gitlab代码)

🟣【Jenkins】持续集成与交付 (八):Jenkins凭证管理(实现使用 SSH 、HTTP克隆Gitlab代码) 1、安装Credentials Binding、git插件2、凭证类型及用途3、(用户名和密码类型)凭证的添加和使用3.1 用户密码类型3.2 测试凭证是否可用3.3 开始构建项目3.3 查看结果(进入Jenk…...

开源模型应用落地-CodeQwen模型小试-SQL专家测试(二)

一、前言 代码专家模型是基于人工智能的先进技术&#xff0c;它能够自动分析和理解大量的代码库&#xff0c;并从中学习常见的编码模式和最佳实践。这种模型可以提供准确而高效的代码建议&#xff0c;帮助开发人员在编写代码时避免常见的错误和陷阱。 通过学习代码专家模型&…...

Arch Linux安装macOS

安装需要的包 sudo pacman -S qemu-full libvirt virt-manager p7zip yay -S dmg2img安装步骤 cd ~ git clone --depth 1 --recursive https://github.com/kholia/OSX-KVM.git cd OSX-KVM # 选择iOS版本 ./fetch-macOS.py #将上一步下载的BaseSystem.dmg转换格式 dmg2img -…...

接口自动化框架篇:Pytest + Allure报告企业定制化实现!

接口自动化框架是现代软件开发中的重要组成部分&#xff0c;能够帮助开发团队提高测试效率和质量。本文将介绍如何使用Pytest作为测试框架&#xff0c;并结合Allure报告进行企业定制化实现。 目标规划 在开始编写接口自动化测试框架之前&#xff0c;我们需要先进行目标规划。…...

保持 Hiti 证卡打印机清洁的重要性和推荐的清洁用品

在证卡印刷业务中&#xff0c;保持印刷设备的清洁至关重要。特别是对于 Hiti 证卡打印机来说&#xff0c;它们是生产高质量证卡的关键工具。保持设备清洁不仅可以保证打印质量和效率&#xff0c;还可以延长其使用寿命。本文将探讨保持 Hiti 证卡打印机清洁卡的重要性&#xff0…...

Unity C#的底层原理概述

文章目录 前言IL与IL2CPP总结 前言 看到底层二字&#xff0c;会感到很高深&#xff0c;好似下一秒就要踏入深渊。实际上&#xff0c;对于C#底层的理解非常简单&#xff0c;比冒泡排序这种基础算法还要简单。 底层的两种机制&#xff1a;Mono和IL2CPP。 IL2CPP其中的"2&qu…...

小说网站怎么做权重/百度优化排名

来自 莫等闲也&#xff0c;原文 // 获取程序的基目录。 System.AppDomain.CurrentDomain.BaseDirectory // 获取模块的完整路径。 System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName // 获取和设置当前目录(该进程从中启动的目录)的完全限定目录。 System.…...

个人主页网站制作免费/培训心得简短200字

2019独角兽企业重金招聘Python工程师标准>>> 一、bucket数据结构介绍。 struct crush_bucket { __s32 id; //bucket的ID号&#xff0c;crushmap中所有bucket的编号都是负数 __u16 type; //bucket的类型(uniform/list/tree/straw/straw2) __u…...

非模板网站/seo工具网站

一、硬核! 30张图解HTTP常见面试题 在面试过程中。HTTP被提问的概率还是比较高的。小编我授集了5大类HTTP面试常问的题目&#xff0c;同时这5大类题跟HTTP的发展和演变关联性是比较大的&#xff0c;通过问答图解的形式由浅入深的方式帮助大家进一步的学习和理解 HTTP . HTTP基…...

西安响应式网站/百度手机app下载安装

Eclipse 多国语言包中不仅有Eclipse 的中文翻译,同时也包含了其他几种主要语言的翻译,Eclipse 能够自动根据Windows 操作系统的语言环境来选择使用语言包中的那一种语言,极具智能化。从Eclipse 的官方网站&#xff08;www.eclipse.org&#xff09;下载3.0.1版Eclipse 的相应的多…...

怎么给网站做友情链接/免费建站的网站哪个好

明天就要面试了&#xff0c;临时抱佛脚&#xff0c;真的很虚啊&#xff0c;好久没刷题了。人外有人天外有天要相信水平无上限 1. Crack the code interview 别人的总结 http://www.hawstein.com/posts/ctci-solutions-contents.html 我的总结转载于:https://www.cnblogs.com/Gr…...

贵州省住房和城乡建设厅网站报名网/宁波seo排名优化哪家好

原文 https://stackoverflow.com/questions/34054780/how-can-mongodb-datasize-be-larger-than-storagesize据我所知,MongoDB的存储大小应始终大于数据大小.但是,升级到Mongo 3.0并使用WiredTiger后,我开始看到数据大小大于存储大小. 这是来自其中一个数据库&#xff1a; { …...