百萬並發的長連接是否會耗盡反向代理的端口號


在做性能測試的時候,百萬並發的長連接是否會耗盡反向代理的端口號呢?答案:不會。
解決方法如下:
多個后端配成upstream,反向代理的端口會被復用,沒有問題。
如果后端是一對一的,源目的端口相同,那就沒必要用反向代理了。
如果是需要一個類似的中轉過濾的部件,那么你需要自己設計一個部件。

說到這個問題,還是要從當初還是一個新手的時候說起。

新手疑問

新手對於長連接的幾個疑問?
1.與一個服務端進行長連接是否會消耗服務端的端口號?
2.與多個服務端進行長連接是否會消耗客戶端的端口號?

答案:
1.完全不消耗;
2.可以不消耗:

TCP的連接的“主鍵”是(源IP,目的IP,源端口,目的端口)
也就是說,只要源IP、目的IP、源端口、目的端口四個因素只要有一個不同就可以標識不同的TCP連接。

對於問題1,大家都很常見,沒有什么異議。因為客戶端主動與服務端進行連接,服務端的端口是復用的,我們從來沒有為服務端的端口數擔心過。
對於問題2,大家不一定常見。比如說nginx作為反向代理進行長連接,這時,是否會消耗代理服務器的端口號?那肯定是消耗的。

為什么之前沒聽說有端口影響並發的?

但是我們通常沒有遇到這種問題,為什么呢?

因為這個問題完全不用擔心。我們分幾種情況討論。
我們假定有個業務(P2P的長連接)是這樣的:A->B->C->D
A: 表示大量不同IP的客戶端;
B: 表示一個類似反向代理的部件;
C: 表示后端的服務;
D: 表示大量不同IP的目的客戶端;

那么,分析如下:
A->B: B只提供一個端口服務,不會耗用B的端口;
B->C: 源IP端口和目的IP端口都相同,只要建立有數的幾個長連接就可以轉發了(即B->C的連接做成一個連接池),沒必要每個A的會話建立多個連接,因此也不用消耗端口:
C->D: 目的IP不同,C可以綁定一個端口去主動連接D,不會消耗端口,或者D反向連接C,這樣C變成了TCP的服務端,也不會消耗端口.

所以在長連接並發的情況下,端口不會成為整個軟件解決方案的瓶頸。

TCP指定客戶端的端口號

什么?你不知道客戶端可以主動綁定端口去連接服務端?
那可以參考我寫的Demo代碼:
結果如下:

./tcpserver(啟動監聽10001)
./tcpserver(啟動監聽10002)

./tcpcli 192.168.0.1 10001 hello
./tcpcli 192.168.0.1 10002 hello

然后可以看到如下結果:

[root@master135 ~]# netstat -lanp | grep tcpcli
tcp        0      0 192.168.0.1:12345    192.168.0.2:10002     ESTABLISHED 22808/./tcpcli2     
tcp        0      0 192.168.0.1:12345    192.168.0.2:10001     ESTABLISHED 22805/./tcpcli2    

服務端:

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/resource.h>
 
#define MAXLINE  1024
#define OPEN_MAX  16 
#define SERV_PORT  10001
 
int main()
{
    int i , maxi ,listenfd , connfd , sockfd ,epfd, nfds;
    int n;
    char buf[MAXLINE];
    struct epoll_event ev, events[20];  
    socklen_t clilen;
    struct pollfd client[OPEN_MAX];
 
    struct sockaddr_in cliaddr , servaddr;
    listenfd = socket(AF_INET , SOCK_STREAM , 0);
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 
    bind(listenfd , (struct sockaddr *) & servaddr, sizeof(servaddr));
    listen(listenfd,10);
    
    epfd = epoll_create(256);
    ev.data.fd=listenfd; 
    ev.events=EPOLLIN|EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
    
    for(;;)
    {
        nfds=epoll_wait(epfd,events,20,500); 
        for(i=0; i<nfds; i++)
        {
            if (listenfd == events[i].data.fd)
            {
                clilen = sizeof(cliaddr);
                connfd = accept(listenfd , (struct sockaddr *)&cliaddr, &clilen);
                if(connfd < 0)  
                {  
                    perror("connfd < 0");  
                    exit(1);  
                }
                ev.data.fd=connfd; 
                ev.events=EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);                
            }
            else if (events[i].events & EPOLLIN)
            {
                if ( (sockfd = events[i].data.fd) < 0)  
                    continue;  
                n = recv(sockfd,buf,MAXLINE,0);
                if (n <= 0)   
                {    
                    close(sockfd);  
                    events[i].data.fd = -1;  
                }
                else
                {
                    buf[n]='\0';
                    printf("Socket %d said : %s\n",sockfd,buf);
                    ev.data.fd=sockfd; 
                    ev.events=EPOLLOUT|EPOLLET;
                    epoll_ctl(epfd,EPOLL_CTL_MOD,connfd,&ev);
                }
            }
            else if( events[i].events&EPOLLOUT )
            {
                sockfd = events[i].data.fd;  
                send(sockfd, "Hello!", 7, 0);  
                  
                ev.data.fd=sockfd;  
                ev.events=EPOLLIN|EPOLLET;  
                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); 
            }
            else 
            {
                printf("This is not avaible!");
            }
        }
    }
    close(epfd);  
    return 0;
}

