成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊聊 TCP 長連接和心跳那些事

網(wǎng)絡(luò) 通信技術(shù)
可能很多 Java 程序員對 TCP 的理解只有一個三次握手,四次揮手的認(rèn)識,我覺得這樣的原因主要在于 TCP 協(xié)議本身稍微有點(diǎn)抽象(相比較于應(yīng)用層的 HTTP 協(xié)議);其次,非框架開發(fā)者不太需要接觸到 TCP 的一些細(xì)節(jié)。

[[254870]]

1 前言

可能很多 Java 程序員對 TCP 的理解只有一個三次握手,四次揮手的認(rèn)識,我覺得這樣的原因主要在于 TCP 協(xié)議本身稍微有點(diǎn)抽象(相比較于應(yīng)用層的 HTTP 協(xié)議);其次,非框架開發(fā)者不太需要接觸到 TCP 的一些細(xì)節(jié)。其實(shí)我個人對 TCP 的很多細(xì)節(jié)也并沒有完全理解,這篇文章主要針對微信交流群里有人提出的長連接,心跳的問題,做一個統(tǒng)一的整理。

在 Java 中,使用 TCP 通信,大概率會涉及到 Socket、Netty,本文會借用它們的一些 API 和設(shè)置參數(shù)來輔助介紹。

2 長連接與短連接

TCP 本身并沒有長短連接的區(qū)別,長短與否,完全取決于我們怎么用它。

  • 短連接:每次通信時,創(chuàng)建 Socket;一次通信結(jié)束,調(diào)用 socket.close()。這就是一般意義上的短連接,短連接的好處是管理起來比較簡單,存在的連接都是可用的連接,不需要額外的控制手段。
  • 長連接:每次通信完畢后,不會關(guān)閉連接,這樣就可以做到連接的復(fù)用。長連接的好處便是省去了創(chuàng)建連接的耗時。

短連接和長連接的優(yōu)勢,分別是對方的劣勢。想要圖簡單,不追求高性能,使用短連接合適,這樣我們就不需要操心連接狀態(tài)的管理;想要追求性能,使用長連接,我們就需要擔(dān)心各種問題:比如端對端連接的維護(hù),連接的保活。

長連接還常常被用來做數(shù)據(jù)的推送,我們大多數(shù)時候?qū)νㄐ诺恼J(rèn)知還是 request/response 模型,但 TCP 雙工通信的性質(zhì)決定了它還可以被用來做雙向通信。在長連接之下,可以很方便的實(shí)現(xiàn) push 模型。

短連接沒有太多東西可以講,所以下文我們將目光聚焦在長連接的一些問題上。純講理論未免有些過于單調(diào),所以下文我借助 Dubbo 這個 RPC 框架的一些實(shí)踐來展開 TCP 的相關(guān)討論。

3 服務(wù)治理框架中的長連接

前面已經(jīng)提到過,追求性能的時候,必然會選擇使用長連接,所以借助 Dubbo 可以很好的來理解 TCP。我們開啟兩個 Dubbo 應(yīng)用,一個 server 負(fù)責(zé)監(jiān)聽本地 20880(眾所周知,這是 Dubbo 協(xié)議默認(rèn)的端口),一個 client 負(fù)責(zé)循環(huán)發(fā)送請求。執(zhí)行 lsof-i:20880 命令可以查看端口的相關(guān)使用情況:

 

*:20880(LISTEN) 說明了 Dubbo 正在監(jiān)聽本地的 20880 端口,處理發(fā)送到本地 20880 端口的請求。

后兩條信息說明請求的發(fā)送情況,驗(yàn)證了 TCP 是一個雙向的通信過程,由于我是在同一個機(jī)器開啟了兩個 Dubbo 應(yīng)用,所以你能夠看到是本地的 53078 端口與 20880 端口在通信。我們并沒有手動設(shè)置 53078 這個客戶端端口,他是隨機(jī)的,但也闡釋了一個道理:即使是發(fā)送請求的一方,也需要占用一個端口。

稍微說一下 FD 這個參數(shù),他代表了文件句柄,每新增一條連接都會占用新的文件句柄,如果你在使用 TCP 通信的過程中出現(xiàn)了 open too many files 的異常,那就應(yīng)該檢查一下,你是不是創(chuàng)建了太多的連接,而沒有關(guān)閉。細(xì)心的讀者也會聯(lián)想到長連接的另一個好處,那就是會占用較少的文件句柄。

