现在公司使用的NIO框架一直时候Mina,当然这也的框架还有Netty。虽然一直在用,但只是简单的停留在业务层面,最近面试的时候有问Mina相关的东西。在之前的博客中已经对BIO,NIO,AIO这三种java提供的IO流进行了介绍以及原理的讲解。今天我就打算开始对实现NIO的Mina框架的学习,看看这个框架是如何帮助我们更好的去管理selector,以及ByteBuffer,和我们的channel。以及如何处理高并发。好了今天就对mina框架的几个重要的概念用一个Mina框架入门代码的方式给大家看一下。:
我们先看一下mina服务端的代码:
package cn.nio.mina; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; /** * * <p> * mina服务端 * </p> * * 类说明 * * @author duanxj * @version */ public class MinaServer { public static void main(String[] args) { MinaServer server = new MinaServer(); server.initMinaServer(19898); } /** * 初始化Mina服务端 * * @param port */ public void initMinaServer(int port) { // 创建NioSocketAcceptor对象 NioSocketAcceptor acceptor = new NioSocketAcceptor(); // 创建请求处理类 MinaServerHandler handler = new MinaServerHandler(); try { // 设置过滤链 acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory())); // 设置处理类 acceptor.setHandler(handler); // 绑定服务端口 acceptor.bind(new InetSocketAddress(port)); } catch (IOException e) { e.printStackTrace(); } } } mina服务端请求处理 package cn.nio.mina; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; /** * * <p>mina</p> * * 类说明 * * @author duanxj * @version */ public class MinaServerHandler extends IoHandlerAdapter{ @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { System.out.println("exceptionCaught"); } @Override public void messageReceived(IoSession session, Object message) throws Exception { String receiverContent = (String)message; System.out.println("server messageReceived: "+receiverContent); session.write("Server has received message:duanxiaojun"); } @Override public void messageSent(IoSession session, Object message) throws Exception { System.out.println("messageSent"); } @Override public void sessionClosed(IoSession session) throws Exception { System.out.println("sessionClosed"); } @Override public void sessionCreated(IoSession session) throws Exception { System.out.println("sessionCreated"); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { System.out.println("sessionIdle"); } @Override public void sessionOpened(IoSession session) throws Exception { System.out.println("sessionOpened"); } }
我们先对服务端的创建做一个简单的介绍。从mina服务端的创建代码可以看出,我们再创建一个mina服务的时候经常使用到的就是这几个对象。首先NioSocketAcceptor 创建用来接收客户端发送来的请求(主要负责实现nio操作相关的实现如accept(),open(),select()等方法)。MinaServerHandler 实现了IoHandlerAdapter用来对接收到的请求进行处理。getFilterChain 设置mina过滤链 对从IOServic饿到IOHandle之前的请求进行过滤处理诸如编解码。最后bind绑定一个特定的端口。好了这样一个简单的mina服务就搭建起来了。但是其中很多知识点我们再接下来的源码解读中进行学习说明,这里只是有个入门,让大家对mina中最主要的几个知识点或者概念有个理解。
下面我们看mina客户端如何来重建用来向服务端发送请求和接收服务端请求处理的结果。先上代码一看:
package cn.nio.mina; import java.net.InetSocketAddress; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.transport.socket.nio.NioSocketConnector; /** * * <p> * mina客户端 * </p> * * 类说明 * * @author duanxj * @version */ public class MinaClient { public static void main(String[] args) { MinaClient client = new MinaClient(); client.initMinaClient("127.0.0.1", 19898); } public void initMinaClient(String host, int port) { // 创建NioSocketConnector对象 NioSocketConnector connector = new NioSocketConnector(); // 创建mina客户端处理 MinaClientHandler handler = new MinaClientHandler(); //设置过滤链 connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory())); //设置处理类 connector.setHandler(handler); // 创建连接 ConnectFuture future = connector.connect(new InetSocketAddress(host, port)); future.awaitUninterruptibly();// 阻塞直到连接建立,因为我们后面要使用连接成功之后创建的Session对象来进行写数据的操作 IoSession session = future.getSession();// 获得Session对象 session.write("hello world"); } } mina客户端业务处理 package cn.nio.mina; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; /** * * <p> * mina客户端请求处理 * </p> * * 类说明 * * @author duanxj * @version */ public class MinaClientHandler extends IoHandlerAdapter{ @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { System.out.println("exceptionCaught"); } @Override public void messageReceived(IoSession session, Object message) throws Exception { String receiverContent = (String)message; System.out.println(receiverContent); } @Override public void messageSent(IoSession session, Object message) throws Exception { System.out.println("messageSent"); } @Override public void sessionClosed(IoSession session) throws Exception { System.out.println("sessionClosed"); } @Override public void sessionCreated(IoSession session) throws Exception { System.out.println("sessionCreated"); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { System.out.println("sessionIdle"); } @Override public void sessionOpened(IoSession session) throws Exception { System.out.println("sessionOpened"); } }
跟服务端一样,我们先说一下客户端创建的几个主要的概念:NioSocketConnector 用来创建与服务端的连接同时向服务端发送数据。MinaClientHandler处理服务端返回的请求。getFilterChain处理从IOService到IOHandler之间的传输信息的过滤。
好了上面两个简单的mina服务端和mina客户端就创建完毕了。其实如果我们只是使用的话了解到上面这样就可以了,因为很多有关NIO就是同步非堵塞的处理mina框架在NioSocketAcceptor和MinaServerHandler之中已经进行了处理包括对大并发访问的处理,当然NIO天生就是用来处理大并发的,否则就不会有抽象的管道的概念了。因此如果只是想用那么学到这里我觉着就差不多了,保证你会使用了,下面就是我现在所在的公司使用mina进行通信协议的配置文件,大家可以看考一下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- ProtocolCodecFilter --> <bean id="digitalcourtProtocolCodecFilter" class="org.apache.mina.filter.codec.ProtocolCodecFilter"> <constructor-arg> <bean class="cn.com.chnsys.imp.courtInterface.server.CustomProtocalCodecFactory"> <constructor-arg type="java.lang.String" value="UTF-8" /> </bean> </constructor-arg> </bean> <!-- IO过滤线程池 --> <bean id="executorIoFilter" class="org.apache.mina.filter.executor.ExecutorFilter"> <constructor-arg index="0"> <value>1000</value> </constructor-arg> <constructor-arg index="1"> <value>1800</value> </constructor-arg> </bean> <!-- Mina过滤器链 --> <bean id="digitalcourtIoFilterChainBuilder" class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder"> <property name="filters"> <map> <entry key="executor" value-ref="executorIoFilter" /> <entry key="codecFilter" value-ref="digitalcourtProtocolCodecFilter" /> </map> </property> </bean> <!-- 协议IO线程池 --> <bean id="digitalcourThreadPool" class="cn.com.chnsys.cif.common.utils.ThreadPoolFactory"> <property name="corePoolSize" value="4" /> <property name="keepAliveSeconds" value="60" /> <property name="maxPoolSize" value="32" /> <property name="queueCapacity" value="64" /> </bean> <!-- mina协议接收服务 --> <bean id="MinaSocketServer" class="cn.com.chnsys.imp.courtInterface.server.DigitalCourtMinaSocketServer"> <property name="ioHandler"> <bean class="cn.com.chnsys.imp.courtInterface.server.ProtocolDispatcherHandle"> <property name="handleMap"> <map> <entry key="login" value-ref="getUserAndLoginProtocol" /> <entry key="getUsers" value-ref="getUserAndLoginProtocol" /> </map> </property> <property name="readTimeout" value="30" /> <property name="charset" value="UTF-8" /> <property name="customProtocolInterceptors"> <list> <!-- 上下文环境处理拦截器 --> <bean id="serverContextHandlerIntercptor" class="cn.com.chnsys.imp.courtInterface.protocol.ServerContextHandlerIntercptor"> <property name="userService" ref="userService" /> <property name="ignoreModuleList"> <list> <value>getSignalSourceUrl</value> <value>getTDSSignalSourceUrl</value> <value>getTrialPlansNoPrivilege</value> </list> </property> <property name="ignoreValidateUserList"> <list> <value>heartBeatTDS</value> <value>checkCMSConnectable</value> </list> </property> </bean> </list> </property> </bean> </property> <property name="threadPool" ref="digitalcourThreadPool" /> <property name="port" value="${port}" /> <property name="ioFilterChainBuilder" ref="digitalcourtIoFilterChainBuilder" /> </bean> </beans>
从这个配置文件我们可以看到,我们只是实现了对编解码拦截的处理,这是因为我们知道原生态的mina框架是不会处理传输信息的加密的,而在我们的应用中使用到了协议信息的加密技术,所以需要对编解码拦截器进行重写,在这里我主要实现的是对ByteBuffer的解码和编码。详细编解码大家也可以参考一下:
/**
* 重写方法描述 {@inheritDoc}
*/
@Override
public void decode(IoSession ioSession, IoBuffer buffer, ProtocolDecoderOutput out)
throws Exception {
Integer protocolLength = (Integer) ioSession.getAttribute(ProtocolConstant.ATTR_PROTOCLLENGTH);
//具体实现的内容就是对IOBuffer中的字节数据进行编解码。这里我就不能跟大家再说了。
}
好了通过上述的一个mina在企业级的应用大家应该可以看到,其实我们真正在使用的时候,其实并不需要对mina的底层如何实现NIO做更多的了解。
但是,小伙伴们 但是。。。。如果你对技术很热情,很感兴趣我觉着就很有必要去了解了解这个mina框架了,因为人家毕竟处理了一个很厉害的问题啊。哈哈哈 好了今天对mina框架的入门咱们就说到这里,从下一讲开始对mina中的这几个牛逼的概念或者知识点进行源码级别的跟踪学习。咱们下次见。。。