关于网络模型中的同步异步的思考

  最近写毕设的时候,写到了数据库部分,想要异步操作mysql,发现mysql并未提供对应异步接口,于是我开始思考是否有办法自己实现一个异步接口。

  想实现一个异步接口需要什么条件?

  (1)不应该在IO操作上阻塞

  (2)每条消息(查询)必须要有相应标识

  对于(1)不必所说,(2)的意思是每条消息发出去后,异步callback的时候你要知道这是哪条信息的回复。举个例子,你分别向服务器发送了两个查询,“小明的id是什么?”和“小红的id是什么?”,过了一会服务器给你回复了“1”,“2”。这时候小明的id一定是1吗?不一定,有可能服务器先接收到了第二个查询,然后回复,也可能是服务器先接收到了第一个查询,但是你收到回复却是先收到第二条。这时候异步是无法保证顺序的,因此需要对信息进行标识。

  如何对信息进行标识呢?方法很多,同步是最容易的一种。“小明的id是什么?”“1”“小红的id是什么?”“2”。这样一来一回绝对不会出错。但是我们现在是异步,那又要怎么做呢?最暴力的方法是使用短连接,对于每个连接,我就只发一条信息,利用连接作为消息的标识。意思就是,连接A发送了“小明的id是什么?”连接B发送了“小红的id是什么?”这样收到消息也不会乱。这样做缺点也很明显,短连接的缺点就是创建和销毁连接开销大。还有一种方法是用长连接的,长连接需要在消息上带上一个标识位,比如“消息id为1,查询是小明的id是什么?”“消息id为2,查询是小红的id是什么?”,收到的回复是“消息id为1,答复是1”“消息id为2,答复是2”。这样也不会乱了。但是这需要服务器提供对应的接口,需要能回复带上消息id。

  说了这么多,进入实例,如何在mysql上用上这个逻辑?我们操作mysql的时候,都是用官方给的接口,比如c++就有对应的mysql connector。如果这些接口不提供异步接口,比如说查询就一个query接口,你一定要阻塞在那里直到回复,那么很遗憾,上述两种方法你都用不上。十分悲惨的是,我在mysql connector上没有找到异步接口,所以我开始找其他方法。

  我想到可以利用协程去解决,但是协程只能解决短连接问题,而且从总体上看,这应该是一个同步非阻塞模型而非异步模型。

  实在不行,还可以自己封装一个mysql connector,只要知道与msql通信的具体协议就可以做了。但是这样同样无法支持长连接的带消息id的做法,因为服务器本身并不支持。对于mysql有个比较投机取巧的方法,就是每次查询带上一个“select msgId”,例如“select 1 and id where name = 小明”。但是我觉得这种方法可能不能做通用,比如不支持update等操作。

  最后无计可施,我开始思考是否真的需要异步。其实要访问mysql服务的进程和mysql是在同一台机器上的,这时候同步异步性能差异并不大,异步的优势在于能将等待时间利用流水线优化掉,同机器操作可以忽略等待时间。何况数据库查询一般都是最后的步骤了,阻塞非阻塞没什么意义,所以我可以直接使用阻塞模型。

  如果访问mysql服务的进程和mysql不在同个机器,异步是很有必要的。这时候我想到个更合理的做法,就是在mysql服务的机器上增加一个mysql代理,代理可以提供非阻塞接口,但是连接mysql服务是阻塞的。可以采用epoll接收请求,然后将请求交给消息队列,然后多工作线程去队列获取请求并阻塞请求mysql。这样做需要自己定协议,工作量也不小,但是相对于修改mysql的协议来说合理很多。

posted @ 2019-02-18 01:14  酱油党gsh  阅读(320)  评论(0编辑  收藏  举报