4 長連接的維護(hù)

因?yàn)榭蛻舳苏埱蟮姆?wù)可能分布在多個服務(wù)器上,客戶端端自然需要跟對端創(chuàng)建多條長連接,使用長連接,我們遇到的***個問題就是要如何維護(hù)長連接。

  1.  //客戶端 
  2.  
  3. public class NettyHandler extends SimpleChannelHandler { 
  4.    private final Map<String, Channel> channels = new ConcurrentHashMap<String, Channel>(); // <ip:port, channel> 
  5. //服務(wù)端 
  6. public class NettyServer extends AbstractServer implements Server { 
  7.    private Map<String, Channel> channels; // <ip:port, channel> 

在 Dubbo 中,客戶端和服務(wù)端都使用 ip:port 維護(hù)了端對端的長連接,Channel 便是對連接的抽象。我們主要關(guān)注 NettyHandler 中的長連接,服務(wù)端同時維護(hù)一個長連接的集合是 Dubbo 的設(shè)計,我們將在后面提到。

5 連接的保活

這個話題就有的聊了,會牽扯到比較多的知識點(diǎn)。首先需要明確一點(diǎn),為什么需要連接的報活?當(dāng)雙方已經(jīng)建立了連接,但因?yàn)榫W(wǎng)絡(luò)問題,鏈路不通,這樣長連接就不能使用了。需要明確的一點(diǎn)是,通過 netstat,lsof 等指令查看到連接的狀態(tài)處于 ESTABLISHED 狀態(tài)并不是一件非常靠譜的事,因?yàn)檫B接可能已死,但沒有被系統(tǒng)感知到,更不用提假死這種疑難雜癥了。如果保證長連接可用是一件技術(shù)活。

6 連接的保活:KeepAlive

首先想到的是 TCP 中的 KeepAlive 機(jī)制。KeepAlive 并不是 TCP 協(xié)議的一部分,但是大多數(shù)操作系統(tǒng)都實(shí)現(xiàn)了這個機(jī)制。KeepAlive 機(jī)制開啟后,在一定時間內(nèi)(一般時間為 7200s,參數(shù) tcp_keepalive_time)在鏈路上沒有數(shù)據(jù)傳送的情況下,TCP 層將發(fā)送相應(yīng)的KeepAlive探針以確定連接可用性,探測失敗后重試 10(參數(shù) tcp_keepalive_probes)次,每次間隔時間 75s(參數(shù) tcp_keepalive_intvl),所有探測失敗后,才認(rèn)為當(dāng)前連接已經(jīng)不可用。

