最近,项目中需要实现三端通信交互,APP与服务端,PC端数据交互,并且需要实时监测,可能大家很快想到IM即时通讯,没错,刚开始我以为要集成友盟等第三方框架 ,但是后来要自己写,我瞬间懵了,这之前没用第三方框架还真没写过啊,没办法,事情总得解决啊,只好硬着头皮上,与服务端讨论选用何种框架,当时只熟悉websocket实现长连接,但是这个需要事件驱动连接,并且不能阻塞消息管道,最后结果就是选择netty。接下来说说这个netty。
什么是netty呢?官方定义“Netty 是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端”,那么现在我们先来看看这个netty到底是个什么,早期的我们用套接字来实现与服务端进行交互,例如,socket,这种依靠IO模型来处理数据,那么这种有什么弊端呢?只能同时处理一个连接,要管理多个并发客户端,需要为每个新的客户端 Socket 创建一个新的 Thread,其实在IO模型中有BIO何NIO之分,BIO即Blocking IO 阻塞型,这种在处理消息中会因为资源问题产生阻塞,1. 在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费2. 需要为每个线程的调用栈都分配内存3. 即使 Java 虚拟机(JVM) 在物理上可以支持非常大数量的线程, 但是远在到达该极限之前, 上下文切换所带来的开销就会带来麻烦而NIO non IO模型不会造成阻塞情况,但是NIO要做到安全正确的处理IO并容易,在高负载很容易出错,那么这个时候netty就登场了。
netty就是封装了NIO的api,采用多路复用来处理高并发的连接问题,其实本质还是采用NIO模型,但是内部内存池重用,内存的拷贝,接受数据和发送数据都做了完美的优化和封装。在性能上要高于前面的框架,附上netty官网地址GitHub - netty/netty: Netty project - an event-driven asynchronous network application framework
接下来就看看,怎么在项目中使用;

这里采用jar包来导进项目的,因为之前想采用依赖的,但是由于版本问题,依赖过于繁琐最后选择采用jar包模式。导进项目,那么我们开始做什么呢?

首先看这个图,我们知道首先需要去连接服务端,连接之后需要不断的去告诉服务端,当前客户端是在线的,服务端回复客户端知道客户端在线,可以进行消息传递了。连接我们肯定需要启用一个service来连接;

这是在service中连接的方法,需要解释下,
1、这是通过一个时间轮询的队列;
2、获取客户端的引导,来连接客户端
4,5,6是服务端对客户端连接引导,我们从字面意思可以理解,日志打印,消息管道,客户端管道;
7、设置握手
后面分别是获取管道,设置远程地址等,连接的监听,这个就不深入去解释了,我们主要放在如何收取到消息。
刚刚前面说了,如果连接上了之后需要不管的告诉服务端,当前客户端在线的,那么怎么去告诉呢?在socket中我知道在tcp协议中有个心跳包,就是通过心跳包来操作的,每隔几分钟发送心跳包来实现长连,so,我这里也模拟了一个心跳包来实现长连业务;

这里我用了一个线程池,每隔一分钟去执行一次发送ping命令消息,服务端接受到我的消息,证明当前状态是连接状态;

可以看到在连接失败的时候,我尝试重新去连接,这个重连机制很有必要的,因为服务端在网络状态不好的时候会与客户端断开连接,如果不尝试重连的话,会造成消息管道关闭,无法接收和发送数据;

到这里,service的大致功能完成了,那么我们需要去注册service,在哪里去注册呢?因为现在要求启动应用就需要连接并且发送业务心跳,那么从这里我们就要知道,必须在application中去注册service了,注册成功去发送业务心跳,整个思路还是很简单的。接下来看看发送心跳和发送消息部分;

绑定了service之后,我们就可以调用service来执行相关的业务动作了,现在需要发送消息,门额只需要将消息通过service的send方法便可以发送出去,这个很简单,

这里我试讲消息包裹在一个类中,然后转成json字符串的方式发送出去的,到这里,服务端就会收到我客户端发送的消息,对客户端的消息做出回应,那么,客户端是如何去接受到服务端的消息的呢?这个才是整个通讯的难点;我定义了一个handler,这个handler并不是我们常用的消息机制的handler

这个handler继承了 SimpleChannelInboundHandler<Object>,那么这个是做什么的呢?其实这个就是服务端与客户端消息通信的管理器,在netty中很多都是用这个来做消息处理器的,消息的读取和异常的捕获都是通过这个处理器来处理的。


可以看到,我们客户端读取服务端的消息就是来自这个chanelread0这个回调方法来实现的;上述代码掺杂了业务的IMCode,所以篇幅比较长,但是思路都是这样,在这里我们根据服务端返回的IMcode来处理相应的逻辑;但是在这个过程中,我们会遇到服务端服务器关闭等异常状况,我们该怎么去处理呢?netty是否有相应的回调呢?别担心,这个netty早就为你做了准备;

在exceptioncaught方法中,可以捕获相应的异常,在这里我捕获异常会去做相应的重新连接机制,所以我在这里没有做过多的其他业务操作;
综上就是一次简单的IM通信,但是有很多的坑,这个涉及了线程操作,因为很多都是耗时操作,需要合理的分配资源,这之间我还遇到过在service连接过程中,由于服务器地址一直没返回回来,导致出现程序无响应,这个很容易产生的,其次,重连这一块还有点不稳定,掉线后有时候会连不上导致无法接收和发送消息,优化的地方有很多,但是如果一个人去优化的话可能耗费很长的时间,毕竟现在都是用第三方IM框架,所以这次当做一个对IM机制的了解和对netty的初识。
后续观察logcat来判断连接是否稳定,可以将log打成文件形式

网友评论