热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:警惕!四层七层负载乱用,Javalettuce连接池故障一例

本文由编程笔记#小编为大家整理,主要介绍了警惕!四层七层负载乱用,Javalettuce连接池故障一例相关的知识,希望对你有一定的参考价值。
本文由编程笔记#小编为大家整理,主要介绍了警惕!四层七层负载乱用,Java lettuce连接池故障一例相关的知识,希望对你有一定的参考价值。









01




写在之前







负载均衡

负载均衡建立在现有网络结构之上,它提供一种廉价、有效、透明的方法,扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。(百科)

































四层负载 七层负载
概念 IP+Port实现的负载均衡、不涉及具体协议、工作在TCP层、只转发你的流量,不关注流量内容是什么,只转发即可 七层是应用层、带协议的才能称为七层,它是基于应用层协议的信息进行转发,七层负载均衡也称为“内容交换”,主要是通过报文中具有真正意义的应用层内容,再加实现负载均衡的设备设置的服务器选择方式 ,决定最终选择哪台上游内容服务器;最常见的是http协议,
实现方式 使用三层的IP与四层的端口,来决定哪些流量做负载均衡,对需要做负载的流量进行NAT处理或直连路由或ip隧道的方式 ,把流量转发至上游服务器、并记录这个TCP或者UDP流量是由哪台上游服务器处理,后续这个连接的所有流量都同样转发到同一台服务器处理; 七层是在四层基础之上存在的,它在在四层的基础上结合应用层协议的特性进行负载,比如一个http服务,除了根据三层的IP+四层的端口来辨别哪些流量需要处理,还能基于七层的URI、浏览器类别、COOKIE、语言等信息来决定负载均衡;
工具选择 LVS、nginx、HAProxy、F5等 Nginx、HAProxy、Squid、F5等
利弊 简单、直接转发,不需要太多复杂配置,安全性相对7层差一些; 更智能化(例如根据用户请求内容区分图片服务器或者对文本进行压缩等),对客户端请求和服务器端的响应可以做修改(header信息、超时配置等),提高应用系统的灵活性;安全方面可以定制一些策略如waf、还有SYN Flood攻击,四层负载会直接到上游服务器,而七层的这些SYN攻击,到负载均衡就截止,不会影响到上游服务器,提高一些安全性;

工作场景中,根据公司业务需要自行选择使用哪种负载均衡即可,一般情况下http应用使用七层负载较多,其它七层应用协议如mysql协议、redis协议等都不建议使用七层负载均衡,因为七层均衡设备或软件很少会实现这种专有协议的负载算法。


五层协议模型

目的是熟悉协议工作在哪一层,数据承载形式是什么,redis协议是7层应用层协议,7层负载均衡设备是否就能实现所有七层协议呢?一般情况下不能,这里的redis是应用层协议,属于专用私有协议,但7层负载均衡如果实现了redis协议,就可以做7层负载,但如果没有实现也能类似HTTP一样用7层负载,强烈不建议这样的用法,因为会出现各种奇葩问题,建议使用4层负载就好了。



































五层模型 常见协议 数据表现形式
应用层 HTTP、FTP、Telnet、SNMP、DNS、SMTP、TFTP以及mysql、redis、grpc专有协议等都属于应用层协议  消息(message)
传输层 有连接安全的TCP,无连接不安全UDP TCP的叫做Segment(数据段)UDP的叫做数据报(Datagram)
网络层 ICMP、IP、BGP、IGMP、RIP、OSPF等协议 数据包(Packet)
数据链路层 ARP、RARP、PPP、MTU等协议  数据帧(Frame)
物理机 EEE802,IEEE802.11等协议 二进制位

除了上面的数据承载形式外,还有一种叫数据单元(data unit)的数据,常用的数据单元有服务数据单元(SDU)、协议数据单元(PDU);SDU是在同一机器上的两层之间传送信息,而PDU是发送机器上每层的信息发送到接收机器上的相应层(同等层间交流用的)。


F5 基础知识

F5是硬件负载均衡器,分LTM和GTM,配置中均有几个重要参数:Node(节点)、Pool(资源池)、和Virtual Server(虚拟服务器)。