在 Netty 中開啟 KeepAlive:

  1. bootstrap.option(ChannelOption.TCP_NODELAY,true

Linux 操作系統(tǒng)中設(shè)置 KeepAlive 相關(guān)參數(shù),修改 /etc/sysctl.conf 文件:

  1. net.ipv4.tcp_keepalive_time=90 
  2. net.ipv4.tcp_keepalive_intvl=15 
  3. net.ipv4.tcp_keepalive_probes=2 
  • KeepAlive 機(jī)制是在網(wǎng)絡(luò)層面保證了連接的可用性,但站在應(yīng)用框架層面我們認(rèn)為這還不夠。主要體現(xiàn)在兩個方面:
  • KeepAlive 的開關(guān)是在應(yīng)用層開啟的,但是具體參數(shù)(如重試測試,重試間隔時間)的設(shè)置卻是操作系統(tǒng)級別的,位于操作系統(tǒng)的 /etc/sysctl.conf 配置中,這對于應(yīng)用來說不夠靈活。
  • KeepAlive 的保活機(jī)制只在鏈路空閑的情況下才會起到作用,假如此時有數(shù)據(jù)發(fā)送,且物理鏈路已經(jīng)不通,操作系統(tǒng)這邊的鏈路狀態(tài)還是 ESTABLISHED,這時會發(fā)生什么?自然會走 TCP 重傳機(jī)制,要知道默認(rèn)的 TCP 超時重傳,指數(shù)退避算法也是一個相當(dāng)長的過程。

KeepAlive 本身是面向網(wǎng)絡(luò)的,并不是面向于應(yīng)用的,當(dāng)連接不可用時,可能是由于應(yīng)用本身 GC 問題,系統(tǒng) load 高等情況,但網(wǎng)絡(luò)仍然是通的,此時,應(yīng)用已經(jīng)失去了活性,所以連接自然應(yīng)該認(rèn)為是不可用的。

看來,應(yīng)用層面的連接保活還是必須要做的。

7 連接的保活:應(yīng)用層心跳

終于點(diǎn)題了,文題中提到的心跳便是一個本文想要重點(diǎn)強(qiáng)調(diào)的另一個 TCP 相關(guān)的知識點(diǎn)。上一節(jié)我們已經(jīng)解釋過了,網(wǎng)絡(luò)層面的 KeepAlive 不足以支撐應(yīng)用級別的連接可用性,本節(jié)就來聊聊應(yīng)用層的心跳機(jī)制是實(shí)現(xiàn)連接保活的。

如何理解應(yīng)用層的心跳?簡單來說,就是客戶端會開啟一個定時任務(wù),定時對已經(jīng)建立連接的對端應(yīng)用發(fā)送請求(這里的請求是特殊的心跳請求),服務(wù)端則需要特殊處理該請求,返回響應(yīng)。如果心跳持續(xù)多次沒有收到響應(yīng),客戶端會認(rèn)為連接不可用,主動斷開連接。不同的服務(wù)治理框架對心跳,建連,斷連,拉黑的機(jī)制有不同的策略,但大多數(shù)的服務(wù)治理框架都會在應(yīng)用層做心跳,Dubbo 也不例外。

8 應(yīng)用層心跳的設(shè)計細(xì)節(jié)

以 Dubbo 為例,支持應(yīng)用層的心跳,客戶端和服務(wù)端都會開啟一個 HeartBeatTask,客戶端在 HeaderExchangeClient 中開啟,服務(wù)端將在 HeaderExchangeServer 開啟。文章開頭埋了一個坑:Dubbo 為什么在服務(wù)端同時維護(hù) Map

  1. // HeartBeatTask 
  2. if (channel instanceof Client) {  
  3.    ((Client) channel).reconnect();  
  4. else { 
  5.    channel.close(); 

熟悉其他 RPC 框架的同學(xué)會發(fā)現(xiàn),不同框架的心跳機(jī)制真的是差距非常大。心跳設(shè)計還跟連接創(chuàng)建,重連機(jī)制,黑名單連接相關(guān),還需要具體框架具體分析。

除了定時任務(wù)的設(shè)計,還需要在協(xié)議層面支持心跳。最簡單的例子可以參考 nginx 的健康檢查,而針對 Dubbo 協(xié)議,自然也需要做心跳的支持,如果將心跳請求識別為正常流量,會造成服務(wù)端的壓力問題,干擾限流等諸多問題。

 

其中 Flag 代表了 Dubbo 協(xié)議的標(biāo)志位,一共 8 個地址位。低四位用來表示消息體數(shù)據(jù)用的序列化工具的類型(默認(rèn) hessian),高四位中,***位為1表示是 request 請求,第二位為 1 表示雙向傳輸(即有返回response),第三位為 1 表示是心跳事件。

心跳請求應(yīng)當(dāng)和普通請求區(qū)別對待。

9 注意和 HTTP 的 KeepAlive 區(qū)別對待

  • HTTP 協(xié)議的 KeepAlive 意圖在于連接復(fù)用,同一個連接上串行方式傳遞請求-響應(yīng)數(shù)據(jù)
  • TCP 的 KeepAlive 機(jī)制意圖在于保活、心跳,檢測連接錯誤。

這壓根是兩個概念。

10 KeepAlive 常見異常

啟用 TCP KeepAlive 的應(yīng)用程序,一般可以捕獲到下面幾種類型錯誤

ETIMEOUT 超時錯誤,在發(fā)送一個探測保護(hù)包經(jīng)過 (tcpkeepalivetime + tcpkeepaliveintvl * tcpkeepaliveprobes)時間后仍然沒有接收到 ACK 確認(rèn)情況下觸發(fā)的異常,套接字被關(guān)閉 java java.io.IOException:Connectiontimedout

EHOSTUNREACH host unreachable(主機(jī)不可達(dá))錯誤,這個應(yīng)該是 ICMP 匯報給上層應(yīng)用的。 java java.io.IOException:Noroute to host

鏈接被重置,終端可能崩潰死機(jī)重啟之后,接收到來自服務(wù)器的報文,然物是人非,前朝往事,只能報以無奈重置宣告之。 java java.io.IOException:Connectionresetbypeer

11 總結(jié)

有三種使用 KeepAlive 的實(shí)踐方案:

1.默認(rèn)情況下使用 KeepAlive 周期為 2 個小時,如不選擇更改,屬于誤用范疇,造成資源浪費(fèi):內(nèi)核會為每一個連接都打開一個保活計時器,N 個連接會打開 N 個保活計時器。 優(yōu)勢很明顯:

  • TCP 協(xié)議層面保活探測機(jī)制,系統(tǒng)內(nèi)核完全替上層應(yīng)用自動給做好了
  • 內(nèi)核層面計時器相比上層應(yīng)用,更為高效
  • 上層應(yīng)用只需要處理數(shù)據(jù)收發(fā)、連接異常通知即可
  • 數(shù)據(jù)包將更為緊湊

2.關(guān)閉 TCP 的 KeepAlive,完全使用應(yīng)用層心跳保活機(jī)制。由應(yīng)用掌管心跳,更靈活可控,比如可以在應(yīng)用級別設(shè)置心跳周期,適配私有協(xié)議。

3.業(yè)務(wù)心跳 + TCP KeepAlive 一起使用,互相作為補(bǔ)充,但 TCP 保活探測周期和應(yīng)用的心跳周期要協(xié)調(diào),以互補(bǔ)方可,不能夠差距過大,否則將達(dá)不到設(shè)想的效果。

各個框架的設(shè)計都有所不同,例如 Dubbo 使用的是方案三,但阿里內(nèi)部的 HSF 框架則沒有設(shè)置 TCP 的 KeepAlive,僅僅由應(yīng)用心跳保活。和心跳策略一樣,這和框架整體的設(shè)計相關(guān)。

責(zé)任編輯:武曉燕 來源: Kirito的技術(shù)分享
相關(guān)推薦

2019-01-28 18:02:21

網(wǎng)絡(luò)安全網(wǎng)絡(luò)安全技術(shù)周刊

2021-01-13 11:11:29

TCP連接耗時網(wǎng)絡(luò)協(xié)議

2022-09-09 08:08:28

開源項(xiàng)目服務(wù)

2017-12-21 08:06:40

2020-11-30 13:10:39

MySQL安全服務(wù)器

2021-08-06 11:50:49

Linux 字節(jié)對齊Linux 系統(tǒng)

2023-07-31 08:21:22

語法校對器Pick

2019-09-23 08:27:15

TCP長連接心跳

2024-02-21 07:24:21

微服務(wù)單體架構(gòu)MVC

2022-12-02 13:49:41

2024-10-14 12:34:08

2021-10-26 11:42:51

系統(tǒng)

2020-10-30 07:43:35

Jenkins配置前端

2020-10-07 22:21:13

程序員技術(shù)線程

2022-02-15 19:50:18

加載器JAVAJDK

2024-07-16 10:25:27

2021-03-07 16:31:35

Java編譯反編譯

2021-06-02 08:33:31

TPCTPC-H系統(tǒng)

2020-05-09 12:16:12

中臺架構(gòu)工具

2014-07-22 10:42:04

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 337p日本欧洲亚洲大胆鲁鲁 | 日本三级做a全过程在线观看 | 青青草av在线播放 | 国产 日韩 欧美 制服 另类 | 免费成人高清在线视频 | 久久久久久国产精品 | 欧美日韩一区不卡 | 久久久蜜桃一区二区人 | 国产精品视频一区二区三区 | 亚洲精品日日夜夜 | 欧美一级久久 | 国产视频二区 | 日韩精品1区2区 | www.中文字幕.com| 国产精品日本一区二区在线播放 | 国产精品一区久久久 | 国产精品毛片一区二区在线看 | 天堂一区 | 日韩高清中文字幕 | 国产1区2区 | 99久久久99久久国产片鸭王 | 亚洲欧洲中文日韩 | 91免费电影 | 婷婷99 | 爱草视频| 日韩欧美操 | 不卡在线一区 | 成人国产精品免费观看 | 91精品国产综合久久久久久漫画 | 国产精品国产成人国产三级 | 国产精品久久久亚洲 | yeyeav| 国产精品一区二区欧美 | 久久久久久国产精品 | 人成在线视频 | 亚洲高清中文字幕 | 国产色在线 | 麻豆hd | 国产精品视频久久 | 国产日韩欧美一区二区 | 国产成人jvid在线播放 |