面试题答案
一键面试优化方面
- 缓冲区设置:
- 发送缓冲区:适当增大发送缓冲区(
SO_SNDBUF
),可以减少因缓冲区满而导致的等待时间,提高数据发送效率。例如在高并发下,大量数据需要快速发送,如果缓冲区过小,会频繁等待缓冲区有空间。 - 接收缓冲区:增大接收缓冲区(
SO_RCVBUF
),可以避免因接收缓冲区满而丢弃数据,同时减少接收端的处理压力。当数据快速到达时,较大的缓冲区可以暂时存储更多数据,等待接收端处理。
- 发送缓冲区:适当增大发送缓冲区(
- 复用端口:设置
SO_REUSEADDR
选项,允许套接字在关闭后立即重用端口。在高并发场景下,频繁地启动和关闭服务器程序时,若不设置此选项,可能会因为端口处于TIME - WAIT状态而无法立即重用,导致新的连接无法建立。 - 非阻塞I/O:将套接字设置为非阻塞模式,这样在等待I/O操作完成时,程序不会阻塞,而是可以继续执行其他任务。比如在等待客户端连接或数据接收时,服务器可以同时处理其他连接请求,提高了并发处理能力。
- 事件驱动模型:使用
select
、poll
或epoll
(在Linux系统下epoll
性能更优)等事件驱动机制,监听多个套接字的状态变化,只有当有事件发生(如可读、可写)时才进行相应处理,避免无效的轮询,提高效率。
示例代码
import socket
import selectors
def server():
# 创建一个默认的事件选择器
sel = selectors.DefaultSelector()
# 创建TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置套接字选项,允许重用地址
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 设置发送缓冲区大小
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 16384)
# 设置接收缓冲区大小
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 16384)
server_address = ('localhost', 8888)
server_socket.bind(server_address)
server_socket.listen(100)
# 设置为非阻塞模式
server_socket.setblocking(False)
# 注册服务器套接字,监听读事件
sel.register(server_socket, selectors.EVENT_READ, data=None)
print('Server is listening on {}:{}'.format(*server_address))
try:
while True:
# 等待事件发生,超时时间为1秒
events = sel.select(timeout=1)
for key, mask in events:
if key.data is None:
# 处理新的连接
accept_wrapper(sel, key.fileobj)
else:
# 处理已连接套接字的读事件
service_connection(sel, key, mask)
except KeyboardInterrupt:
print('Caught keyboard interrupt, exiting')
finally:
sel.close()
def accept_wrapper(sel, sock):
conn, addr = sock.accept()
print('Accepted connection from {}'.format(addr))
conn.setblocking(False)
data = types.SimpleNamespace(addr=addr, inb=b'', outb=b'')
# 注册已连接套接字,监听读和写事件
events = selectors.EVENT_READ | selectors.EVENT_WRITE
sel.register(conn, events, data=data)
def service_connection(sel, key, mask):
sock = key.fileobj
data = key.data
if mask & selectors.EVENT_READ:
recv_data = sock.recv(1024)
if recv_data:
data.outb += recv_data
else:
print('Closing connection to {}'.format(data.addr))
sel.unregister(sock)
sock.close()
if mask & selectors.EVENT_WRITE:
if data.outb:
sent = sock.send(data.outb)
data.outb = data.outb[sent:]
if __name__ == '__main__':
import types
server()
此代码结合了上述提到的一些优化点,如复用端口、非阻塞I/O和事件驱动模型(使用selectors
模块),模拟了一个简单的高并发服务器场景。