zoukankan      html  css  js  c++  java
  • Jedis源码分析

    Jedis源码分析

    Jedis继承关系

        Jedis提供了redis的客户端的连接和命令查询.从jedis继承关系中,Jedis实现很多的命令接口,每个接口都定义了不同的操作形式,这符合面向对象开发原则中的接口隔离原则和单一职责原则。下面的接口声明了相关的redis命令操作,每个接口都负责对一部分的命令进行方法声明。

    下列接口由父类BinaryJedis所依赖的接口

    • BasicCommands:提供基础的查询命令,如ping,quit,flushdb
    • BinaryJedisCommands:提供了针对redis数据结构的CURD等操作,其中参数(K-V)必须以byte数组形式提供
    • MultiKeyBinaryCommands:提供了针对redis数据结构的CURD等批量操作,其中参数(K-V)必须以byte数组形式提供
    • AdvancedBinaryJedisCommands:提供高级操作redis的命令,如config相关,slowlog,client等命令,其中参数(K-V)必须以byte数组形式提供
    • BinaryScriptingCommands:提供Lua脚本运行命令,命令必须以byte数组形式提供。

    Jedis所依赖的接口

    • JedisCommands:提供了针对redis数据结构的CURD等操作,其中参数(K-V)必须以String形式提供
    • MultiKeyCommands:提供了针对redis数据结构的CURD等批量操作,其中参数(K-V)必须以String数组形式提供
    • AdvancedJedisCommands:提供高级操作redis的命令,如config相关,slowlog,client等命令,其中参数(K-V)必须以String形式提供
    • ScriptingCommands:提供Lua脚本运行命令,命令必须以String形式提供。
    • BasicCommands:提供如ping,quit,flushDb运维类命令
    • ClusterCommands:提供集群状态查看,集群操作的命令
    • SentinelCommands:提供哨兵操作命令
    • ModuleCommands:提供redis模块加载和卸载命令

        除了方法接口声明之外,Jedis提供了客户端接口Client,使用该类仍然可以连接Redis服务端并进行相关的命令操作.Client本身只是提供相关的命令方法,而各方法的声明则需要Commands接口,连接操作则需要Connection类,也就是说Client类类似于一个前台,可以提供各种服务,而具体的实现则依赖于Connection和Commands.Client提供方法与jedis基本一致,少于不同的是,Client类不提供get()方法的返回值。

    package com.zzz.jedis.service;
    import org.junit.Test;
    import redis.clients.jedis.Client;
    public class ClientTest {
        @Test
        public void testClient() {
            Client client = new Client("localhost");
            client.connect();
            System.out.println(client.isConnected());
            client.set("a", "zhangsan");
            client.close();
        }
    }

    Jedis如何连接服务端

        Jedis通过Socket对Redis 服务端进行连接,而Jedis和Client本身并没有socket连接方法的实现,相关的连接方法都在Connection类中,观察如下的继承关系,Connection类是其顶层的实现类。Jedis或者Client发送命令时,必须通过Connection类的connect()方法建立TCP连接。

        在使用Connection类时,需提供相关的参数进行实例化,从构造方法可以看到,需要提供主机名,如果不设置,则使用默认名localhost.连接端口,不设置则使用默认端口6379,连接超时时长(如果不进行设置,则默认与socketTimeout保持一致),socket读取超时时长(如果不进行设置,则使用默认时长2000ms),SSL加密传输则是可选的。在连接时必须判断当前对象的连接状态,如代码2-1所示,如果符合条件则新建Socket实例。

    代码1-1:Connection构造方法

      public Connection(final String host, final int port, final boolean ssl,
          SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
          HostnameVerifier hostnameVerifier) {
        this.host = host;
        this.port = port;
        this.ssl = ssl;
        this.sslSocketFactory = sslSocketFactory;
        this.sslParameters = sslParameters;
        this.hostnameVerifier = hostnameVerifier;
      }

    代码2-1:socket是否连接状态

     public boolean isConnected() {
        return socket != null && socket.isBound() && !socket.isClosed() && socket.isConnected()
            && !socket.isInputShutdown() && !socket.isOutputShutdown();
      }

    代码2-2:socket连接

    public void connect() {
        if (!isConnected()) {
          try {
            socket = new Socket();
            // ->@wjw_add
            socket.setReuseAddress(true);
            socket.setKeepAlive(true); // Will monitor the TCP connection is
            // valid
            socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
            // ensure timely delivery of data
            socket.setSoLinger(true, 0); // Control calls close () method,
            // the underlying socket is closed
            // immediately
            // <-@wjw_add
    
            socket.connect(new InetSocketAddress(host, port), connectionTimeout);
            socket.setSoTimeout(soTimeout);
            //SSL 加密连接
            if (ssl) {
              if (null == sslSocketFactory) {
                sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
              }
              socket = sslSocketFactory.createSocket(socket, host, port, true);
              if (null != sslParameters) {
                ((SSLSocket) socket).setSSLParameters(sslParameters);
              }
              if ((null != hostnameVerifier) &&
                  (!hostnameVerifier.verify(host, ((SSLSocket) socket).getSession()))) {
                String message = String.format(
                    "The connection to '%s' failed ssl/tls hostname verification.", host);
                throw new JedisConnectionException(message);
              }
            }
            //获取socket对象的输入输出流
            outputStream = new RedisOutputStream(socket.getOutputStream());
            inputStream = new RedisInputStream(socket.getInputStream());
          } catch (IOException ex) {
            broken = true;
            throw new JedisConnectionException("Failed connecting to host " 
                + host + ":" + port, ex);
          }
        }
      }

    向Redis服务端发送命令

       Jedis和Client类通过Connection对象的sendCommand()方法进行命令传输,在传输命令时必须将字符串转为字节数组,如果类似于ping,ask这样的命令,只需要将命令转为字节即可。转换完成之后写入到输出流中,将字节流发送给Redis 服务端。

    代码3:发送命令

      public void sendCommand(final ProtocolCommand cmd) {
        sendCommand(cmd, EMPTY_ARGS);
      }
    
      public void sendCommand(final ProtocolCommand cmd, final String... args) {
        final byte[][] bargs = new byte[args.length][];
        for (int i = 0; i < args.length; i++) {
          bargs[i] = SafeEncoder.encode(args[i]);
        }
        sendCommand(cmd, bargs);
      }
    
      public void sendCommand(final ProtocolCommand cmd, final byte[]... args) {
        try {
          connect();
          Protocol.sendCommand(outputStream, cmd, args);
        } catch (JedisConnectionException ex) {
          /*
           * When client send request which formed by invalid protocol, Redis send back error message
           * before close connection. We try to read it to provide reason of failure.
           */
          try {
            String errorMessage = Protocol.readErrorLineIfPossible(inputStream);
            if (errorMessage != null && errorMessage.length() > 0) {
              ex = new JedisConnectionException(errorMessage, ex.getCause());
            }
          } catch (Exception e) {
            /*
             * Catch any IOException or JedisConnectionException occurred from InputStream#read and just
             * ignore. This approach is safe because reading error message is optional and connection
             * will eventually be closed.
             */
          }
          // Any other exceptions related to connection?
          broken = true;
          throw ex;
        }
      }
  • 相关阅读:
    PHP安装扩展mcrypt以及相关依赖项 【PHP安装PECL扩展的方法】
    linux设置开机自动启动
    php安装gd库
    php扩展库 说明
    把lighttpd配置为系统服务
    安装mysql5.6
    怎样当一个企业舍不得的人
    JQuery的$(document).ready(function(){})与JS的window.onload 的各自优势!
    JS中Null与Undefined的区别
    JS文本框输入限制
  • 原文地址:https://www.cnblogs.com/zhengzuozhanglina/p/11375662.html
Copyright © 2011-2022 走看看