Jedis的主要设计与实现
Redis地址:https://github.com/redis/redis
Jedis地址: https://github.com/redis/jedis
Jedis是Redis的Java客户端的一种实现,支持单机版Redis、Redis Cluster、Redis Sentinel等三种模式,以及客户端逻辑分片的ShardedJedis。
Jedis实例有3种请求模式,Pipeline,Transaction和Client。其实就是和Redis的普通client、Redis的Pipeline、Redis的事务三者大体对应。
Client
Client就是常见的一问一答模式,客户端发送一个命令,阻塞等待服务端执行,然后读取返回结果。
Pipeline
Pipeline模式是一次性发送多条命令给服务端,服务端依次处理完毕后,一条响应将结果一次性返回。Pipeline以队列的形式实现,命令是依次执行的。
Transaction
Transaction主要表示Redis的事务操作,开启事务后,命令并不是立即被执行的,而是会进入一个事务队列中,然后返回QUEUED
,表示事务已入对队。在执行EXEC
命令后以队列的形式返回给客户端。
非事务状态下,redis每个命令都是一个执行单位;事务状态,一个事务是一个执行单位。
MULTI
命令的执行标记着事务的开始:DISCARD
命令用于取消事务。它清空整个事务队列,将事务状态调整为非事务状态。WATCH
需要在事务外执行,用于监视任意数量的键(watch(String... keys)
)。当调用exec开始事务时,如果被监视的键被其他客户端修改了,那么整个事务被丢弃,调用exec
返回nil
。EXEC
命令触发事务,以FIFO的方式,执行事务队列中的所有命令,并将命令执行得到的回复队列作为自己的结果返回给客户端。
redis中并没有事务的回滚,只有中断机制。除了上述的四个命令外,还可以使用lua脚本去执行一组原子性的命令。在语义上和MULTI
/EXEC
是一样的。
Jedis的大体结构
Jedis继承自BinaryJedis,BinaryJedis持有一个Client、Transaction、Pipeline。 Client负责最基础的Socket的维护和访问。Client继承自Connection,Connection中持有一个Socket实例和OutputStream、InputStream两个流。
JedisPool
提供了池化的Jedis
对象。Jedis
作为单个连接的操作入口,提供了Jedis
支持的所有的API。BinaryJedis
聚合了Client
、Transaction
、Pipeline
等功能对象,是Jedis
的二进制版本。Client
封装了对Redis服务端的API访问。Connection
同Socket通信实现了和Redis服务端的访问能力。Protocol
封装了redis协议,Client
发送命令后,经过Protocol
的包装,以符合Redis规范的格式请求到Redis服务端。
Jedis如何通过JedisPool管理连接
Jedis的连接池技术用的是commons-pool2
实现的,核心一是定义池对象工厂PooledObjectFactory
,二是定义对象池Object Pool
。
池对象工厂PooledObjectFactory
负责创建池对象PooledObject
,我们从对象池Object Pool
,对象池会委托池对象工厂生产/复用池对象。
Jedis代表一个操作redis的连接,Jedis本身并不是线程安全的,通过JedisPool来保证线程安全。
个人实现demo:https://github.com/RussXia/easy-jedis
具体可以参考:
Redis的三种集群模式
Redis支持三种集群模式:
- Cluster 模式
- 主从复制模式
- Sentinel(哨兵)模式
RedisCluster分片集群
RedisCluster提供集群分片功能,RedisCluster的分片并不是一致性hash,而是引入了hash槽的概念。
Redis集群默认有16384(2^14)个hash槽,集群中的每个节点负责一部分hash槽。存储元素时,slot = CRC16(key) & 16383
计算key落在哪个槽。
比如有A(1-5000)、B(5001-10000)、C(10001-16384)时,如果B节点宕机/失去联系了,那么集群中5001-10000这个范围内的hash槽是不可用,但不影响A(1-5000)、C(10001-16384)这两个节点的正常使用
因此,Redis集群的优势:
- 数据分散在不同的节点上
- 集群中部分节点不可用的情况下,不会影响其他可用节点的使用
Redis的主从复制
为了避免单点故障,Redis提供了主从复制功能。集群分为两类角色,master节点和slave节点。master提供读写功能,slave只提供读功能。
主节点和从节点之间的数据同步通过master-slave replication
(主从复制)功能实现。
-
Redis的主从复制是异步的,同步数据时不会影响主节点的读写
- 如果主节点宕机,需要手动切换master节点;
- 主节点宕机时,未来得及同步的数据可能会丢失。
- Slave重启或新增时,会发送sync请求和主节点全量同步;如果多个slave同时重启,会影响主节点的IO。
Sentinel(哨兵)模式
Redis的redis-sentinel是一个独立运行的进程,是用来监控redis集群,做到master-slave高可用(High-Available)的工具。当集群中master节点不可用时,sentinel会自动选举一个新的master节点继续处理服务。Sentinel模式为master-slave replication
提供了节点发现、自动故障切换、master节点提升的能力。
- 主观下线(Subjectively Down):单个Sentinel实例在特定时间内无法得到服务器的响应,该单个sentinel该这个服务器标记为主观下线。
- 客观下线(Objectively Down):多个Sentinel实例对同一个服务器都作出了主观下线的判断,该服务器则被标记为客观下线。
当某个Sentinel监视到某个服务主观下线后,该Sentinel会询问其他Sentinel该服务是否也是主观下线。如果达到指定数量的Sentinel都认为该服务主观下线,则该服务被标记为客观下线,并对其做故障转移。
Redis的集群
Jedis支持ShardedJedis、JedisCluster、JedisSentinel等多种集群方式。其中Sentinel这一模式,是保证redis的HA和failover的。
而ShardedJedis、JedisCluster、codis等则是对数据进行分片。
- ShardedJedis:
ShardedJedis
是redis最开始不支持集群时,客户端实现的一个数据分片的集群方式。ShardedJedis
首先对key通过一致性hash计算对应的redis实例。 - JedisCluster:JedisCluster是根据redis cluster特性实现的Java客户端,redis cluster的分片实现是hash槽:
slot = CRC16(key) & 16383
。 - Codis:Codis是一个支持redis集群分片的中间件,Codis会保存分片和redis集群的对应关系。Codis可以看成一个proxy,客户端请求Codis,Codis自动将其转发到对应分片的redis实例上。