在配置F5的过程中Virtual Server是核心,通过配置它,可以关联到Pool、Node、并且为VS分配一个IP,这里简单说下配置VS时的类型有哪些,这些也是F5的负载均衡类型,VS的Type有Performance L4、Standard VS、Forwarding IP 和 Fast Http,这里重点介绍下4层与7层负载均衡,其它两种不做多过介绍。


Performance L4类型的Virtual Server,是我们常说的四层负载均衡,也是一般企业都会选择的类型,因为它对应用系统的影响小、转发快、不改变TCP中的任何参数、直接转发。



Forwarding IP类型的 Virtual Server 它主要用在内外网路由功能上面,如果要使用路由功能,需要单独开启,这里不过多介绍。








02




问题排查







问题解决后图示

警惕!四层、七层负载乱用,Java lettuce连接池故障一例

问题描述

最近升级 codis proxy 时,发现升级完成后,Java 程序无法连接 Codis 的现象,线上大面积报警,没有办法只能重启下Java程序,服务正常;访问方式是Java 程序访问F5 VS IP,然后由 VS IP 负载到 Codis Proxy,这个问题不解决,影响后续 Codis Proxy升级。


信息收集

1. 网络反馈 F5 VS 配置未变更过;

2. Java程序也未发版,使用 lettuce连接池通过F5 VS 去访问 Codis 服务 ;

3. 升级 codis proxy 即 codis proxy重启过;

4. RD 同学反馈连接 codis异常,建议重启试下;

5. 重启后正常,把出问题的 Java 程序全部重启,业务恢复;

6. 其实这个时间回滚 codis proxy 代码也不能解决这个问题,必须 重启Java 程序(一般是谁升级了,就回滚谁,百分之八九十可以解决,很可惜此时回滚codis proxy 代码是无法解决)。


怀疑猜想

1. codis proxy 新代码导致,但中间件同学反馈,Java 程序重启之后正常,说明codis proxy 升级后的程序没有问题;

2. Java 使用的 lettuce 客户端套件问题; 

3. Java 程序异常,日志显示如下:

警惕!四层、七层负载乱用,Java lettuce连接池故障一例



[2020-02-28 10:21:52.180] [http-nio-8080-exec-5] ERROR [b9c9e148e24f478aba6d75cf93a21f0d] [] [] [] c.j.fsinnerapi.app.provider.common.exception.handler.GlobalExceptionHandler - occur error:
org.springframework.data.redis.connection.PoolException: Returned connection io.lettuce.core.StatefulRedisConnectionImpl@4afe9497 was either previously returned or does not belong to this connection provider

大意是先前链接已经返回,或者是不属于此连接提供者,说明先前的链接已经失效了,抛出异常了。

肯定是codis proxy 升级重启过程中导致问题,但问题原因究竟是什么呢?不可能因为升级codis proxy,把所有应用全部重启一次吧,陷入僵局,现在抓包分析吧。

抓包分析

java程序服务器端

警惕!四层、七层负载乱用,Java lettuce连接池故障一例

三次握手是成功的,在Push数据时,被reset掉了,这里的reset是谁发出来的呢,根据TTL可以判定是F5。


F5与Java程序端

警惕!四层、七层负载乱用,Java lettuce连接池故障一例

F5与Java程序之间的数据包,与Java程序端的一致,但这里的TTL是255,自身发出的RST。

F5到Codis proxy数据包与codis-proxy自己的数据包,均没有tcp.port == 6701的数据包,根据TTL的判定,应该是七层RESET,原因是如果七层代理有设计RESET功能,并且在无法连接后端程序的时候,发送RESET应用层响应包给客户端,那就认为是七层RESET,如果没有,而只是机械转发后端TCP层的包给客户端,就是四层RESET。从这里发现这个F5的 VS 竟然是七层负载均衡;


