Jedis分片Sentinel連接池實(shí)驗(yàn)
1.起因
眾所周知,Redis官方HA工具Sentinel已經(jīng)問世很久了,但令人費(fèi)解的是,Jedis官方卻遲遲沒有更新它的連接池。到目前Maven庫中最新的2.7.3版本為止,都只能要么使用分片連接池,要么使用不分片的Sentinel連接池。如果既進(jìn)行了Sharding,又對每組的主從實(shí)例配置Sentinel進(jìn)行監(jiān)控,怎么辦?答案是只能自己開發(fā)了,暈!還好萬能的GitHub上已經(jīng)有人提供了一個(gè)簡單可用的分片Sentinel連接池實(shí)現(xiàn),于是就直接拿來用用。
2.Sentinel配置
我的Redis是這樣配置的:一個(gè)主6379帶一個(gè)從16379,名字叫mymaster-6379;另一個(gè)主6380帶另一個(gè)從16380,名字叫mymaster-6380。Sentinel按照慣例,起三個(gè)實(shí)例26379,26380,26381。以其中一個(gè)為例,其他的Sentinel配置只是端口號不同:
port 26379
dir /tmp
sentinel monitor mymaster-6379 192.168.241.220 6379 2
sentinel down-after-milliseconds mymaster-6379 30000
sentinel parallel-syncs mymaster-6379 1
sentinel failover-timeout mymaster-6379 180000
sentinel monitor mymaster-6380 192.168.241.220 6380 2
sentinel down-after-milliseconds mymaster-6380 30000
sentinel parallel-syncs mymaster-6380 1
sentinel failover-timeout mymaster-6380 180000
如果想要Sentinel快速failover,就把sentinel down-after-milliseconds mymaster-6379 30000中的30秒改短點(diǎn),這個(gè)時(shí)間是Sentinel Ping不到Master多久后開始failover。具體Redis主從復(fù)制和Sentinel如何配置就不贅述了,請參考我以前寫的Redis主從和HA配置。
3.Jedis客戶端擴(kuò)展
3.1 測試代碼
將GitHub那個(gè)開源的連接池引到工程里,使用起來很簡單,只需通過構(gòu)造方法傳入所有Sentinel實(shí)例的連接地址以及master的名字列表。連接池配置可以使用默認(rèn)的。
注意:一定要每次重新從pool中取一個(gè)連接,否則會(huì)一直訪問老master。
public static void main(String[] args) throws Exception {
Set<String> sentinels = new HashSet<String>();
sentinels.add("192.168.111.222:26379");
sentinels.add("192.168.111.222:26380");
sentinels.add("192.168.111.222:26381");
ShardedJedisSentinelPool pool = new ShardedJedisSentinelPool(
Arrays.asList("mymaster-6379", "mymaster-6380"),
sentinels
);
while (true) {
String ts = new SimpleDateFormat("hh:mm:ss").format(new Date());
ShardedJedis jedis = pool.getResource();
try {
System.out.println(ts + ": hello=" + jedis.get("hello"));
} catch (Exception e) {
System.out.println(ts + ": Cannot connect to Redis");
} finally {
jedis.close();
}
Thread.sleep(500L);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
3.2 連接泄露Bug
循環(huán)時(shí)發(fā)現(xiàn)ShardedJedisSentinelPool連接池有時(shí)竟然滿了!畢竟默認(rèn)是8個(gè)連接,但為什么會(huì)這樣?查了下代碼,果然是這個(gè)原因!這個(gè)新擴(kuò)展的pool沒有為getResource()出來的ShardedJedis設(shè)置DataSource,所以關(guān)閉ShardedJedis時(shí)沒法正常還回到連接池中。
補(bǔ)丁很簡單,就是參照J(rèn)edis的ShardedJedisPool,覆寫一些資源管理的方法。
@Override
public ShardedJedis getResource() {
ShardedJedis jedis = super.getResource();
jedis.setDataSource(this);
return jedis;
}
@Override
public void returnBrokenResource(final ShardedJedis resource) {
if (resource != null) {
returnBrokenResourceObject(resource);
}
}
@Override
public void returnResource(final ShardedJedis resource) {
if (resource != null) {
resource.resetState();
returnResourceObject(resource);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
3.3 KeyTag
此外,ShardedJedisSentinelPool的構(gòu)造方法中沒提供傳入KeyTagPattern的地方,這也需要手動(dòng)傳入到ShardedJedisFactory中。需要的話,自己動(dòng)手加一下吧!
4.Failover實(shí)驗(yàn)
以key=hello為例,事先在6380實(shí)例上準(zhǔn)備好key的值。
4.1 實(shí)驗(yàn)結(jié)果
首先,啟動(dòng)Java程序不斷訪問。然后,在后臺kill掉6380那個(gè)Redis實(shí)例??刂婆_輸出會(huì)警告無法連接Redis。大概30秒左右,本地連接池注冊的Sentinel監(jiān)聽器會(huì)收到failover消息。于是,從pool.getResource()拿到的連接自動(dòng)切換到Slave實(shí)例了,又可以查詢到hello對應(yīng)的值了。
10:13:42: hello=nihaonihao111
10:13:42: Cannot connect to Redis
10:13:43: Cannot connect to Redis
10:13:45: Cannot connect to Redis
10:13:48: Cannot connect to Redis
10:13:50: Cannot connect to Redis
10:13:53: Cannot connect to Redis
10:13:56: Cannot connect to Redis
10:13:58: Cannot connect to Redis
10:14:01: Cannot connect to Redis
10:14:03: Cannot connect to Redis
10:14:06: Cannot connect to Redis
10:14:08: Cannot connect to Redis
10:14:11: Cannot connect to Redis
八月 24, 2015 10:14:13 上午 redis.clients.jedis.ShardedJedisSentinelPool initPool
信息: Created ShardedJedisPool to master at [192.168.111.222:6379 192.168.111.222:16380 ]
10:14:13: hello=nihaonihao111
10:14:14: hello=nihaonihao111
10:14:14: hello=nihaonihao111
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
4.2 如何恢復(fù)
如果想反復(fù)試驗(yàn)怎么辦?很簡單,重新啟動(dòng)6380實(shí)例,但此時(shí)16380已經(jīng)變成了Master,6380成了它的Slave。沒關(guān)系!隨便連接到一個(gè)Sentinel實(shí)例上,執(zhí)行sentinel failover mymaster-6380就可以強(qiáng)制failover一次。這樣6380和16380的主從關(guān)系就又恢復(fù)了!
5.總結(jié)
總體來說這個(gè)連接池還是不錯(cuò)的,雖然有點(diǎn)小問題,但免去了開發(fā)的麻煩!不知道用于生產(chǎn)環(huán)境的話,是否還有其他“坑”,不管怎樣可以先用著,感謝作者的分享!
|