第3章 快取初始化程序
每次web服務啟動時都會執行快取初始化程序。前面講過存盤slot和node對應關系的slots全域變數要在快取初始化的時候被賦值,其實還有一個全域變數需要在初始化時賦值,即nodes,該變數存盤了集群所有節點的資訊。在web服務器啟動時,會呼叫RedisCacheStoreInitializationImpl的initCache()方法初始化快取集群
3.1 原始碼分析
3.1.1 RedisCacheStoreInitializationImpl的initCache()
public boolean initCache()
{
if ("TRUE".equalsIgnoreCase(this.cacheStoreConfig.getEnabled())) {
Properties props = loadProperty("jedis.properties");
props.setProperty("host", this.cacheStoreConfig.getHost());
this.isClusterEnabled = Boolean.parseBoolean(props.getProperty("enableCluster"));
if (this.isClusterEnabled)
ClusterNativeClient.init(props);
else {
SingleNativeClient.init(props);
}
this.cacheStoreConfig.setState(1);
this.log.info("Redis cache: cache started");
return true;
}
說明:
如果是集群配置,則呼叫ClusterNativeClient的init方法。
3.1.2 ClusterNativeClient的init方法
public static void init(Properties props)
{
String[] servers = props.getProperty("host").split(",");
HashSet nodes = new HashSet();
for (String s : servers) {
String[] ipAndPort = s.split(":");
String ip = ipAndPort[0];
int port = Integer.valueOf(ipAndPort[1]).intValue();
nodes.add(new HostAndPort(ip, port));
}
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(Integer.parseInt(props.getProperty("maxIdle")));
config.setMaxTotal(Integer.parseInt(props.getProperty("maxTotal")));
pool = new JedisCluster(nodes, config);
}
說明:
在init方法中會new一個JedisCluster物件。
public JedisCluster(Set<HostAndPort> nodes, GenericObjectPoolConfig poolConfig) {
this(nodes, 2000, 5, poolConfig);
}
public JedisCluster(Set<HostAndPort> jedisClusterNode, int timeout, int maxRedirections, GenericObjectPoolConfig poolConfig)
{
this.connectionHandler = new JedisSlotBasedConnectionHandler(jedisClusterNode, poolConfig, timeout);
this.maxRedirections = maxRedirections;
}
這里jedis會給兩個引數賦默認值:timeout=2000 ,maxRedirections=5。Timeout為連接超時時間,maxRedirections為最大重定向次數。
3.1.3 JedisSlotBasedConnectionHandler構造方法
public JedisSlotBasedConnectionHandler(Set<HostAndPort> nodes, GenericObjectPoolConfig poolConfig, int timeout)
{
super(nodes, poolConfig, timeout);
}
說明:
JedisSlotBasedConnectionHandler的構造方法中會繼續呼叫其父類JedisClusterConnectionHandler的構造方法。
3.1.4 JedisClusterConnectionHandler構造方法
public JedisClusterConnectionHandler(Set<HostAndPort> nodes, GenericObjectPoolConfig poolConfig, int timeout)
{
this.cache = new JedisClusterInfoCache(poolConfig, timeout);
initializeSlotsCache(nodes, poolConfig);
}
說明:
在JedisClusterConnectionHandler構造方法中會執行初始化方法initializeSlotsCache。
3.1.5 初始化方法initializeSlotsCache
private void initializeSlotsCache(Set<HostAndPort> startNodes, GenericObjectPoolConfig poolConfig) {
Iterator i$ = startNodes.iterator();
Jedis jedis;
if (i$.hasNext()) { HostAndPort hostAndPort = (HostAndPort)i$.next();
jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort()); } Iterator i$;
try { this.cache.discoverClusterNodesAndSlots(jedis);
if (jedis != null)
jedis.close();
}
catch (JedisConnectionException e)
{
if (jedis != null)
jedis.close();
}
finally
{
if (jedis != null) {
jedis.close();
}
}
while (i$.hasNext()) { HostAndPort node = (HostAndPort)i$.next();
this.cache.setNodeIfNotExist(node);
}
}
說明:
initializeSlotsCache方法告訴我們兩個資訊:1、初始化和服務器端進行互動時,是選取第一個節點并試圖和其連接,這個節點即是cache-applicationContext.xml檔案中我們配置的第一個節點。
<property name="host" value ="192.168.174.24:7000,192.168.174.24:7001,192.168.174.26:7000,192.168.174.26:7001,192.168.174.26:7002,192.168.174.24:7002"></property>
2、當發生連接例外時,例如這個節點當掉,不會拋例外,而是會直接關閉連接,并執行setNodeIfNotExist,將組態檔中的節點全部添加到nodes當中。discoverClusterNodesAndSlots方法是給slots進行初始化。其中,會呼叫clusterNodes()方法,該方法是給服務器端發送了一個cluster nodes命令,發送該命令可以獲取當前集群的基本資訊:
192.168.174.24:7001> cluster nodes
eb52be41cd51117460254a0c6a7badf6e82a3b49 192.168.174.24:7002 master - 0 1476165816509 7 connected 5461-10922
9e62f8dd7e1dc8b021dc01d9b37b58fce175022b 192.168.174.26:7001 master - 0 1476165819511 10 connected 0-5460
0b0c0bae6e73389466c178a23f6b9cba560824cb 192.168.174.26:7000 slave eb52be41cd51117460254a0c6a7badf6e82a3b49 0 1476165818511 7 connected
6df2966a46d8ee812b3757094731290604a917c1 192.168.174.24:7000 slave 9e62f8dd7e1dc8b021dc01d9b37b58fce175022b 0 1476165817509 10 connected
595bd6dd6f5e89d6a936bf9d4d8be7fef5018b80 192.168.174.26:7002 slave 2e171799e798b6f72b1d0b1396a17f6584e13b30 0 1476165820511 12 connected
2e171799e798b6f72b1d0b1396a17f6584e13b30 192.168.174.24:7001 myself,master - 0 0 12 connected 10923-16383
Jedis就是靠這個方法獲取及時的節點和slot的分配資訊然后給slots初始化。
3.1.6 JedisClusterInfoCache的discoverClusterNodesAndSlots方法:
public void discoverClusterNodesAndSlots(Jedis jedis) {
this.w.lock();
try
{
this.nodes.clear();
this.slots.clear();
String localNodes = jedis.clusterNodes();
for (String nodeInfo : localNodes.split("\n")) {
ClusterNodeInformation clusterNodeInfo = nodeInfoParser.parse(nodeInfo, new HostAndPort(jedis.getClient().getHost(), jedis.getClient().getPort()));
HostAndPort targetNode = clusterNodeInfo.getNode();
setNodeIfNotExist(targetNode);
assignSlotsToNode(clusterNodeInfo.getAvailableSlots(), targetNode);
}
} finally {
this.w.unlock();
}
}
3.1.7 assignSlotsToNode方法
public void assignSlotsToNode(List<Integer> targetSlots, HostAndPort targetNode) {
this.w.lock();
try {
targetPool = (JedisPool)this.nodes.get(getNodeKey(targetNode));
if (targetPool == null) {
setNodeIfNotExist(targetNode);
targetPool = (JedisPool)this.nodes.get(getNodeKey(targetNode));
}
for (Integer slot : targetSlots)
this.slots.put(slot, targetPool);
}
finally
{
JedisPool targetPool;
this.w.unlock();
}
}
說明:
由以上可以看出,初始化程序中,當連接的節點當機時,是不能通過cluster nodes命令獲取集群資訊的,那么slots將不能被初始化,即slots為空,而nodes則記錄了組態檔中手動輸入的集群ip資訊。但是web服務并不會因此報錯,服務會正常啟動,不過當客戶端發起快取查詢或寫入命令時,例如hget,從slots中獲取slot對應的node會取空,因此會執行getConnection()隨機選取節點進行連接。
public Jedis getConnectionFromSlot(int slot)
{
JedisPool connectionPool = this.cache.getSlotPool(slot);
if (connectionPool != null)
{
return connectionPool.getResource();
}
return getConnection();
}
3.2 總結
3.2.1 正常情況
每次web服務啟動時,都會執行快取的初始化(initCache()),初始化時會對兩個全域Map ——slots和nodes進行初始化,首先jedis會加載快取組態檔,獲取組態檔中事先寫好的第一個節點地址進行連接,并向其發送cluster nodes 命令,該命令會回傳集群節點和slot的分配資訊,然后根據回傳的結果初始化slots和nodes,該程序結束后,還會再次取組態檔中的節點地址給nodes賦值,若nodes中已經存在該值則跳過,即取集群中實際節點和配置節點的并集。這么做的目的是由于jedis在執行隨機選取節點(getConnection())時,會從nodes中選擇一個節點,若cluster nodes發送失敗,獲取不到實際的集群資訊,還可以通過組態檔獲取相應資訊,以保證快取的可用性。
3.2.2 例外情況:連接例外
當發送cluster nodes 發生連接例外時,jedis得不到實際的集群配置資訊,就不會對slots進行初始化,此時slots為null,但是會將組態檔中的節點資訊放到nodes中。以保證隨機訪問的可用性。
第4章 總結:
1、 初始化時當所連接節點當機,slots不會被初始化,即為空,nodes會記錄組態檔中的節點資訊。那么此次服務啟動后所有訪問快取的操作將不能定向訪問,會變成隨機訪問再重定向模式。
2、 當服務啟動后,訪問快取前,有節點當機,slots中的槽分配資訊就不準確了,此時會取到已經當機的節點進行連接,因此會發生連接例外,這時也會轉換成隨機訪問的模式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/119270.html
標籤:其他
上一篇:jedis原始碼分析(上)