排查过程异常的曲折,当时抓包的时候,在重启完proxy后,立即停止了抓包,就出现类似上面的数据包;继续实验,让Java程序直接连 Codis proxy时,重启Codis proxy,Java程序丢几个包后,很快就能重新获取连接,继续访问codis并操作,RD 认为是F5的问题,F5同学目前也不确认问题原因,从这里可以得出Java 程序通过4层直连codis proxy,codis proxy重启时,Java程序丢几个包后,即可直接继续访问,无需重启;


从上面两个方面可以发现,F5 VS使用的七层负载,直连相当对4层,直接让F5同学修改VS配置,修改成4层转发,Codis Proxy 重启,Java 程序丢几个包后正常,到此问题找到了一个解决办法。


到底是什么原因造成的这种现象呢?Codis Proxy程序不升级,F5 VS 使用的七层负载(使用七层负载,暂不说是否合理),Java 程序能正常访问,为什么Codis Proxy 重启后,Java 程序收到一些RESET后,再连F5 VS 就不成功了呢?RD反馈是RESET,这句话看着没有什么太大的问题,其实里面隐藏着一个问题,RESET是什么时候发的,怎么判定 Java程序收到 Reset(Codis Proxy重启过程中)后又重新发起过连接呢?通过再次抓包分析(重启后多抓一段时间的数据包),确实论证了一个问题点,Codis Proxy重启正常后,Java 程序没有再发起连接请求,不重试了,Java 客户端放弃“治疗”,出现上面的 Java 异常错误。到这里基本上可以判断,应该是 Java 客户端使用 lettuce 连接池问题。


入手点是 lettuce 连接池源码,思路是七层负载时,VS IP:9100一直是通的,即使Pool池中所有NodeIP:Port都down了,也不影响IP:9100,客户端依然可以连接,四层负载(直连时)VS  IP:9100是随Codis  Proxy 端口存在而存在,重启时Java 客户端可以感知到IP:9100的存在与否。


问题本质原因

应用系统基于SpringBoot2.x版本开发,并配合使用Spring官方推荐的Lettuce作为Redis连接池套件。


Lettuce开发者认为,Redis单线程效率较高,而在项目实际开发过程中,仅阻塞或事务性操作对Redis的连接时长要求较高,所以Lettuce连接管理单元在默认情况下仅开启一个本地链接供多个LettuceConnector使用,也就是说,在默认情况下,即使你配置了连接池,实际上使用的也仅仅是一个物理连接,且默认情况下Lettuce不会校验该链接的可靠性(是否被关闭、是否可以ping通),所以链接永不释放。


Lettuce通过两种链接方式进行访问redis服务





















独享链接 共享链接
多线程分别持有独立的连接(每个线程单独建立Socket) 多线程共享同一个链接
每个操作都会开启和关闭对应的连接 连接永不关闭,且默认情况下不进行可靠性校验
默认情况下,阻塞性、事务性操作会强制开启一个独占链接 默认情况下,非阻塞性、非事务性操作都只能使用共享连接进行操作

private boolean validateConnection = false;
private boolean shareNativeConnection = true;

由于使用了默认参数配置即共享链接,它只开启一个NativeConnection 实际上仅开启了一个连接。

七层负载时F5 VS IP:9100是通的,Codis Proxy重启时,不影响F5 VS IP:9100的连通性,Java程序无法感知到Codis Proxy重启了,它一直认为服务是正常的,但Codis Proxy 重启导致LettuceConnectionFactory.SharedConnection(共享链接模式下)的validateConnection方法在校验时失败,就会重复调用connectionProvider.release(connection),即出现上面的异常日志,并没有重试再去连接,而一直异常状态;

四层负载时F5 VS IP:9100 随Codis Proxy重启,而Java的Lettuce 可以感知到,连接被关闭;




















参数 四层负载 七层负载
validateConnection = false;
shareNativeConnection = true;(默认)
共享链接方式正常访问 共享链接方式不可以正常访问,报异常错误
validateConnection = false;
shareNativeConnection = false;
独享链接方式正常访问 独享链接方式正常访问


各位RD同学可读下源码分析一下,这里给一个网上分析链接:https://segmentfault.com/a/1190000016417906








03




总结







