拥抱变化,如今也走上了.net/java通吃的时代,下面就讲讲如何让.net/java都能正常访问分片的redis吧。
有几个关键点:一致性环哈希、哈希算法、序列化、反序列化
后两个都比较直接,只要选择一种跨语言的序列化方式就行了,如:json, protobuf, ace等,本文全略了
本文是基于jedis的一致性环哈希来修改的,.net选的是servicestack.redis组件来修改
无奈两个组件都有各自的一致性环哈希算法,不兼容,那就选一个作为标准,修改另一个咯。本文选择jedis的一致性环哈希作为标准,进而修改.net来适应jedis
jedis的逻辑是给每个redis节点构造160个虚拟节点,放入一颗二叉树中(key/value:key是一个long值,根据哈希算法算出来的一个long、value是节点id,是个string)。
OK,逻辑清楚了,那就简单了,给c#端写个一模一样的一致性环哈希算法。
public classSharded
{private object nodes_lock = new object();private RedBlackTreeMap nodes = new RedBlackTreeMap();private IHash hashAlgo = newMD5_LongSUM_Multiply_Hash();public void AddTarget(int index, stringshard)
{lock(nodes_lock)
{for (int n = 0; n < 160; ++n)
{var hashKey = "SHARD-" + index + "-NODE-" +n;long hashValue = this.hashAlgo.Hash(hashKey);
nodes.SetOrAddValue(hashValue, shard);
}
}
}public string GetShardInfo(stringkey)
{long searchHashKey = this.hashAlgo.Hash(key);longnearestKey;stringshard;lock(nodes_lock)
{if (this.nodes.NearestGreater(searchHashKey, outnearestKey))
{
shard= this.nodes.GetValue(nearestKey);returnshard;
}if (this.nodes.Least(out searchHashKey, outshard))returnshard;
}throw new Exception("GetShardInfo exception");
}
}
其中RedBlackTreeMap这个是TreeLib中的组件,需要在nuget上引用。
MD5_LongSUM_Multiply_Hash,这是个MD5算法,输入为string,输出为long。
此处由于考虑到输出不是string,因此自己又改了改,让他输出long
public classMD5_LongSUM_Multiply_Hash : IHash
{public long Hash(stringkey)
{var md5=Md5Hash(key);if (string.IsNullOrEmpty(md5))
Log.GetLog().Info("Hash, md5 is null or empty");var convertedKeyBytes =Encoding.UTF8.GetBytes(md5);long value = 1;foreach(var b inconvertedKeyBytes)
value*= b*-1;returnvalue;
}private string Md5Hash(stringinput)
{
MD5CryptoServiceProvider md5Hasher= newMD5CryptoServiceProvider();if (string.IsNullOrEmpty(input))
Log.GetLog().Info("Md5Hash, input is null or empty");byte[] data =md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
StringBuilder sBuilder= newStringBuilder();for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}returnsBuilder.ToString();
}
}
剩下的就是java端的这个输入string,输出long的算法,需要和.net的输入输出一致了。
那就也写一个哈希算法,让他输入string,输出long,和.net的一致,这里只要java/.net用同一种md5算法,后续的md5变成long就很容易了。
importorg.springframework.security.authentication.encoding.MessageDigestPasswordEncoder;importredis.clients.util.Hashing;importredis.clients.util.SafeEncoder;importjava.io.UnsupportedEncodingException;/*** Created by z on 2017/4/12.*/
public class MD5_SUM_Hash implementsHashing {
MessageDigestPasswordEncoder encoder=new MessageDigestPasswordEncoder("MD5");public longhash(String key) {return this.hash(SafeEncoder.encode(key));
}public long hash(byte[] bytes) {
String converted_str= null;try{
converted_str= new String(bytes, "UTF8");
}catch(UnsupportedEncodingException e) {
e.printStackTrace();
}
String result=encoder.encodePassword(converted_str, null);try{
bytes=result.getBytes("UTF8");
}catch(UnsupportedEncodingException e) {
e.printStackTrace();
}long value = 1;for(byteb : bytes)
value*= b*-1;returnvalue;
}
}
org.springframework.security
spring-security-core
OK,核心的就这些了。