如何在python中最好地实现服务器和客户端?
Posted
技术标签:
【中文标题】如何在python中最好地实现服务器和客户端?【英文标题】:How to best implement server and client in python? 【发布时间】:2019-04-11 15:16:21 【问题描述】:我有一个大文件需要加载到内存中,然后可以根据用户输入进行一些操作。但我不想在有用户输入时一次又一次地将文件加载到内存中。
一种解决方案可能是由一个作为“服务器”的进程加载数据文件,并让另一个客户端进程代表客户端查询服务器。
我想知道最好的客户端-服务器实现是什么。我知道我可以实现一个 HTTP 服务器,但是查询它需要遵循 HTTP 协议,这有太多的开销(对于我来说,客户端只需要向服务器发送一个字符串,所以不需要所有的 HTTP 标头。 ) 较轻的解决方案是首选。另外,客户端和服务器都应该在同一台机器上运行,所以使用内存比使用网络在客户端和服务器之间共享信息要快吗?
实际上,服务器可以将数据作为一些python对象加载到内存中,如果有办法从客户端访问这些python对象,应该也可以。
有人可以就解决此问题的最佳解决方案提供一些建议吗?谢谢。
【问题讨论】:
文件是什么数据? 没关系。因为我寻找的解决方案应该独立于数据。例如,如果数据只是一个表,我可以只使用 sqlite3。这不是我想要的。 嗯,它确实有点重要,所以我们可以尝试找出一个尽可能适合您的用例的解决方案。例如,您需要如何访问客户端中的数据?一个二进制数组的切片?一次全部?等等。 使用mmap
怎么样? docs.python.org/2/library/mmap.html
字符串不会一次全部发送。每次只发送一个字符串。返回的结果是字典或列表,可以很大。所以我不希望服务器计算一次结果并将其发回,因为这会导致额外的副本。我认为直接从客户端访问服务器中的对象可能会更好。
【参考方案1】:
好的,那么基于cmets,数据以字符串为键,值是列表或字典,客户端通过字符串请求对象。
不幸的是,没有安全、健全的方法可以直接跨进程访问此类数据,而无需一些中间序列化/反序列化步骤。一个明显的选择,除了安全问题,是pickle
ing 他们。 msgpack
也有道理。
对于协议,如果久经考验的 HTTP 对您来说太慢了,对于像这样的简单请求-响应周期,也许只需让客户端发送要检索的密钥,后跟空字符或换行符或者什么的,服务器直接回复序列化的对象,然后关闭连接。
您可能还想考虑将序列化数据简单地存储在数据库中,无论是 SQLite 还是其他数据库。
编辑:我决定尝试一下。这是一个小而幼稚的 asyncio
+ msgpack
基于服务器 + 客户端的解决方案:
server.py
import asyncio
import random
import msgpack
import time
from functools import lru_cache
def generate_dict(depth=6, min_keys=1, max_keys=10):
d =
for x in range(random.randint(min_keys, max_keys)):
d[x] = (
generate_dict(
depth=depth - 1, min_keys=min_keys, max_keys=max_keys
)
if depth
else "foo" * (x + 1)
)
return d
DATA = f"x": generate_dict() for x in range(10)
@lru_cache(maxsize=64)
def get_encoded_data(key):
# TODO: this does not clear the cache upon DATA being mutated
return msgpack.packb(DATA.get(key))
async def handle_message(reader, writer):
t0 = time.time()
data = await reader.read(256)
key = data.decode()
addr = writer.get_extra_info("peername")
print(f"Sending key key!r to addr!r...", end="")
value = get_encoded_data(key)
print(f"len(value) bytes...", end="")
writer.write(value)
await writer.drain()
writer.close()
t1 = time.time()
print(f"t1 - t0 seconds.")
async def main():
server = await asyncio.start_server(handle_message, "127.0.0.1", 8888)
addr = server.sockets[0].getsockname()
print(f"Serving on addr")
async with server:
await server.serve_forever()
asyncio.run(main())
client.py
import socket
import msgpack
import time
def get_key(key):
t0 = time.time()
s = socket.socket()
s.connect(("127.0.0.1", 8888))
s.sendall(str(key).encode())
buf = []
while True:
chunk = s.recv(65535)
if not chunk:
break
buf.append(chunk)
val = msgpack.unpackb(b"".join(buf))
t1 = time.time()
print(key, (t1 - t0))
return val
t0 = time.time()
n = 0
for i in range(10):
for x in range(10):
assert get_key(x)
n += 1
t1 = time.time()
print("total", (t1 - t0), "/", n, ":", (t1 - t0) / n)
在我的 Mac 上,
接收端每条消息大约需要 0.02814 秒,对于每秒 35 个请求的单消费者吞吐量。 服务端每条消息大约需要 0.00241 秒,吞吐量为每秒 413 个请求。(从DATA
的生成方式可以看出,有效载荷可能非常大。)
希望这会有所帮助。
【讨论】:
SQLite 绝对不是解决方案,因为它涉及到磁盘。根据磁盘的不同,性能可能会很慢。一切都必须在记忆中。数据上会有计算,进行计算并发送结果(很小)效率更高。如果直接获取数据然后在客户端进行计算,客户端获取的数据会很大。所以任何数据库解决方案都不适合我的问题。 连接方式也是一个问题。我认为在 C 语言中有一种方法可以使一段数据持久保存在内存中。 (ipcs
和ipcrm
可以显示共享内存的信息并删除一块共享内存)。有没有办法让 python 对象在内存中持久化?
您可以使用 ctypes 调用mlockall
,但我认为您在这里过早地优化了一些东西。你有没有尝试过 SQLite?它的性能可能令人惊讶。
@user1424739 请查看我的编辑 - 我添加了一个示例服务器/客户端。以上是关于如何在python中最好地实现服务器和客户端?的主要内容,如果未能解决你的问题,请参考以下文章
在不处理的情况下在线获取信用卡信息——如何最好地做到这一点?