美文网首页「实战篇」python的docker爬虫技术码农的世界『互联网架构』
『互联网架构』软件架构-redis的通信协议(protocol)

『互联网架构』软件架构-redis的通信协议(protocol)

作者: IT人故事会 | 来源:发表于2019-05-17 20:37 被阅读3次

    原创文章,欢迎转载。转载请注明:转载自IT人故事会,谢谢!
    原文链接地址:『互联网架构』软件架构-redis的通信协议(protocol)(52)

    redis的通信协议是什么?双方约定了一种编码方式,客户端将要发送的命令进行编码,然后服务端收到后,使用同样的协议进行解码,服务端处理完成后,再次编码返回给客户端,客户端解码拿到返回结果,这样就完成了一次通信。

    (一)协议介绍

    https://redis.io/topics/protocol

    1. 易于实现
    2. 可以高效地被计算机分析(parse)
    3. 可以很容易地被人类读懂

    (二)编码后的字符标记解释

    1. For Simple Strings the first byte of the reply is "+" 回复
    2. For Errors the first byte of the reply is "-" 错误
    3. For Integers the first byte of the reply is ":" 整数
    4. For Bulk Strings the first byte of the reply is "$" 字符串
    5. For Arrays the first byte of the reply is "*" 数组

    (三)模拟Redis客户端&分片

    • 客户端

    Jedis跟redis通讯很简单发送了socket,然后发送了resp这种‘暗语’进行通讯。

    import java.io.IOException;
    import java.io.InputStream;
    import java.net.Socket;
    
    public class RedisClient {
    
        private Socket socket;
        private  String host;
        private int port;
    
        public RedisClient() throws IOException {
            this.socket = new Socket("192.168.79.100",6379);
        }
    
        public RedisClient(String host, int port) {
            this.host = host;
            this.port = port;
            try {
                this.socket = new Socket(host,port);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        //set wk 2018
        public String set( String key, String value) throws IOException {
           //"*3\r\n$3\r\nset\r\n$2\r\nwk\r\n$4\r\n2018"
            StringBuilder stringBuilder=new StringBuilder();
            stringBuilder.append("*3").append("\r\n");
            stringBuilder.append("$").append(CommandRedis.set.name().length()).append("\r\n");
            stringBuilder.append(CommandRedis.set).append("\r\n");
            stringBuilder.append("$").append(key.getBytes().length).append("\r\n");
            stringBuilder.append(key).append("\r\n");
            stringBuilder.append("$").append(value.getBytes().length).append("\r\n");
            stringBuilder.append(value).append("\r\n");
            socket.getOutputStream().write(stringBuilder.toString().getBytes());
            byte[] b=new byte[2048];
            socket.getInputStream().read(b);
            return new String(b);
    
        }
    
    
        public String get( String key) throws IOException {
            //"*3\r\n$3\r\nset\r\n$2\r\nwk\r\n$4\r\n2018"
            StringBuilder stringBuilder=new StringBuilder();
            stringBuilder.append("*2").append("\r\n");
            stringBuilder.append("$").append(CommandRedis.get.name().length()).append("\r\n");
            stringBuilder.append(CommandRedis.get).append("\r\n");
            stringBuilder.append("$").append(key.getBytes().length).append("\r\n");
            stringBuilder.append(key).append("\r\n");
            socket.getOutputStream().write(stringBuilder.toString().getBytes());
            byte[] b=new byte[2048];
            socket.getInputStream().read(b);
            return new String(b);
    
        }
    
        public String setnx(String key, String value) throws Exception {
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("*3").append("\r\n");
            stringBuffer.append("$5").append("\r\n");
            stringBuffer.append("setnx").append("\r\n");
            stringBuffer.append("$").append(key.getBytes().length).append("\r\n");
            stringBuffer.append(key).append("\r\n");
            stringBuffer.append("$").append(value.getBytes().length).append("\r\n");
            stringBuffer.append(value).append("\r\n");
            socket.getOutputStream().write(stringBuffer.toString().getBytes());
            byte[] b = new byte[2048];
            socket.getInputStream().read(b );
            return new String(b);
        }
    
        // 管道
        public void pipeline(String key)throws Exception {
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("*2").append("\r\n");
            stringBuffer.append("$"+CommandRedis.subscribe.name().length()).append("\r\n");
            stringBuffer.append(CommandRedis.subscribe).append("\r\n");
            stringBuffer.append("$").append(key.getBytes().length).append("\r\n");
            stringBuffer.append(key).append("\r\n");
            socket.getOutputStream().write(stringBuffer.toString().getBytes());
            InputStream inputStream = socket.getInputStream();
            while (true) {
                byte[] b = new byte[2048];
                inputStream.read(b );
                System.out.println(new String(b));
            }
    
        }
    
        //subscribe
        public void subscribe(String key)throws Exception {
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("*2").append("\r\n");
            stringBuffer.append("$"+CommandRedis.subscribe.name().length()).append("\r\n");
            stringBuffer.append(CommandRedis.subscribe).append("\r\n");
            stringBuffer.append("$").append(key.getBytes().length).append("\r\n");
            stringBuffer.append(key).append("\r\n");
            socket.getOutputStream().write(stringBuffer.toString().getBytes());
            InputStream inputStream = socket.getInputStream();
            while (true) {
                byte[] b = new byte[2048];
                inputStream.read(b );
                System.out.println(new String(b));
            }
        }
    
        public void close(){
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    
    
    
    public enum CommandRedis {
    
        set,get,incr,subscribe
    
    }
    
    • 分片

    RedisClient分片方式,简单模拟,理解概念分片。

    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestRedisClient {
        public static void main(String[] args) throws IOException {
    
     /*       RedisClient redisClient=new RedisClient();
            System.out.println(redisClient.set("wk","悟空"));
            System.out.println(redisClient.get("wk"));*/
    
    
            List<RedisClient> pool=new ArrayList<>();
            pool.add(new RedisClient("192.168.0.12",6379));
            pool.add(new RedisClient("192.168.0.12",6380));
            pool.add(new RedisClient("192.168.0.12",6381));
            Crc16Sharding crc16Sharding=new Crc16Sharding(pool);
            for (int i=0;i<100;i++){
                String key="xx"+i;
    
                RedisClient redisClient=crc16Sharding.crc16(key);
                redisClient.set(key,i+"");
                System.out.println(redisClient.get(key));
            }
    
        }
    }
    
    
    import com.sun.org.apache.xerces.internal.impl.xpath.regex.Match;
    import java.util.List;
    
    public class Crc16Sharding {
        List<RedisClient> pool;
    
        public Crc16Sharding(List<RedisClient> pool) {
            this.pool = pool;
        }
    
        /**
         * 通过一个key可以定位到一块 节点
         * @param key
         * @return
         */
        public RedisClient crc16(String key){
    
            int num=Math.abs(key.hashCode()%pool.size());
            return  pool.get(num);
           /* if(key.length()<3){
               return pool.get(0);
            }else  if(key.length()<6){
                return  pool.get(1);
            }else{
                return  pool.get(2);
            }*/
        }
    }
    
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestRedisClient {
        public static void main(String[] args) throws IOException {
            List<RedisClient> pool=new ArrayList<>();
            pool.add(new RedisClient("192.168.0.12",6379));
            pool.add(new RedisClient("192.168.0.12",6380));
            pool.add(new RedisClient("192.168.0.12",6381));
            Crc16Sharding crc16Sharding=new Crc16Sharding(pool);
            for (int i=0;i<100;i++){
                String key="xx"+i;
    
                RedisClient redisClient=crc16Sharding.crc16(key);
                redisClient.set(key,i+"");
                System.out.println(redisClient.get(key));;
            }
        }
    }
    
    

    (四)redis使用技巧

    1. 如果是订单表,取的话也是通过唯一的订单号来进行,不要通过循环去redis里面取。
    2. 开关类型的放到本地的jvm内存中,尽量减少redis的压力。
    3. 避免使用慢查询 hgetall
    4. 存储redis的购物车,有的人都是很多的好几M的,几百个商品,redis存储的内容的压缩。减少通信的宽带。优化存储内容。
    5. 少用字符串存储,可以存储map格式放入redis。获取redis里面的map,直接通过key就可以取到对应的值。

    (五)监控&运维

    • Open-falcon

    http://open-falcon.org/
    open-falcon是小米开源监控系统,强大灵活的数据采集、采集、传输、绘制、查询、报警等。采用全部golang编写,portal和dashboard使用python编写。

    • Redis-migrate-tool

    https://github.com/vipshop/redis-migrate-tool
    2年多没更新了,可以了解下
    Redis-migrate-tool是唯品会开源针对redis数据运维工具。
    支持单独、twemproxy集群、redis custer、aof、rdb文件迁移。支持实时迁移、异构迁移、过滤等功能。

    PS:介绍了运维工具,redis通信的内容拼接,redis存储的技巧,redis正常的时候没问题,并发量大的时候一定要注意,在大流量的情况下,代码的细节至关重要!

    相关文章

      网友评论

        本文标题:『互联网架构』软件架构-redis的通信协议(protocol)

        本文链接:https://www.haomeiwen.com/subject/dbnzbqtx.html