自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

黑科技解密!實(shí)現(xiàn)Socket進(jìn)程間遷移!

開發(fā) 前端
今天介紹一個(gè)可以拿出去吹牛的功能:實(shí)現(xiàn)socket句柄在進(jìn)程之間遷移!為了這篇文章,xjjdog可算下了苦功夫,半夜還在翻資料。因?yàn)樾枰?yàn)證后,才能證明這項(xiàng)技術(shù)確實(shí)是正確的。

 [[409990]]

本文轉(zhuǎn)載自微信公眾號(hào)「小姐姐味道」,作者小姐姐養(yǎng)的狗。轉(zhuǎn)載本文請(qǐng)聯(lián)系小姐姐味道公眾號(hào)。

今天介紹一個(gè)可以拿出去吹牛的功能:實(shí)現(xiàn)socket句柄在進(jìn)程之間遷移!為了這篇文章,xjjdog可算下了苦功夫,半夜還在翻資料。因?yàn)樾枰?yàn)證后,才能證明這項(xiàng)技術(shù)確實(shí)是正確的。

正文。

我們的服務(wù)器上,運(yùn)行著大量的server實(shí)例(instance)。這些instance,每個(gè)都要承載著數(shù)十萬的連接和非常繁忙的網(wǎng)絡(luò)請(qǐng)求。能夠把這樣的連接數(shù),這樣的流量,玩弄于股掌之間,是每個(gè)互聯(lián)網(wǎng)程序員的夢(mèng)想。

但軟件總是要升級(jí)的,每當(dāng)升級(jí)的時(shí)候,就需要先停掉原來的instance,然后再啟動(dòng)一個(gè)新的。在這一停一起之間,數(shù)十秒就過去了,更不要說JAVA這種啟動(dòng)時(shí)間就能生個(gè)孩子的速度了。

傳統(tǒng)的做法,是先把這個(gè)instance從負(fù)載均衡上面摘除,然后啟動(dòng)起來再加上;對(duì)于微服務(wù)來說,就要先隔離,然后啟動(dòng)后再取消隔離。這些操作,對(duì)于海量應(yīng)用來說,就是個(gè)噩夢(mèng)。

1. 零停機(jī)更新

有沒有一種方法,能夠把一個(gè)進(jìn)程所掛載的連接(socket),轉(zhuǎn)移到另外一個(gè)進(jìn)程之上呢?這樣,我在升級(jí)的時(shí)候,就可可以先啟動(dòng)一個(gè)升級(jí)版本的進(jìn)程,然后把老進(jìn)程的socket,one by one的給轉(zhuǎn)移過去。

實(shí)現(xiàn)零停機(jī)更新。

這個(gè)是可以的。Facebook就實(shí)踐過類似的技術(shù),它們把這項(xiàng)技術(shù),叫做Socket Takeover。千萬別用百度搜這個(gè)關(guān)鍵字,你得到的可能是一堆垃圾。

這么牛x的技術(shù),還這么有用,為什么就沒人科普呢?別問我,我也不知道,可能大家現(xiàn)在都在糾結(jié)怎么研究茴香豆的茴字寫法,沒時(shí)間干正事吧。

那今天就由xjjdog來介紹一下吧,順便增加一下大家以后的吹牛資本。

這個(gè)牛x的功能,是由Linux一對(duì)底層的系統(tǒng)調(diào)用函數(shù)所實(shí)現(xiàn)的:sendmsg()和recvmsg()。我們一般在發(fā)送網(wǎng)絡(luò)數(shù)據(jù)包的時(shí)候,一般會(huì)使用send函數(shù),但send函數(shù)只有在socket處于連接狀態(tài)時(shí)才可以使用;與之不同的是,sendmsg在任何時(shí)候都可以使用。

2. 技術(shù)要點(diǎn)

在c語言網(wǎng)絡(luò)編程中,首先要通過listen函數(shù),來注冊(cè)監(jiān)聽地址,然后再用accept函數(shù)接收新連接。比如:

  1. int listen_fd = socket(addr->ss_family, SOCK_STREAM, 0); 
  2. ... 
  3. bind(listen_fd, (struct sockaddr *) addr, addrlen); 
  4. ... 
  5. int accept_fd = accept(fd, (struct sockaddr *) &addr, &addrlen); 

int accept_fd = accept(fd, (struct sockaddr *) &addr, &addrlen);

我們首先要做的,就是把listen_fd,從一個(gè)進(jìn)程,傳遞到另外一個(gè)進(jìn)程中去。怎么發(fā)送呢?肯定是要通過一個(gè)通道的。在Linux上,那就是UDS,全稱Unix Domain Sockets。

2.1 Unix Domain Sockets監(jiān)聽

