更新時間:2022-02-15 10:12:24 來源:動力節點 瀏覽1375次
Memcache是在服務端實現分片的分布式的緩存系統,而Redis是基于Master-Slave(主從),如果想把Reids做成分布式緩存,就要多做幾套Master-Slave,每套Master-Slave完成各自的容災處理,另外,Redis只能在客戶端完成分片。
Redis有中語言的客戶端,其中基于Java語言的客戶端叫做Jedis,Jedis客戶端已經為Redis實現了分布式存儲。下面分別介紹了Jedis分布式存儲的簡單使用以及Spring與Jedis的整合使用。
package com.ghs.test;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
public class TestShardJedis {
ShardedJedisPool pool = null;
ShardedJedis jedis = null;
@Before
public void setup(){
JedisShardInfo shardInfo1 = new JedisShardInfo("192.168.1.108");
JedisShardInfo shardInfo2 = new JedisShardInfo("...");
List<JedisShardInfo> shardInfos = new ArrayList<JedisShardInfo>();
shardInfos.add(shardInfo1);
shardInfos.add(shardInfo2);
pool = new ShardedJedisPool(new GenericObjectPoolConfig(), shardInfos);
}
@Test
public void testShard(){
jedis = pool.getResource();
//CRUD
jedis.set("name", "zhangsan");
System.out.println(jedis.get("name"));
jedis.del("name");
//釋放對象
pool.returnResource(jedis);
}
}
spring中的配置:
這里只是把示例一中JedisShardInfo和ShardJedisPool的示例交給Sping容器管理。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:property-placeholder location="classpath:redis.properties" />
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="${redis.pool.maxActive}" />
<property name="maxIdle" value="${redis.pool.maxIdle}" />
<property name="maxWait" value="${redis.pool.maxWait}" />
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
</bean>
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1">
<list>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis1.ip}" />
<constructor-arg index="1" value="${redis.port}"
type="int" />
</bean>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis2.ip}" />
<constructor-arg index="1" value="${redis.port}" type="int" />
</bean>
</list>
</constructor-arg>
</bean>
</beans>
對訪問操作進行封裝:
package com.ghs.test;
import java.util.List;
import javax.annotation.Resource;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
public class SpringRedisClient implements IRedisClient{
@Resource(name="shardedJedisPool")
private ShardedJedisPool shardedJedisPool;
private ShardedJedis getResource(){
return shardedJedisPool.getResource();
}
@Override
public String set(String key,String value){
return getResource().set(key,value);
}
@Override
public String get(String key){
return getResource().get(key);
}
@Override
public Long del(String key) {
return getResource().del(key);
}
@Override
public Long lpush(String key, String... strings) {
return getResource().lpush(key, strings);
}
@Override
public Long rpush(String key, String... strings) {
return getResource().rpush(key, strings);
}
@Override
public List<String> lrange(String key, int start, int end){
return getResource().lrange(key, start,end);
}
//…………………………
}
(1)JedisShardInfo類
這個類封裝了Redis主機的一些基本信息:
private int timeout;
private String host;
private int port;
private String password = null;
private String name = null;
最重要的是它的父類中有一個weight字段,作為本Redis服務器的權值。
這個類還有一個繼承自父類的方法createResource(),用來生成這個Redis服務器對應的Jedis對象,即往Redis服務器存取數據的對象。
(2)Sharded類
對一致性哈希算法熟悉以后,對Sharded類的理解就不難了,通過這個類來為每個分片創建虛擬節點,為每次操作獲取分片。
Sharded中的三個字段,nodes是用來模擬一致性哈希算法用的;algo是用來對字符串產生哈希值的hash函數,這里默認的是murmurhash,這個算法的隨機分布特征表現比較好;resources這個map是用來存儲JedisShardInfo與其對應的Jedis類之間的映射關系。
public static final int DEFAULT_WEIGHT = 1;
private TreeMap<Long, S> nodes;//機器節點
private final Hashing algo;
private final Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>();//每個機器節點關聯的虛擬節點
下面我們來看看初始化操作和獲取分片的操作:
//初始化操作,為每個主機管理虛擬節點
private void initialize(List<S> shards) {
nodes = new TreeMap<Long, S>();
for (int i = 0; i != shards.size(); ++i) {
final S shardInfo = shards.get(i);
if (shardInfo.getName() == null) for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo);
}
else for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
nodes.put(this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() + n), shardInfo);
}
resources.put(shardInfo, shardInfo.createResource());
}
}
在for循環中,遍歷主機列表(shards.get(i)),之后對每個主機按照單權重160的比例計算shard值,將shard值和主機信息(shardInfo)放到nodes中,將主機信息(shardInfo)和其對應的鏈接資源(Jedis)映射放入到resources中。
Weight是權重,用于調節單個主機被映射值個數,如果weight為1,那么當前主機將被映射為160個值,weight為2,當前主機將被映射為320個值,因此weight為2的節點被訪問到的概率就會高一些。
遍歷list中的每一個shardInfo,將其權重weight*160生成n,然后用名字或者編號來生成n個哈希值(這個是為了保證哈希算法的平衡性而生成的虛擬節點),然后將其和本shardInfo的對應關系存儲到treemap里面(這是在模擬一致性哈希算法中將虛擬節點映射到環上的操作),最后將shardInfo與對應的Jedis類的映射關系存儲到resources里面。
//獲取分片
public S getShardInfo(byte[] key) {
SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
if (tail.isEmpty()) {
return nodes.get(nodes.firstKey());
}
return tail.get(tail.firstKey());
}
public S getShardInfo(String key) {
return getShardInfo(SafeEncoder.encode(getKeyTag(key)));
}
首先根據傳入的key按照hash算法(默認為murmurhash)取得其value,然后用這個value到treemap中找key大于前面生成的value值的第一個鍵值對,這個鍵值對的value既是對應的shardedInfo。如果您想了解更多相關知識,可以關注一下動力節點的Java在線學習,里面的課程內容更加豐富,希望對大家能夠有所幫助。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習