日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網營銷解決方案
黑科技解密!實現Socket進程間遷移!

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

為云城等地區(qū)用戶提供了全套網頁設計制作服務,及云城網站建設行業(yè)解決方案。主營業(yè)務為成都網站設計、成都做網站、云城網站設計,以傳統(tǒng)方式定制建設網站,并提供域名空間備案等一條龍服務,秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務。我們深信只要達到每一位用戶的要求,就會得到認可,從而選擇與我們長期合作。這樣,我們也可以走得更遠!

今天介紹一個可以拿出去吹牛的功能:實現socket句柄在進程之間遷移!為了這篇文章,xjjdog可算下了苦功夫,半夜還在翻資料。因為需要驗證后,才能證明這項技術確實是正確的。

正文。

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

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

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

1. 零停機更新

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

實現零停機更新。

這個是可以的。Facebook就實踐過類似的技術,它們把這項技術,叫做Socket Takeover。千萬別用百度搜這個關鍵字,你得到的可能是一堆垃圾。

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

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

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

2. 技術要點

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

 
 
 
 
  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,從一個進程,傳遞到另外一個進程中去。怎么發(fā)送呢?肯定是要通過一個通道的。在Linux上,那就是UDS,全稱Unix Domain Sockets。

2.1 Unix Domain Sockets監(jiān)聽

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

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

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

這樣。其他的進程,就可以通過兩種不同的方式,來連接我們的服務。

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

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

2.2 fd遷移技術要點

怎么遷移呢?我們關鍵看第二步。

實際上,當新升級的服務通過UDS連接上來,我們就開始使用sendmsg函數,將listen_fd給轉移過去。

我們來看一下sendmsg這個函數的參數。

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

socket可以理解為我們的UDS連接。關鍵在于msghdr這個結構體。

 
 
 
 
  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ā)送的數據,比如HelloWord;除此之外,還有兩個ancillary (附屬的) 的變量,提供了附加的功能,那就是變量msg_control和msg_controllen。其中,msg_control又指向了另外一個結構體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. }; 

在這個結構體中,有一個叫做cmsg_type的成員變量,是我們實現socket遷移的關鍵。

它共有三個類型。

  • SCM_RIGHTS
  • SCM_CREDENTIALS
  • SCM_SECURITY

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

 
 
 
 
  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列表,設置在cmsg_data上 
  8. int *fds = (int *) CMSG_DATA(cmsg); 

依靠sendmsg函數,socket句柄就發(fā)送到另外一個進程了。

3. 接收和還原

同樣的,recvmsg函數,將會接收這部分數據,然后將其還原成cmsghdr結構體。然后我們就可以從cmsg_data中獲取句柄列表。

為什么能這么做呢?因為socket句柄,在某個進程里,其實只是一個引用。真正的fd句柄,其實是放在內核中的。所謂的遷移,只不過是把一個指針,從一個進程中去掉,再加到另外一個進程中罷了。

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

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

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

對于普通fd,肯定要調用與原新連接到來時相同的代碼邏輯。所以,一個大體的遷移過程,包括:

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

4. End

這是一項黑科技,其實已經在一些主流的應用中使用了。你會看到一些非常眼熟的軟件,這項功能是它們的一大賣點。比如HAProxy,運行在4層網絡的負載均衡;比如Envoy,Istio默認的數據平面軟件,使用類似的技術完成熱重啟。

其實,在servicemesh的推進過程中,proxy的替換,也會使用類似的技術,比如SOFA。對于golang和C語言來說,由于API暴露的比較好,這種功能可以很容易的實現;但在Java中,卻有不少的困難,因為Java的跨平臺特性不會做這種為Linux定制的API。

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

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


網站欄目:黑科技解密!實現Socket進程間遷移!
本文路徑:http://www.5511xx.com/article/cdooggj.html