wordpress换主题后,seo知名公司,成免费crm特色学生版的特点,wordpress 版权说明#xff1a; 下面的示例基本都是基于Linux去实现#xff0c;目的是为了环境的统一#xff0c;以便于把性能调整到最优。且基于Java。建议生产环境不要使用Windows/Mac OS这些。 在Java领域#xff0c;基于客户端进行分片最常用的库应该是Jedis#xff0c;下面基本是基… 说明 下面的示例基本都是基于Linux去实现目的是为了环境的统一以便于把性能调整到最优。且基于Java。建议生产环境不要使用Windows/Mac OS这些。 在Java领域基于客户端进行分片最常用的库应该是Jedis下面基本是基于Jedis进行实例实践。当然除了这个还可以基于自己的业务去实现。 现在官方已经出到了4.0版本也同样支持了集群功能那么现在市面上基本不用客户端去实现分片做集群主要集中在服务端来达到高可用的Redis集群所以是否有必要客户端去实现集群需要在自己的业务上来深入考究。 同样的除了官方集群外还有很多成熟的方案去实现服务端集群比如推特、豌豆荚这些官方开源的方案等。 在客户端进行分片来达到集群的效果最简单的理解应该是这样A和B两个Key通过Hash得到A放在Redis1B放在Redis2中。先忽略Redis其中一台挂掉的问题对于算法远没有在这里说的那么简单。 分片的大致原理都是基于Hash算法来制定哪个Key放到哪个Redis中。 在这篇http://www.cnblogs.com/EasonJim/p/7625738.html文章中提到的几款客户端中都已经实现了分片的操作。 下面是基于Jedis去实现了客户端分片功能配置 对于单实例的Redis的使用我们可以用Jedis并发环境下我们可以用JedisPool。但是这两种方法否是针对于单实例的Redis的情况下使用的但是有时候我们的业务可能不是单实例Redis能支撑的那么我们这时候需要引入多个实例进行“数据分区”。其实好多人都说用Redis集群不就搞定了吗但是Redis集群无论部署还是维护成本都比较高对于一些业务来说使用起来还是成本很高。所以对我们来说更好的方案可能是在客户端实现对数据的手动分区. 对于分区的方案我感觉大多数人都会想到Hash的确Hash是最简单最有效的方式。但是Hash的问题是“单节点挂掉不可用数据量大了不好扩容”。对于如果业务的可靠性要求不高同时数据可控的情况下可以考虑数据分区的方式。 其实数据分区就是Shard其实Redis已经对Shard有很好的支持了用到的是ShardedJedisPool接下来简单的搞一下数据分片 package redis.clients.jedis.tests;import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.*;import java.util.ArrayList;
import java.util.List;/*** ShardJedis的测试类*/
public class ShardJedisTest {private ShardedJedisPool sharedPool;Beforepublic void initJedis(){JedisPoolConfig config new JedisPoolConfig();//Jedis池配置config.setTestOnBorrow(true);String hostA 127.0.0.1;int portA 6381;String hostB 127.0.0.1;int portB 6382;ListJedisShardInfo jdsInfoList new ArrayListJedisShardInfo(2);JedisShardInfo infoA new JedisShardInfo(hostA, portA);JedisShardInfo infoB new JedisShardInfo(hostB, portB);jdsInfoList.add(infoA);jdsInfoList.add(infoB);sharedPool new ShardedJedisPool(config, jdsInfoList);}Testpublic void testSetKV() throws InterruptedException {try {for (int i0;i50;i){String key testi;ShardedJedis jedisClient sharedPool.getResource();System.out.println(key:jedisClient.getShard(key).getClient().getHost():jedisClient.getShard(key).getClient().getPort());System.out.println(jedisClient.set(key,Math.random()));jedisClient.close();}}catch (Exception e){e.printStackTrace();}}} 这里我是用JUnit做的测试我在本机开了两个Redis实例 端口号分别是6381和6382。然后用ShardedJedisPool实现了一个Shard主要是生成了50个Key分别存到Redis中。运行结果如下 test0:127.0.0.1:6382
OK
test1:127.0.0.1:6382
OK
test2:127.0.0.1:6381
OK
test3:127.0.0.1:6382
OK
test4:127.0.0.1:6382
OK
test5:127.0.0.1:6382
OK
test6:127.0.0.1:6382
OK
test7:127.0.0.1:6382
OK
test8:127.0.0.1:6381
OK
test9:127.0.0.1:6381 可以看到KV分别分发到了不同的Redis实例这种Shard的方式需要我们提前计算好数据量的大小便于决定实例的个数。同时这种shard的可靠性不是很好如果单个Redis实例挂掉了那么这个实例便不可用了。 其实Shard使用起来很简单接下来我们看看ShardedJedisPool的具体的实现 首先在初始化ShardedJedisPool的时候我们需要创建一个JedisShardInfo实例JedisShardInfo主要是对单个连接的相关配置 public class JedisShardInfo extends ShardInfoJedis {private static final String REDISS rediss;private int connectionTimeout;private int soTimeout;private String host;private int port;private String password null;private String name null;// Default Redis DBprivate int db 0;private boolean ssl;private SSLSocketFactory sslSocketFactory;private SSLParameters sslParameters;private HostnameVerifier hostnameVerifier; 像连接超时时间、发送超时时间、Host和port等。这些都是之前我们实例化Jedis用到的。 同时还需要进行JedisPoolConfig的设置可以猜到ShardedJedisPool也是基于JedisPool来实现的。 看看ShardedJedisPool的构造 public ShardedJedisPool(final GenericObjectPoolConfig poolConfig, ListJedisShardInfo shards) {this(poolConfig, shards, Hashing.MURMUR_HASH);}public ShardedJedisPool(final GenericObjectPoolConfig poolConfig, ListJedisShardInfo shards,Hashing algo) {this(poolConfig, shards, algo, null);}public ShardedJedisPool(final GenericObjectPoolConfig poolConfig, ListJedisShardInfo shards,Hashing algo, Pattern keyTagPattern) {super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern));}public Pool(final GenericObjectPoolConfig poolConfig, PooledObjectFactoryT factory) {initPool(poolConfig, factory);}public void initPool(final GenericObjectPoolConfig poolConfig, PooledObjectFactoryT factory) {if (this.internalPool ! null) {try {closeInternalPool();} catch (Exception e) {}}this.internalPool new GenericObjectPoolT(factory, poolConfig);} 构造方法很长但是很清晰关键点在ShardedJedisFactory的构建因为这是使用commons-pool的必要工厂类。同时我们可以看到这里分分片策略使用的确实是Hash而且还是冲突率很低的MURMUR_HASH。 那么我们直接看ShardedJedisFactory类就好了因为commons-pool就是基于这个工厂类来管理相关的对象的这里缓存的对象是ShardedJedis 我们先看一下ShardedJedisFactory public ShardedJedisFactory(ListJedisShardInfo shards, Hashing algo, Pattern keyTagPattern) {this.shards shards;this.algo algo;this.keyTagPattern keyTagPattern;}Overridepublic PooledObjectShardedJedis makeObject() throws Exception {ShardedJedis jedis new ShardedJedis(shards, algo, keyTagPattern);return new DefaultPooledObjectShardedJedis(jedis);}Overridepublic void destroyObject(PooledObjectShardedJedis pooledShardedJedis) throws Exception {final ShardedJedis shardedJedis pooledShardedJedis.getObject();for (Jedis jedis : shardedJedis.getAllShards()) {try {try {jedis.quit();} catch (Exception e) {}jedis.disconnect();} catch (Exception e) {}}}Overridepublic boolean validateObject(PooledObjectShardedJedis pooledShardedJedis) {try {ShardedJedis jedis pooledShardedJedis.getObject();for (Jedis shard : jedis.getAllShards()) {if (!shard.ping().equals(PONG)) {return false;}}return true;} catch (Exception ex) {return false;}} 其实这里makeObject是创建一个ShardedJedis同时ShardedJedis也是连接池里保存的对象。 可以看到destroyObject和validateObject都是将ShardedJedis里的redis实例当做了一个整体去对待一个失败全部失败。 接下来看下ShardedJedis的实现这个里面主要做了Hash的处理和各个Shard的Client的缓存。 public class ShardedJedis extends BinaryShardedJedis implements JedisCommands, Closeable {protected ShardedJedisPool dataSource null;public ShardedJedis(ListJedisShardInfo shards) {super(shards);}public ShardedJedis(ListJedisShardInfo shards, Hashing algo) {super(shards, algo);}public ShardedJedis(ListJedisShardInfo shards, Pattern keyTagPattern) {super(shards, keyTagPattern);}public ShardedJedis(ListJedisShardInfo shards, Hashing algo, Pattern keyTagPattern) {super(shards, algo, keyTagPattern);} 这里的dataSource是对连接池的引用用于在Close的时候资源返还。和JedisPool的思想差不多。 由于ShardedJedis是BinaryShardedJedis的子类所以构造函数会一直向上调用在Shard中 public Sharded(ListS shards, Hashing algo, Pattern tagPattern) {this.algo algo;this.tagPattern tagPattern;initialize(shards);}private void initialize(ListS shards) {nodes new TreeMapLong, 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());}} 这里主要做整个ShardedJedis中Jedis缓存池的初始化和分片的实现可以看到首先获取shardInfo就是之前的JedisShardInfo根据shardInfo生成多个槽位将这些槽位存到TreeMap中同时将shardInfo和Jedis的映射存到resources中。当我们做Client的获取的时候 首先调用ShardedJedisPool的getResource方法从对象池中获取一个ShardedJedis ShardedJedis jedisClient sharedPool.getResource(); 调用ShardedJedis的getShard方法获取一个Jedis实例——一个shard。 public R getShard(String key) {return resources.get(getShardInfo(key));}public S getShardInfo(String key) {return getShardInfo(SafeEncoder.encode(getKeyTag(key)));}public S getShardInfo(byte[] key) {SortedMapLong, S tail nodes.tailMap(algo.hash(key));if (tail.isEmpty()) {return nodes.get(nodes.firstKey());}return tail.get(tail.firstKey());} 这里主要是对key做hash然后去TreeMap中判断当前的key落在哪个区间上再通过这个区间上的ShardInfo从resources的Map中获取对应的Jedis实例。 这也就是说每一个ShardedJedis都维护了所有的分片将多个实例当成一个整体去使用这也就导致只要集群中一个实例不可用整个ShardedJedis就不可用了。同时对于Hash的分片方式是不可扩容的扩容之后原本应该存储在一起的数据就分离了。 其实这种是Jedis默认提供的分片方式其实针对我们自己的场景我们也可以尝试自己做一个路由机制例如根据不同年份、月份的数据落到一个实例上。 而对于在Spring中集成Jedis分片来说应该是做简单的 1、在properties中定义其它Redis redis.host2192.168.142.34 2、注入Bean bean id shardedJedisPool class redis.clients.jedis.ShardedJedisPool constructor-arg index0 refjedisPoolConfig/ constructor-arg index1 list bean classredis.clients.jedis.JedisShardInfo constructor-arg index0 value${redis.host}/ constructor-arg index1 value${redis.port} typeint/ constructor-arg index2 value${redis.timeout} typeint/ property namepassword value${redis.password}/ /bean bean classredis.clients.jedis.JedisShardInfo constructor-arg index0 value${redis.host2}/ constructor-arg index1 value${redis.port} typeint/ constructor-arg index2 value${redis.timeout} typeint/ property namepassword value${redis.password}/ /bean /list /constructor-arg
/bean 3、代码使用 //获取Bean
ShardedJedisPool shardedPool (ShardedJedisPool)context.getBean(shardedJedisPool);
ShardedJedis shardedJedis shardedPool.getResource(); ...
shardedPool.returnResource(shardedJedis);
//操作
shardedJedis.set(test, 123);
String president shardedJedis.get(test);
shardedJedis.del(test); 参考 http://www.jianshu.com/p/af0ea8d61dda以上内容转自此篇文章 http://www.jianshu.com/p/37b5b6cdb277 http://www.jianshu.com/p/a1038eed6d44 http://blog.csdn.net/yfkiss/article/details/38944179 http://hello-nick-xu.iteye.com/blog/2078153以上内容部分转自此篇文章 http://blog.csdn.net/benxiaohai529/article/details/52935216 http://blog.csdn.net/mid120/article/details/52799241 http://blog.csdn.net/lang_man_xing/article/details/38405269 http://www.cnblogs.com/hk315523748/p/6122263.html http://ihenu.iteye.com/blog/2267881 http://blog.csdn.net/koushr/article/details/50956870如有问题请联系我easonjim#163.com或者下方发表评论。