此问题有两种解决方案,一是七层负载修改成四层(这里由于是F5,支持这种应用层协议使用Standard 类型可以使用,Nginx无法使用七层代理这种非标准应用层协议,4层stream 可以),另一种修改Java使用lettuce连接池参数,禁止lettuce作者推荐的共享式,使用独立链接方式,连接池默认5个连接,使用完成,再重新创建。(由于我是运维,lettuce 源码读的不是精,如有错误,可以指点出来,在这里谢谢你)


郑重声明下非标准的应用层协议,一律推荐使用4层负载,不能使用七层负载,类似MySQL、Redis协议等,原因:Redis是应用层协议,但不能使用7层负载,一是因为负载转换器不可能实现这种私有的协议,二是因为这样一来会造成很多意想不到的错误,并且大部分七层反向代理服务器(软件)不支持这种操作,只有像F5这种硬件设备才支持。


附件 F5 四层负载配置图示

警惕!四层、七层负载乱用,Java lettuce连接池故障一例


附件 F5 七层负载配置图示

警惕!四层、七层负载乱用,Java lettuce连接池故障一例







您的关注是写作的动力

往期故障分享




往期K8S分享




推荐阅读
  • 护墙_搭建LVS负载均衡NAT和DR模式
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了搭建LVS负载均衡NAT和DR模式相关的知识,希望对你有一定的参考价值。 ... [详细]
  • UDP协议开发
    UDP是用户数据报协议(UserDatagramProtocol,UDP)的简称,其主要作用是将网络数据流量压缩成数据报形式,提供面向事务的简单信息传送服务。与TCP协议不同,UD ... [详细]
  • DDOSDDOS的中文名叫分布式拒绝服务***,俗称洪水***DDoS***概念DoS的***方式有很多种,最基本的DoS***就是利用合理的服务请求来 ... [详细]
  • 目录结构如下:Nginx基础知识NginxHTTP服务器的特色及优点Nginx的主要企业功能Nginx作为web服务器的主要应用场景包括:Nginx的安装安装环境 ... [详细]
  • 超赞!GitHub上百万下载量Java面试手册!颠覆你的认知
    金三面试不顺心,马上银四面试在即,自己复盘总觉得Java知识点很凌乱?没有合适的方法学习!今天分享这份GitHub上百万下载量Ja ... [详细]
  • linux js文件怎么打开文件夹路径,js 获取文件本地路径
    1.代码获取文件本地路径选择导入数据源:functionbrowseFolder(){try{varMessage“\u8bf7\u9009\u62e9\u6587\u4ef6\u ... [详细]
  • 为什么80%的码农都做不了架构师?#0系列目录#聊聊远程通信Java远程通讯技术及原理分析聊聊Socket、TCPIP、HTTP、FTP及网 ... [详细]
  • 这是一份详细 & 清晰的计算机网络基础 学习指南
    前言计算机网络基础该是程序猿需掌握的知识,但往往会被忽略今天,我将献上一份详细&清晰的计算机网络基础学习指南,涵盖TCPUDP协议、Http协议、Socket等,希望你们会喜欢。目 ... [详细]
  • 使用pm2方便开启node集群模式
    使用pm2方便开启node集群模式 ... [详细]
  • LVS–基础–02–常用命令1、帮助命令ipvsadm--help2、添加虚拟服务器2.1、语法ipvsadm-A[-t|u|f][vip_addr:port][-s:指定算 ... [详细]
  • Linux数据链路层的包解析仅以此文作为学习笔记,初学者,如有错误欢迎批评指正,但求轻喷。一般而言,Linux系统截获数据包后,会通过协议栈,按照TCPIP层次进行解析,那我们如何 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
  • 计算机网络计算机网络分层结构
    为了解决计算机网络复杂的问题,提出了计算机网络分层结构。计算机网络分层结构主要有OSI7层参考模型,TCPIP4层参考模型两种。为什么要分层不同产商 ... [详细]
  • UDP千兆以太网FPGA_verilog实现(四、代码前期准备UDP和IP协议构建)
    UDP:userDatagramprotocol用户数据报协议无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETFRFC76 ... [详细]
author-avatar
想挽回的-谎言
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有