客戶端:

#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

using namespace std;

int main(int argc, char *argv[])
{
    if (argc != 4)
    {
        cout << "usage: " << argv[0] << " ip port message" << endl;
        return -1;
    }

    char *szIp = argv[1];
    in_addr_t iIp = inet_addr(szIp);
    if (iIp == INADDR_NONE)
    {
        cerr << "fail to parse ip: " << szIp << endl;
        return -1;
    }
    char *pEnd = NULL;
    uint16_t usPort = strtoul(argv[2], &pEnd, 10);
    if (*pEnd != '\0')
    {
        cerr << "fail to parse port: " << argv[2] << endl;
        return -1;
    }
    char *szMsg = argv[3];
    size_t uiMsgLen = strlen(szMsg);

    int iSockFd = socket(AF_INET, SOCK_STREAM, 0);
    if (iSockFd < 0)
    {
        cerr << "fail to create socket, err: " << strerror(errno) << endl;
        return -1;
    }
    cout << "create socket fd " << iSockFd << endl;

    sockaddr_in client;
    memset(&client, 0, sizeof(client));
    client.sin_family = AF_INET;
    client.sin_addr.s_addr = htonl(INADDR_ANY);
    client.sin_port = htons(12345);
	setsockopt(iSockFd,SOL_SOCKET,SO_REUSEADDR,&client,sizeof(client));

    sockaddr_in oServerAddr;
    memset(&oServerAddr, 0, sizeof(oServerAddr));
    oServerAddr.sin_family = AF_INET;
    oServerAddr.sin_addr.s_addr = iIp;
    oServerAddr.sin_port = htons(usPort);
    

    
    if (bind(iSockFd, (sockaddr *)&client, sizeof(client)) < 0)
    {
        cerr << "bind failed, err: " << strerror(errno) << endl;
        return -1;
    }
    if (connect(iSockFd, (sockaddr *)&oServerAddr, sizeof(oServerAddr)) < 0)
    {
        cerr << "fail to connect to " << szIp << ":" << usPort << ", err: " << strerror(errno) << endl;
        return -1;
    }
    cout << "connect to " << szIp << ":" << usPort << endl;

    ssize_t iSendLen = send(iSockFd, szMsg, uiMsgLen, 0);
    if (iSendLen < 0)
    {
        cerr << "fail to send, err: " << strerror(errno) << endl;
        return -1;
    }
    cout << "send len: " << iSendLen << ", msg: " << szMsg << endl;

    char szRecvBuf[1024 * 1024];
    char *pRecvBuf = szRecvBuf;
    size_t uiBufLen = sizeof(szRecvBuf);
    size_t uiRecvTotal = 0;
    while (uiRecvTotal < iSendLen)
    {
        ssize_t iRecvLen = recv(iSockFd, pRecvBuf, uiBufLen, 0);
        if (iRecvLen < 0)
        {
            cerr << "fail to recv, close connection, err: " << strerror(errno) << endl;
            close(iSockFd);
            return -1;
        }
        if (iRecvLen == 0)
        {
            cout << "connection closed by server" << endl;
            close(iSockFd);
            break;
        }
        pRecvBuf += iRecvLen;
        uiBufLen -= iRecvLen;
        uiRecvTotal += iRecvLen;
    }
    szRecvBuf[uiRecvTotal] = '\0';
    cout << "recv len: " << uiRecvTotal << ", msg: " << szRecvBuf << endl; 
	usleep(1000000000);
    close(iSockFd);

    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM