最近准备写一个Vert.x的系列专题,将自己的理解分享给大家,也想通过笔记整理编写加深自己的理解。
引子,一个餐厅的故事
假设我们经营一家餐厅,我们餐厅的活动抽象成:接客(accept), 客户下单(read), 上菜(write), 离店(disconnect)这4个步骤。那么这家餐厅这样运行:
1. 开门营业,当有客人到来, 进行接待;
2. 客人落座后,给客人菜单,让客户点单;
3. 客人点单完成后,将订单提交后厨,菜做好后,为客户上菜;
4. 客户结账离开;
假设餐厅只有一位服务员,而且服务员必须完成当前的工作后才能做其他的事情(阻塞调用),这时餐厅会出现以下问题:
1. 当服务员正在为客户点单时候,无法接待新的客户,客户不完成点单操作,服务员就只能一直在旁边等待;
2. 当服务上完菜回到门口接客时,无法响应客户的加菜(点单)请求,如果没有新客户到来,服务员只能一直在门口等待;
我们发现餐厅的接待(并发)能力非常低,几乎只能接待一个客户。这肯定不行,要想生意好,如何解决? 一服务员不行,多招一些服务员(多线程)能不能解决问题,假设我们这样做:
1. 安排1个服务员在店门, 负责接待新到的客户;
2. 为每一个进店的客户配备一个服务员, 响应客户的点餐,上菜,结账请求。
我们发现餐厅的接待能力上来了,看起来餐厅经营似乎有起色,可月底一结算竟然还亏损了,还不如一个服务员。那问题出在哪里?
1. 负责接待的员工太闲了,在大多来时候(没有新客户到来),什么都不干,在门口傻傻发呆;
2. 为每个客户配置一个服务员效率很低,在等待客户点菜,后厨正在做菜,客户吃饭的时候,客户其实并不需要什么服务,服务员只能傻等;
总结下来, 餐厅服务员太多了,工资成本太高(线程占用资源),服务员的工作效率很低,大部分时间都是在空闲等待。而且餐厅面积有限(服务器资源有限),大量的服务员还会相互干扰,出现混乱(大量线程切换),进一步影响了运行效率。
那如何优化才提高餐厅运营效率,扭亏为盈呢, 我们可以为餐厅加装一个设备(Selector), 并改变服务机制:
1. 当有新客户到达时候,设备给服务员发给通知,让服务员尽快进行接待,这样店门外就不需要一个专职的接待员;
2. 客户落座后,不需要为客户专门配置一个服务员,当客户需要服务(下单,上菜,结账)的时候,设备通知服务员, 这样一个服务员可以服务多个客户;
经过优化改造,需要的服务员数量大大的减少,服务员的工作更加饱满,餐厅各服务员工作有条不紊,经营改善,员工加薪,皆大欢喜。
我们看到优化的关键是用异步调用(非阻塞调用)替换掉了阻塞调用。服务员只需要把店门打开, 就可以做其它事情了, 不需要一直在门口等待客人到来, 当有客人到来了会通知服务员进行响应; 客户落座后, 服务员只要把菜单给客户, 就可以忙其它事情了, 当客户完成点单后, 会通知服务员进行响应; 在菜做好之前, 服务员不需要等待, 只要菜做好后通知服务员上菜即可; … 这就是异步编程和响应式系统(asynchronous programming, and reactive systems)的基本思想。
代码实例:应答服务器(Echo Server)
我们想使用传统的阻塞I/O实现。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Objects;
public class SyncEchoServer {
public static void main(String[] args) throws Throwable {
try (ServerSocket server = new ServerSocket()) {
server.bind(new InetSocketAddress(3000));
while (true) {
Socket socket = server.accept();
int clientPort = socket.getPort();
ClientHandler handler = new ClientHandler