• C# servicestack.redis 互通 java jedis


    拥抱变化,如今也走上了.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 class Sharded
        {
            private object nodes_lock = new object();
            private RedBlackTreeMap<long, string> nodes = new RedBlackTreeMap<long, string>();
            private IHash hashAlgo = new MD5_LongSUM_Multiply_Hash();
            
            public void AddTarget(int index, string shard)
            {
                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(string key)
            {
                long searchHashKey = this.hashAlgo.Hash(key);
    
                long nearestKey;
                string shard;
    
                lock (nodes_lock)
                {
                    if (this.nodes.NearestGreater(searchHashKey, out nearestKey))
                    {
                        shard = this.nodes.GetValue(nearestKey);
                        return shard;
                    }
    
                    if (this.nodes.Least(out searchHashKey, out shard))
                        return shard;
                }
    
                throw new Exception("GetShardInfo exception");
            }
        }

    其中RedBlackTreeMap这个是TreeLib中的组件,需要在nuget上引用。

    MD5_LongSUM_Multiply_Hash,这是个MD5算法,输入为string,输出为long。
    此处由于考虑到输出不是string,因此自己又改了改,让他输出long
    public class MD5_LongSUM_Multiply_Hash : IHash
        {
            public long Hash(string key)
            {
                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 in convertedKeyBytes)
                    value *= b*-1;
    
                return value;
            }
    
            private string Md5Hash(string input)
            {
                MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
    
                if (string.IsNullOrEmpty(input))
                    Log.GetLog().Info("Md5Hash, input is null or empty");
    
                byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
                StringBuilder sBuilder = new StringBuilder();
                for (int i = 0; i < data.Length; i++)
                {
                    sBuilder.Append(data[i].ToString("x2"));
                }
                return sBuilder.ToString();
            }
        }

    剩下的就是java端的这个输入string,输出long的算法,需要和.net的输入输出一致了。

    那就也写一个哈希算法,让他输入string,输出long,和.net的一致,这里只要java/.net用同一种md5算法,后续的md5变成long就很容易了。

    import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder;
    import redis.clients.util.Hashing;
    import redis.clients.util.SafeEncoder;
    
    import java.io.UnsupportedEncodingException;
    
    /**
     * Created by z on 2017/4/12.
     */
    public class MD5_SUM_Hash implements Hashing {
    
        MessageDigestPasswordEncoder encoder=new MessageDigestPasswordEncoder("MD5");
    
        public long hash(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(byte b : bytes)
                value *= b*-1;
            return value;
        }
    }
    <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-core</artifactId>
            </dependency>

    OK,核心的就这些了。

  • 相关阅读:
    Tomcat单机多实例部署
    通过Zabbix监控Tomcat单机多实例
    Swarm容器集群管理(超详细)
    15、基于consul+consul-template+registrator+nginx实现自动服务发现
    14、Docker监控方案(Prometheus+cAdvisor+Grafana)
    创建python虚拟环境
    Linux系统基础优化及常用命令
    Linux常用命令--用户管理,文件权限,打包命令等
    Linux--CentOS7使用firewalld打开关闭防火墙与端口
    vim
  • 原文地址:https://www.cnblogs.com/aarond/p/jedis_servicestack_redis_communication.html
Copyright © 2020-2023  润新知