从jedis源码中学习java socket client端的基本用法

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:

Jedis

在很多教材或者教程上,通常都是很简单的一个例子来演示如何使用Java进行TCP通讯.在这款广泛被使用的开源组件中,我们能够更好的学习到一个企业级的组件在TCP连接的处理上,更应该关注哪些方面.有哪些是我们应该掌握或者了解的TCP知识.TCP协议本身相当复杂,我们做应用的可以先从应用层需要用到的相关知识开始了解.
jedis中,与redis服务端建立连接的代码在Connection这个类中.

  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);

        if (ssl) {
          if (null == sslSocketFactory) {
            sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
          }
          socket = (SSLSocket) 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);
          }
        }

        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);
      }
    }
  }
  
  
    public boolean isConnected() {
    return socket != null && socket.isBound() && !socket.isClosed() && socket.isConnected()
        && !socket.isInputShutdown() && !socket.isOutputShutdown();
  }

通过以上代码,可以看到用到了jdk中相关的Socket对象来与redis服务进行tcp连接.重点看几个方法

setReuseAddress(true)
setKeepAlive(true)
setTcpNoDelay(true)
setSoLinger(true, 0)
setSoTimeout(soTimeout)

Socket

setReuseAddress

要解释这个方法的作用,首先要了解TCP套接字的一个状态-time-wait.
众所周知,TCP的连接建立和断开要经历三次握手和四次挥手.
如图所示,
四次挥手-图片来自维基百科
而time-wait这个状态就是连接发起方,通常就是client,在收到服务器端的FIN包后进入的状态.那为什么这个状态需要存在呢?客户端收到服务端发送的FIN包后,会向服务端发送一个ACK包,服务端接收到ACK包后进入CLOSED状态.但是由于网络或者其他问题,导致服务端有可能没有收到ACK包,导致服务器端重发FIN包.
time-wait这个需要保持多久呢?大概是2MSL(最大分段生存期).

MSL(最大分段生存期)指明TCP报文在Internet上最长生存时间,每个具体的TCP实现都必须选择一个确定的MSL值。RFC 1122建议是2分钟,但BSD传统实现采用了30秒。

了解了这些之后,我们可以看看这个方法的内部实现

public void setReuseAddress(boolean on) throws SocketException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on));
    }

我们看到,它是对SO_REUSEADDR这个套接字选项进行了设置.

这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时 SO_REUSEADDR 选项非常有用。

对应linux操作系统响应的配置为

net.ipv4.tcp_tw_reuse = 1    表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1  表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭

setKeepAlive

心跳检测是我们用来判断后端程序是否依旧正常服务的一个常用手段.TCP协议提供keepalive机制来监测TCP连接是否已经断开.

net.ipv4.tcp_keepalive_intvl = 20 重发keepalive包间隔 
net.ipv4.tcp_keepalive_probes = 3 重发次数
net.ipv4.tcp_keepalive_time = 60 超过一段时间没有进行数据传输则进行心跳监测

setTcpNoDelay

要了解这个方法的作用,首先要知道TCP协议中一个注明的算法-Nagle算法.Nagle算法的初衷是避免小包拥塞网络.比如只需要发送一个字节的数据,可是由于TCP包本身就占用了几十个字节,这样是比较浪费网络资源的.Nagle算法导致了要么是等待ACK到达或者缓冲区满,才会发送新的数据.
再介绍一下TCP-Delayed-ACK ,它是将ACK和响应数据绑在一个包中发送,降低协议开销.
如果客户端采用了Nagle算法和服务端采用了Delayed-ACK,同时客户端采用write-write-read模式,就很容易造成客户端与服务端都在等待对方数据的问题.
Delayed Ack 是有个超时机制的,而默认的超时正好就是40ms.
这里有一篇文章很好的介绍了write-write-read这种问题
setTcpNoDelay其实就是讲Nagle算法关闭,使得每次无论数据包有多大都会被立即发送.

setSoLinger

SO_LINGER选项用来控制Socket关闭时的行为,默认情况下,执行Socket的close方法,该方法会立即返回,但底层的Socket实际上并不会立即关闭,他会立即延迟一段时间,知道发送完剩余的数据,才会真正的关闭Socket,断开连接。

setSoLinger(true, 0)

执行该方法,那么执行Socket的close方法,该方法也会立即返回,但底层的Socket也会立即关闭,所有未发送完的剩余数据被丢弃.

setSoTimeOut

官方文档中解释

启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。

将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。如果超过超时值,将引发java.net.SocketTimeoutException,虽然 Socket
仍旧有效。选项必须在进入阻塞操作前被启用才能生效。超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。
参数:
timeout - 指定的以毫秒为单位的超时值。
抛出:
SocketException -
如果底层协议出现错误,例如 TCP 错误。

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
1天前
|
搜索推荐 前端开发 Java
java医院绩效考核管理系统项目源码
系统需要和his系统进行对接,按照设定周期,从his系统获取医院科室和医生、护士、其他人员工作量,对没有录入信息化系统的工作量,绩效考核系统设有手工录入功能(可以批量导入),对获取的数据系统按照设定的公式进行汇算,且设置审核机制,可以退回修正,系统功能强大,完全模拟医院实际绩效核算过程,且每步核算都可以进行调整和参数设置,能适应医院多种绩效核算方式。
3 0
|
2天前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
2天前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
|
2天前
|
设计模式 存储 JavaScript
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
|
2天前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
|
2天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
2天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
2天前
|
SQL Java 数据库连接
Java从入门到精通:2.3.1数据库编程——学习JDBC技术,掌握Java与数据库的交互
ava从入门到精通:2.3.1数据库编程——学习JDBC技术,掌握Java与数据库的交互
|
2天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
|
2天前
|
Java API
Java从入门到精通:2.1.5深入学习Java核心技术之文件操作
Java从入门到精通:2.1.5深入学习Java核心技术之文件操作