【问题标题】:Tornado websockets with mysql and redisTornado websockets 与 mysql 和 redis
【发布时间】:2018-04-26 08:40:41
【问题描述】:

我有一个 tornado 服务器,它将与客户端创建 web-socket 连接。每当客户端请求某些数据时,我都需要从 Redis 或 MySQL-DB 获取它。除此之外,我还需要收听来自龙卷风服务器的广播,接收网络数据包并将它们发送给订阅数据的客户端。向客户端发送广播数据包取决于数据包中的令牌。如果客户端订阅了令牌,我们应该将数据包发送给他。

请求率:

  1. 5000 个活动 Web 套接字连接(可以增加)
  2. 每秒每个套接字连接的 1-DB 请求,因此总计 5000 个 DB-requests/秒
  3. 每秒每个套接字连接 1 个 Redis 请求,因此总共 5000 个 Redis 请求/秒。
  4. 在广播中,我应该每秒收听 1000 个数据包并检查是否有任何用户订阅了令牌。

我知道 redis 是单线程的并且异步工作(如果我错了,请纠正我)。对于我使用的 MySQL-DB 异步驱动程序是 `tormysql`(我对 DB 的大部分调用都是选择查询,没有复杂的 DB 操作。)。

我的问题:

  1. 对 MySQL-DB 的调用会阻塞吗?如果他们阻止调用,我可以为数据库查询运行不同的线程/进程吗?
  2. 龙卷风是否有可能在广播中丢弃数据包?
  3. 我可以在我的资源前面安装一个负载平衡器并为它们提供服务器,但我是否可以使用具有 2 核 8 GB RAM 的单个 CPU?

更新 1
我为 MySQL 编写了一个代码:
Server.py

import logging
import tornado.escape
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket
import os.path
import uuid
import sys
from tornado import gen

from tornado.options import define, options

import tormysql ## MySQL async driver
pool = tormysql.ConnectionPool(
    max_connections = 20, #max open connections
    idle_seconds = 7200, #conntion idle timeout time, 0 is not timeout
    wait_connection_timeout = 3, #wait connection timeout
    host = "127.0.0.1",
    user = "test",
    passwd = "",
    db = "testdb",
    charset = "utf8"
)


define("port", default=8000, help="run on the given port", type=int)

class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r"/", MainHandler),
            (r"/dataSock", ChatSocketHandler),
        ]
        settings = dict(
            cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
            static_path=os.path.join(os.path.dirname(__file__), "static"),
            xsrf_cookies=True,
        )
        super(Application, self).__init__(handlers, **settings)

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("indexWS.html")

class ChatSocketHandler(tornado.websocket.WebSocketHandler):
    openConnCount = 0
    def get_compression_options(self):
        # Non-None enables compression with default options.
        return {}

    def open(self):
        # print("Socket open:%s"%(self))
        ChatSocketHandler.openConnCount += 1
        None
    def on_close(self):
        # print("Socket closed:%s"%(self))
        ChatSocketHandler.openConnCount -= 1
        None

    async def on_message(self, message):
        logging.info("open conn count %r", ChatSocketHandler.openConnCount)
        returnDB = await getDataFromDB()
        self.write_message("server got this from you:%s"%(str(returnDB)))

@gen.coroutine
def getDataFromDB():
    with (yield pool.Connection()) as conn:
        try:
            with conn.cursor() as cursor:
                yield cursor.execute("SHOW TABLES;")
                # print(cursor.fetchall())
        except:
            yield conn.rollback()
        else:
            yield conn.commit()

        with conn.cursor() as cursor:
            yield cursor.execute("SELECT * FROM theme;")
            datas = cursor.fetchall()

    return datas

    # yield pool.close()
def main():
    tornado.options.parse_command_line()
    app = Application()
    # print "options:", options
    # sys.exit()
    app.listen(options.port)
    print("PORT:%s"%(options.port))
    tornado.ioloop.IOLoop.current().start()

if __name__ == "__main__":
    main()

现在,当我使用以下代码测试此代码时:
Client.py

import asyncio
import websockets

async def hello(i):
    async with websockets.connect('ws://localhost:8000/dataSock') as websocket:
        name = 'A'#input("What's your name? ")
        print("******************************%s******************************"%(i))
        for j in range(100):
            await websocket.send(name)
            # print("> {}".format(name))

            greeting = await websocket.recv()
            print("{}: {}".format(i, len(greeting)))
            asyncio.sleep(10)

async def start():
    for i in range(10):
        await hello(i)
    print("end")
    asyncio.sleep(20)
asyncio.get_event_loop().run_until_complete(start())
# asyncio.get_event_loop().run_forever()

如果我运行代码的单个实例,一切都运行良好。当我将客户端数量增加到 70(客户端的 70 个实例)时,有一个 我得到的响应延迟。

第二题解释:
Tornado 服务器必须在某个端口上侦听,在该端口上我将接收网络数据包,如果他们订阅了我必须将其发送给客户端。那么这些数据包是否有可能被丢弃?

【问题讨论】:

    标签: mysql asynchronous redis tornado


    【解决方案1】:
    1. 对 MySQL-DB 的调用会阻塞吗?如果他们阻止调用,我可以为数据库查询运行不同的线程/进程吗?

    正如您提到的,您使用tormysql 作为驱动程序,所以不,调用不会阻塞,因为tormysql 是异步的。

    1. 龙卷风是否有可能在广播中丢弃数据包?

    我不太明白你的意思。但是由于 websocket 协议是建立在 TCP 之上的,所以所有数据包的传递都是有保证的。 TCP 会处理这个问题。

    1. 我可以在我的资源前面安装一个负载平衡器并为它们提供服务器,但我是否可以使用具有 2 核 8 GB RAM 的单个 CPU?

    是的。


    我认为你现在想太多了。过早的优化是邪恶的。您还没有编写任何代码,并且正在考虑性能。这只是在浪费你的时间。

    先编写代码,然后对其进行压力测试,看看您的应用程序可以处理多少负载。然后进行分析,看看事情在哪里变慢了。 然后你优化代码,或者改变你的设置,也许升级你的硬件。

    但是只考虑性能而不编写和压力测试代码只是浪费时间。

    【讨论】:

    • 感谢您的回复。我已经更新了我的代码,我仍在测试它。对于第二个问题:tornado 服务器必须侦听接收数据的端口。该数据必须发送到通过套接字连接到 torando 的客户端。 web-socket 连接与它必须监听的 tornado 服务器的传入数据完全不同。
    • 感谢您对开发的建议。这真的很有帮助。我将开始开发服务器并测试代码。
    • @aja 如果这些客户端通过 TCP 套接字 (SOCK_STREAM) 连接,那么您不必担心数据包传递。
    猜你喜欢
    • 2014-05-17
    • 1970-01-01
    • 2015-09-22
    • 2015-10-12
    • 1970-01-01
    • 2013-02-15
    • 1970-01-01
    • 1970-01-01
    • 2011-04-22
    相关资源
    最近更新 更多