Triển Khai Máy Chủ Web Xử Lý Đồng Thời với Python

Mô Hình Phi Chặn Đơn Tiến Trình

#coding=utf-8
from socket import *
import time

danh_sach_ket_noi = []

def main():
    server_socket = socket(AF_INET, SOCK_STREAM)
    server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    server_socket.bind(('', 8080))
    server_socket.listen(128)
    server_socket.setblocking(False)

    while True:
        try:
            client_socket, client_addr = server_socket.accept()
            print(f"Kết nối mới: {client_addr}")
            client_socket.setblocking(False)
            danh_sach_ket_noi.append((client_socket, client_addr))
        except BlockingIOError:
            pass
        
        for sock, addr in danh_sach_ket_noi[:]:
            try:
                du_lieu = sock.recv(1024)
                if du_lieu:
                    print(f'Nhận từ {addr}: {du_lieu.decode()}')
                else:
                    sock.close()
                    danh_sach_ket_noi.remove((sock, addr))
            except BlockingIOError:
                continue

if __name__ == '__main__':
    main()

Máy Chủ Web Phi Chặn

import socket
import re

class WebServer:
    def __init__(self, port, thu_muc_goc):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_socket.bind(("", port))
        self.server_socket.listen(128)
        self.server_socket.setblocking(False)
        self.danh_sach_client = []
        self.thu_muc_goc = thu_muc_goc

    def xu_ly_yeu_cau(self, request, client_socket):
        if not request: return
        
        ten_file = re.search(r"GET\s+(.*?)\s+HTTP", request)
        ten_file = ten_file.group(1) if ten_file else "/index.html"
        
        try:
            with open(self.thu_muc_goc + ten_file, "rb") as f:
                noi_dung = f.read()
            header = (
                "HTTP/1.1 200 OK\r\n"
                f"Content-Length: {len(noi_dung)}\r\n\r\n"
            ).encode()
            client_socket.send(header + noi_dung)
        except FileNotFoundError:
            loi = "404 Không tìm thấy tài nguyên"
            header = (
                "HTTP/1.1 404 Not Found\r\n"
                f"Content-Length: {len(loi)}\r\n\r\n"
            ).encode()
            client_socket.send(header + loi.encode())

    def chay(self):
        while True:
            try:
                client_socket, _ = self.server_socket.accept()
                client_socket.setblocking(False)
                self.danh_sach_client.append(client_socket)
            except BlockingIOError:
                pass
            
            for sock in self.danh_sach_client[:]:
                try:
                    du_lieu = sock.recv(1024).decode()
                    if du_lieu:
                        self.xu_ly_yeu_cau(du_lieu, sock)
                    else:
                        sock.close()
                        self.danh_sach_client.remove(sock)
                except BlockingIOError:
                    continue

Mô Hình Epoll

import socket
import select

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("", 8888))
server.listen(10)
server.setblocking(False)

epoll_ctrl = select.epoll()
epoll_ctrl.register(server.fileno(), select.EPOLLIN)
ket_noi = {}

while True:
    su_kien = epoll_ctrl.poll()
    for fd, event in su_kien:
        if fd == server.fileno():
            client, addr = server.accept()
            client.setblocking(False)
            epoll_ctrl.register(client.fileno(), select.EPOLLIN)
            ket_noi[client.fileno()] = client
        elif event & select.EPOLLIN:
            du_lieu = ket_noi[fd].recv(1024)
            if du_lieu:
                print(f"Nhận: {du_lieu.decode()}")
            else:
                epoll_ctrl.unregister(fd)
                ket_noi[fd].close()
                del ket_noi[fd]

Máy Chủ Web Sử Dụng Epoll

import socket
import select
import re

class WebServerEpoll:
    def __init__(self, port, thu_muc_goc):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server.bind(("", port))
        self.server.listen(128)
        self.thu_muc_goc = thu_muc_goc
        self.epoll = select.epoll()
        self.epoll.register(self.server.fileno(), select.EPOLLIN)
        self.socket_map = {}

    def xu_ly_yeu_cau(self, request, client_socket):
        # Logic xử lý tương tự phiên bản WSGIServer
        pass

    def chay(self):
        while True:
            events = self.epoll.poll()
            for fd, event in events:
                if fd == self.server.fileno():
                    client, _ = self.server.accept()
                    self.epoll.register(client.fileno(), select.EPOLLIN)
                    self.socket_map[client.fileno()] = client
                elif event & select.EPOLLIN:
                    du_lieu = self.socket_map[fd].recv(1024)
                    if du_lieu:
                        self.xu_ly_yeu_cau(du_lieu.decode(), self.socket_map[fd])
                    else:
                        self.epoll.unregister(fd)
                        self.socket_map[fd].close()
                        del self.socket_map[fd]

Máy Chủ Web Sử Dụng Gevent

from gevent import monkey
import gevent
import socket
import re

monkey.patch_all()

class GeventWebServer:
    def __init__(self, port, thu_muc_goc):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server.bind(("", port))
        self.server.listen(128)
        self.thu_muc_goc = thu_muc_goc

    def xu_ly_client(self, client_socket):
        while True:
            du_lieu = client_socket.recv(1024).decode()
            if not du_lieu: break
            # Logic xử lý request tương tự
            client_socket.send(b"HTTP/1.1 200 OK\r\n\r\nHello")

    def chay(self):
        while True:
            client, _ = self.server.accept()
            gevent.spawn(self.xu_ly_client, client)

Vấn Đề C10K

Tham khảo tài liệu về giới hạn kết nối đồng thời:

  • http://www.52im.net/thread-561-1-1.html
  • http://www.52im.net/thread-566-1-1.html

Thẻ: non-blocking-sockets epoll gevent C10K-problem python-web-server

Đăng vào ngày 13 tháng 7 lúc 14:18