百万并发的长连接是否会耗尽反向代理的端口号

在做性能测试的时候,百万并发的长连接是否会耗尽反向代理的端口号呢?答案:不会。
解决方法如下:
多个后端配成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]='';
                    printf("Socket %d said : %s
",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 != '')
    {
        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] = '';
    cout << "recv len: " << uiRecvTotal << ", msg: " << szRecvBuf << endl; 
	usleep(1000000000);
    close(iSockFd);

    return 0;
}

原文地址:https://www.cnblogs.com/bugutian/p/12743201.html