与慢速设备通讯异步化方案
像MySQL、被对接的银行系统等,都可称作慢速设备。它们的共同特点是只提供了同步调用接口,而且响应通常会比较慢。
一般业务系统在业务线程或进程中,直接调用它们提供的API进行访问,如下图所示:
结果造成了以下两大问题:
1) 性能低:同步阻塞方式;
2) 可靠性差:业务系统和慢速设备强耦合。
性能低,是因为慢速设备不能快速响应返回结果;可靠性差,是因为业务系统和慢速设备没有解耦合,比如当慢速设备的API因异常被长时间挂起时,会导致业务系统的线程或进程也一同被挂起。
要解决性能低,就需要异步化,也就是业务系统可异步访问慢速设备;要解决可靠性差,就需要两者间解耦合。
下图所示的方案,针对这两个问题进行了优化,即实现了异步化,也做到了解耦合:
以MySQL为例,业务系统在向MySQL发起SQL操作时,不再直接调用MySQL的API,而是将SQL放入到队列中,然后立即返回。
针对每一个MySQL实例,都会有一个侍服线程,与它建立一对一对绑定关系,也就是这个线程专门为它服务的,如果有多个MySQL实例,则有相应个数的侍服线程,侍服线程不会跨MySQL实例。
侍服线程会实时监听队列,当有数据时,立即取出来,然后调用MySQL的API执行SQL操作,这个过程是同步的,侍服线程会阻塞,直到MySQL返回结果。
对于MySQL返回的结果,有两种业务系统的方式:一是由侍服线程回调业务,对结果的处理权在侍服线程;二是将结果存入结果队列,然后业务可以以epoll的方式取出结果,这种方式可以让对结果的处理权回归到业务线程。
1) 侍服线程回调业务实现方式
下面以伪代码方式,展现实现方式:
void mysql_thread()
{
while (true)
{
sql = _sql_queue.pop(); // 阻塞等待队列中有SQL,如果有,则取出SQL
result = mysql.query(sql); // 调用MySQL接口,进行SQL操作,结果存在result中
service->callback(result); // 回调业务接口,让业务对结果进行处理
}
}
2) 以epoll的方式取出结果
这种方式让对结果的处理权回归到业务线程。实现方式非常简单,只需要结果队列是可以epoll的即可,通常可以借助eventfd或pipe来包装一个队列,让这队列可以epoll监听。侍服线程只需要将结果存入结果队列,然后会自动唤醒处于等待状态的epoll。
异步化方案不能支持事务,关键原因是事务和MySQL连接有绑定关系,同一个连接不支持多个并发的事务。