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来开发各种服务。
首先我们看一下http的实现
Magician.createHttpServer().httpHandler("/", req -> {
req.getResponse()
.sendJson(200, "{'status':'ok'}");
}).bind(8080).start();
复制代码
如果不想把handler 跟这段代码窝在一起,可以单独建立handler,在这里添加进去即可
WebSocket实现
Magician.createHttpServer().bind(8080)
.httpHandler("/", new DemoHandler())
.webSocketHandler("/websocket", new DemoSocketHandler())
.start();
复制代码
只需要在创建http服务的时候,添加一个WebSocketHandler即可。
UDP实现
Magician.createUdpServer()
.handler(outputStream -> {
// outputStream 是ByteArrayOutputStream类型的
// 它是客户端发过来的数据,自行解析即可
}).bind(8088).start();
复制代码
同样的,也可以单独创建handler,在这里添加进去
了解更多
想了解更多的话,欢迎访问Magician官网:magician-io.com