Magician 是一个异步非阻塞的网络编程包,用 java 语言实现,支持 Http, WebSocket, UDP 等协议
jdk11+
Magician 的底层使用的是 NIO,但是没有用 Selector,因为 Selector 的设计初衷就是为了用单线程来处理高并发,从而减少 因为连接太多而造成线程太多,占用过多的服务器资源。 但是这样做的坏处也很明显,就是无法充分利用 cpu 的多核性能,还有一个就是 如果业务比较耗时,会造成整个循环被堵住。
所以,考虑到这一点,Magician 决定使用 accept,代码如下:
while (true){
SocketChannel channel = null;
try {
/* 这个方法会阻塞,直到有新连接进来 */
channel = serverSocketChannel.accept();
channel.configureBlocking(false);
/* 将任务添加到队列里执行 */
ParsingThreadManager.addTaskToParsingThread(channel);
} catch (Exception e){
logger.error("处理请求出现异常", e);
ChannelUtil.close(channel);
}
}
有一个 while 不断的监听 accept,当没有新请求进来的时候 accept 是阻塞的,所以不会有空轮询的问题,当有了新的请求进来,就会把 channel 丢到队列里面去,然后继续监听 accept 。
那么这个队列是什么结构的?他又是如何来执行的呢?
首先,队列的结构是这样的:他是一个 LinkedBlockingDeque,有序 且 长度无限(除非内存爆了),然后这个队列 放在了一个线程中, 线程开启后就会有一个 while 不断的从这个队列里 take 元素,如果队列为空,take 就会阻塞,队列一旦有数据 take 就会按顺序返回里面的元素。
所以,只要这个线程在运行,我们就可以不断的往队列里丢任务,让这个线程来慢慢消化。
如果这样的线程+队列有多个, 我们把收到的请求 通过轮询算法 分配到这些队列,让线程各自消化,是不是就可以实现一个,线程数量可控,同时又具有异步特性的模型?
这个模型就是 Magician 现在所使用的,如下图所示:
在使用 Magician 的时候,可以自己配置 需要几个线程来同时运行。
除了 http,udp 也是采用的这个模型。只不过 udp 是以同步的模式读数据,数据读完了 再丢到队列里 让队列去执行业务逻辑。
说了这么多原理,我们接下来说说 如何使用 Magician 来开发各种服务。
Magician.createHttpServer().httpHandler("/", req -> {
req.getResponse()
.sendJson(200, "{'status':'ok'}");
}).bind(8080).start();
如果不想把 handler 跟这段代码窝在一起,可以单独建立 handler,在这里添加进去即可
Magician.createHttpServer().bind(8080)
.httpHandler("/", new DemoHandler())
.webSocketHandler("/websocket", new DemoSocketHandler())
.start();
只需要在创建 http 服务的时候,添加一个 WebSocketHandler 即可。
Magician.createUdpServer()
.handler(outputStream -> {
// outputStream 是 ByteArrayOutputStream 类型的
// 它是客户端发过来的数据,自行解析即可
}).bind(8088).start();
同样的,也可以单独创建 handler,在这里添加进去
想了解更多的话,欢迎访问 Magician 官网:http://magician-io.com
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.