UDS(Unix Domain Sockets)在Linux上的表現(xiàn),是一個(gè)文件。相比較于普通socket監(jiān)聽在端口上,一個(gè)進(jìn)程也可以監(jiān)聽在一個(gè)UDS文件上,比如/tmp/xjjdog.sock。由于通過這個(gè)文件進(jìn)行數(shù)據(jù)傳輸,并不需要走網(wǎng)卡等物理設(shè)備,所以通過UDS傳輸數(shù)據(jù),速度是非??斓摹?/p>

但今天我們不關(guān)心它有多塊,而是關(guān)心它多有用。通過bind函數(shù),我們同樣可以通過這個(gè)文件接收連接,就像端口接收連接一樣。

  1. struct sockaddr_un addr; 
  2. char *path="/tmp/xjjdog.sock"
  3. int err, fd; 
  4. fd = socket(AF_UNIX, SOCK_STREAM, 0); 
  5. memset(&addr, 0, sizeof(struct sockaddr_un)); 
  6. addr.sun_family = AF_UNIX; 
  7. strncpy(addr.sun_path, path, strlen(path)); 
  8. addrlen = sizeof(addr.sun_family) + strlen(path); 
  9. err = bind(fd, (struct sockaddr *) &addr, addrlen); 
  10. ... 
  11. accept_fd = accept(fd, (struct sockaddr *) &addr, &addrlen); 

這樣。其他的進(jìn)程,就可以通過兩種不同的方式,來連接我們的服務(wù)。

通過端口:進(jìn)行正常的服務(wù),輸出正常的業(yè)務(wù)數(shù)據(jù)。執(zhí)行正常業(yè)務(wù)

通過UDS:開始接收listen_fd和accept_fd們。執(zhí)行不停機(jī)遷移socket業(yè)務(wù)

2.2 fd遷移技術(shù)要點(diǎn)

怎么遷移呢?我們關(guān)鍵看第二步。

實(shí)際上,當(dāng)新升級(jí)的服務(wù)通過UDS連接上來,我們就開始使用sendmsg函數(shù),將listen_fd給轉(zhuǎn)移過去。

我們來看一下sendmsg這個(gè)函數(shù)的參數(shù)。

  1. ssize_t sendmsg( 
  2.     int socket, 
  3.     const struct msghdr *message, 
  4.     int flags 
  5. ); 

socket可以理解為我們的UDS連接。關(guān)鍵在于msghdr這個(gè)結(jié)構(gòu)體。

  1. struct msghdr { 
  2.     void            *msg_name;      /* optional address */ 
  3.     socklen_t       msg_namelen;    /* size of address */ 
  4.     struct          iovec *msg_iov; /* scatter/gather array */ 
  5.     int             msg_iovlen;     /* # elements in msg_iov */ 
  6.     void            *msg_control;   /* ancillary data, see below */ 
  7.     socklen_t       msg_controllen; /* ancillary data buffer len */ 
  8.     int             msg_flags;      /* flags on received message */ 
  9. }; 

其中, msg_iov表示要正常發(fā)送的數(shù)據(jù),比如HelloWord;除此之外,還有兩個(gè)ancillary (附屬的) 的變量,提供了附加的功能,那就是變量msg_control和msg_controllen。其中,msg_control又指向了另外一個(gè)結(jié)構(gòu)體cmsghdr。

  1. struct cmsghdr { 
  2.     socklen_t cmsg_len;    /* data byte count, including header */ 
  3.     int       cmsg_level;  /* originating protocol */ 
  4.     int       cmsg_type;   /* protocol-specific type */ 
  5.     /* followed by */ 
  6.     unsigned char cmsg_data[]; 
  7. }; 

在這個(gè)結(jié)構(gòu)體中,有一個(gè)叫做cmsg_type的成員變量,是我們實(shí)現(xiàn)socket遷移的關(guān)鍵。

它共有三個(gè)類型。

  • SCM_RIGHTS
  • SCM_CREDENTIALS
  • SCM_SECURITY

其中,SCM_RIGHTS就是我們所需要的,它允許我們從一個(gè)進(jìn)程,發(fā)送一個(gè)文件句柄到另外一個(gè)進(jìn)程。

  1. struct msghdr msg; 
  2. ... 
  3. struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 
  4. cmsg->cmsg_level = SOL_SOCKET; 
  5. cmsg->cmsg_type = SCM_RIGHTS; 
  6.  
  7. //socket fd列表,設(shè)置在cmsg_data上 
  8. int *fds = (int *) CMSG_DATA(cmsg); 

依靠sendmsg函數(shù),socket句柄就發(fā)送到另外一個(gè)進(jìn)程了。

3. 接收和還原

同樣的,recvmsg函數(shù),將會(huì)接收這部分?jǐn)?shù)據(jù),然后將其還原成cmsghdr結(jié)構(gòu)體。然后我們就可以從cmsg_data中獲取句柄列表。

