• linux系统实现多个进程监听同一个端口


    通过 fork 创建子进程的方式可以实现父子进程监听相同的端口。

    方法:在绑定端口号(bind函数)之后,监听端口号之前(listen函数),用fork()函数生成子进程,这样子进程就可以克隆父进程,达到监听同一个端口的目的。

    # 代码示例:一主一子
    import socket
    import select
    import sys
    import struct
    import os
    import time
    
    
    if __name__ == '__main__':
        pid = os.getpid()
        s1 = socket.socket()  # 创建 socket 对象
        # host = socket.gethostname()  # 获取本地主机名
        host = '127.0.0.1'
        port1 = 12346  # 设置端口号
        # port2 = 12347
    
        # --关键代码--
        s1.bind((host, port1))
        pid1 = os.fork()
    
        print("我会被主子进程分别执行一次")
        # 也可以分别写到分子进程里
        s1.listen(5)
        # --关键代码--
        
        while True:
            if pid1 == 0:	        	
            	# s1.listen(5)
                print("子进程")
                socket1, addr1 = s1.accept()
                print(addr1)
                socket1.send("子进程响应".encode('utf-8'))
                socket1.close()
                print('结束服务端子进程')
            else:
            	# s1.listen(5)
                print("主进程")
                socket2, addr2=s1.accept()
                print(addr2)
                socket2.send("主进程响应".encode('utf-8'))
                socket2.close()
                print('结束服务端主进程')
    
    # 代码示例:一主多子		
    import socket
    import select
    import sys
    import struct
    import os
    import time
    
    
    if __name__ == '__main__':
        pid = os.getpid()
        s1 = socket.socket()  # 创建 socket 对象
        # host = socket.gethostname()  # 获取本地主机名
        host = '127.0.0.1'
        port1 = 12346  # 设置端口号
        # port2 = 12347
        s1.bind((host, port1))
        pid1 = os.fork()
        # s1.listen(5)
        print("我会被主子进程分别执行一次")
        while True:
            if pid1 == 0:
                s1.listen(5)
                print("子进程1")
                socket1, addr1 = s1.accept()
                print(addr1)
                socket1.send("子进程响应1".encode('utf-8'))
                socket1.close()
                print('结束服务端子进程1')
            elif pid1 != 0:
                pid2 = os.fork()
                if pid2 == 0:
                    s1.listen(5)
                    print("子进程2")
                    socket2, addr2 = s1.accept()
                    print(addr2)
                    socket2.send("子进程响应2".encode('utf-8'))
                    socket2.close()
                    print('结束服务端子进程2')
                else:
                    s1.listen(5)
                    print("主进程")
                    socket2, addr2 = s1.accept()
                    print(addr2)
                    socket2.send("主进程响应".encode('utf-8'))
                    socket2.close()
                    print('结束服务端主进程')	
    
    # 试想下子进程还有子进程的写法和用法		            
    

    惊群现象

    当连接到来时,子进程、父进程都可以 accept, 这就是著名的“惊群”问题(thundering herd problem)。

    在该模型下(多个子进程同时共享监听套接字)即可实现服务器并发处理客户端的连接。这里要注意的是,计算机三次握手创建连接是在内核进程里完成的,不需要应用服务进程参数的,而服务进程仅仅要做的是调用accept将已建立的连接构建对应的连接套接字connfd(可参考 http://blog.csdn.net/ordeder/article/details/21551567)。多个服务进程同时阻塞在accept等待监听套接字已建立连接的信息,那么当内核在该监听套接字上建立一个连接,那么将同时唤起这些处于accept阻塞的服务进程,从而导致“惊群现象”的产生,唤起多余的进程将影响服务器的性能(仅有一个服务进程accept成功,其他进程被唤起后没抢到“连接”而再次进入休眠)。

    应用多进程多线程模型

    一直疑惑一个应用app如何才能以多进程,多线程的方式运行。对于多线程可能很好理解,我们只要在进程中启用多线程的模式即可。也就是来一个请求,我们就用函数pthread_create()启用一个线程即可。这样我们的应用就可以在单进程,多线程的模式下工作。

    一个应用app通常工作在多进程,多线程的模式下,它的效率是最高的。那么我们如何才能做到多进程模式呢?经验告诉我们,如果多次启动一个进程会报错:“Address already in use!"。这是由于bind函数导致的,由于该端口号已经被监听了。

    fork原理

    fork时,子进程复制一份父进程的资源。然后父子进程分别执行os.fork()之后的程序

    子进程中fork函数返回0,父进程中返回子进程的pid

    Python的os.fork()是一个会返回两次的函数

    https://www.cnblogs.com/Magic-Dev/p/11405448.html

    通过linux内核的SO_REUSEPORT选项实现多个进程监听相同的端口

    # reuseport.py代码
    
    import socket
    import os
    #xiaorui.cc
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    s.bind(('0.0.0.0', 1234))
    s.listen(1)
    
    while True:
        conn, addr = s.accept()
        print('Connected to {}'.format(os.getpid()))
        data = conn.recv(1024)
        conn.send(data)
        conn.send(str(os.getpid()))
        conn.close()
    
    # 启动多个进程    
    nohup python reuseport.py &
    nohup python reuseport.py &
    nohup python reuseport.py &    
    
    # 使用nc测试,可以得到随机的一个进程响应
    echo "xiaorui" | nc localhost 1234
    
    # 使用socat(nc的增强版)测试,可以得到随机的一个进程响应
    echo "ss" | socat - tcp-connect:localhost:1234
    

    http://xiaorui.cc/2015/12/02/使用socket-so_reuseport提高服务端性能/
    https://www.jianshu.com/p/7e84a33b46e9

  • 相关阅读:
    alter table add column default
    JS小知识点
    java list按照元素对象的指定多个字段属性进行排序
    Web.xml配置详解之context-param
    spring在web.xml中的配置
    Activiti工作流(4):编写一个HelloWorld
    Activiti工作流(3):activiti核心API
    Activiti工作流框架学习(二)——使用Activiti提供的API完成流程操作
    Activiti工作流框架学习(一)——环境的搭建和数据表的了解
    Redis系列之(二):Redis主从同步,读写分离
  • 原文地址:https://www.cnblogs.com/shengulong/p/11633481.html
Copyright © 2020-2023  润新知