為什么能這么做呢?因?yàn)閟ocket句柄,在某個(gè)進(jìn)程里,其實(shí)只是一個(gè)引用。真正的fd句柄,其實(shí)是放在內(nèi)核中的。所謂的遷移,只不過是把一個(gè)指針,從一個(gè)進(jìn)程中去掉,再加到另外一個(gè)進(jìn)程中罷了。

fd句柄的屬性,有兩種情況。

  • 監(jiān)聽fd,直接調(diào)用accept函數(shù)作用在fd上即可
  • 普通fd,需要將其還原成正常的socket

圖片來自論文:(Zero Downtime Release: Disruption-free Load Balancing of a Multi-Billion User Website)

對(duì)于普通fd,肯定要調(diào)用與原新連接到來時(shí)相同的代碼邏輯。所以,一個(gè)大體的遷移過程,包括:

  1. 首先遷移listener fd到新進(jìn)程,并開啟監(jiān)聽,以便新進(jìn)程能快速接收新的請(qǐng)求。如果我們開啟了SO_REUSEADDR選項(xiàng),新老服務(wù)甚至能夠一起進(jìn)行服務(wù)
  2. 等待新進(jìn)程預(yù)熱之后,停掉原進(jìn)程的監(jiān)聽
  3. 遷移原老進(jìn)程中的大量socket,這些socket可能有數(shù)萬條,最好編碼能看到遷移進(jìn)度
  4. 新進(jìn)程接收到這些socket,陸續(xù)將其還原為正常的連接。相當(dāng)于略過了accept階段,直接就獲取了socket列表
  5. 遷移完畢,老進(jìn)程就空轉(zhuǎn)了,此時(shí)可以安全的停掉

4. End

這是一項(xiàng)黑科技,其實(shí)已經(jīng)在一些主流的應(yīng)用中使用了。你會(huì)看到一些非常眼熟的軟件,這項(xiàng)功能是它們的一大賣點(diǎn)。比如HAProxy,運(yùn)行在4層網(wǎng)絡(luò)的負(fù)載均衡;比如Envoy,Istio默認(rèn)的數(shù)據(jù)平面軟件,使用類似的技術(shù)完成熱重啟。

其實(shí),在servicemesh的推進(jìn)過程中,proxy的替換,也會(huì)使用類似的技術(shù),比如SOFA。對(duì)于golang和C語言來說,由于API暴露的比較好,這種功能可以很容易的實(shí)現(xiàn);但在Java中,卻有不少的困難,因?yàn)镴ava的跨平臺(tái)特性不會(huì)做這種為Linux定制的API。

可以看到,sendmsg和recvmsg這兩個(gè)函數(shù),可以實(shí)現(xiàn)的功能非常的酷。它比較適合無狀態(tài)的proxy服務(wù),如果服務(wù)內(nèi)有狀態(tài)存留,這種遷移并不見得安全,當(dāng)然也可以嘗試把此項(xiàng)技術(shù)運(yùn)用在一些中間件上。但無論如何,這種黑科技,有一種別樣的暴力美,肯定會(huì)把windows server用戶給饞哭的。

作者簡介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。

 

責(zé)任編輯:武曉燕 來源: 小姐姐味道
相關(guān)推薦

2020-11-04 07:17:42

Nodejs通信進(jìn)程

2023-09-27 23:32:46

Python監(jiān)控進(jìn)程

2022-02-07 13:34:05

冬奧會(huì)黑科技機(jī)器人

2013-03-28 13:14:45

AIDL進(jìn)程間通信Android使用AI

2018-03-28 09:35:16

數(shù)據(jù)系統(tǒng)云服務(wù)

2017-08-06 00:05:18

進(jìn)程通信開發(fā)

2018-07-23 06:38:40

AI芯片數(shù)據(jù)中心

2016-07-07 15:38:07

京東

2013-08-27 09:24:22

SDN網(wǎng)絡(luò)軟件定義網(wǎng)絡(luò)谷歌SDN

2016-07-14 16:40:56

黑科技

2024-01-05 08:41:31

進(jìn)程間通信IPC異步通信

2024-01-03 10:17:51

Linux通信

2010-01-05 10:00:48

Linux進(jìn)程間通信

2023-06-08 16:41:06

人工智能

2020-07-10 10:34:22

人工智能無人機(jī)物聯(lián)網(wǎng)

2017-07-07 10:24:56

云計(jì)算

2011-06-13 09:15:18

AIXlinuxunix

2021-01-22 10:58:16

網(wǎng)絡(luò)安全進(jìn)程間碼如

2016-11-10 19:31:00

蘇寧雙11

2011-06-22 17:09:50

QT 進(jìn)程 通信
點(diǎn)贊
收藏

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