mina学习笔记

mina学习笔记

    由于前端时间写的一个项目广东省浮动车道路匹配相关的工作,在这个项目中的各个模块间的数据转发所用的底层框架是mina框架,这两天有时间,将mina框架学习过程整理一遍,希望能对初学者有所帮助。

mina是什么

    mina(Multipurpose Infrastructure for Network Applications)是一个能够帮助用户开发高性能和高伸缩性网络应用程序框架,它底层使用java nio技术基于TCP/IP协议提供抽象的、事件驱动的、异步的API接口。在高并发的应用中mina是一个不很不错的选择,mina目前是apache的开源项目。
   Mina是一个Java NIO框架,NIO的基本思想是:服务器程序只需要一个线程就能同时负责接收客户的连接、客户发送的数据,以及向各个客户发送响应数据。
   mina应用一般包含两个部分,客户端和服务器,客户端发送消息,服务端监听,当有客户端请求时,就处理数据否则一直监听。

mina架构

mina整体架构图如下:

mina学习笔记_第1张图片

   从图中可以看出,mina在系统中扮演的角色是,用户层和socket层的中间件的角色,mina的使用使得底层socket对开发人员是透明的,开发人员可以更专注于业务的需求上。mina服务用例过程如下:

mina学习笔记_第2张图片

  • IoServiece :这个接口在一个线程上负责套接字的建立,拥有自己的 Selector,监听是否有连接被建立。
  • IoHandler :这个接口负责编写业务逻辑,也就是接收、发送数据的地方。这也是实际开发过程中需要用户自己编写的部分代码。
  • IoFilter :过滤器用于悬接通讯层接口与业务层接口,这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤、数据的编码(write 方向)与解码(read 方向)等功能,其中数据的 encode与 decode是最为重要的、也是你在使用 Mina时最主要关注的地方。
  • IoProcessor :这个接口在另一个线程上负责检查是否有数据在通道上读写,也就是说它也拥有自己的 Selector,这是与我们使用 JAVA NIO 编码时的一个不同之处,通常在 JAVA NIO 编码中,我们都是使用一个 Selector,也就是不区分 IoService与 IoProcessor 两个功能接口。另外,IoProcessor 负责调用注册在 IoService 上的过滤器,并在过滤器链之后调用 IoHandler。

  • IoAccepter :相当于网络应用程序中的服务器端

  • IoConnector :相当于客户端
  • IoSession :当前客户端到服务器端的一个连接实例

mina客户端

   mina客户端首先建立一个IOconnector用来和和服务器端通信,这就相当于建立了一个连接对象,客户端向session中写入数据,经过filter chain过滤之后发送给服务端,当然从服务端反馈回来的数据,也会经过filter chain 的过滤,然后再到handler中进行处理,其处理过程如上如图中箭头向上的过程。建立Ioconnector的代码如下:

 final NioSocketConnector connector = new NioSocketConnector();
        ConnectFuture connection=null;
        connector.getFilterChain().addLast("logger", new LoggingFilter());
        connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
        connector.getSessionConfig().setIdleTime(IdleStatus.WRITER_IDLE,300);
        connector.setDefaultRemoteAddress(new InetSocketAddress("58.251.157.170",10024));

        connector.setHandler(new ClientHandler());
        ConnectFuture connect = connector.connect();
        connect.awaitUninterruptibly();

connection = connect;

 for(int i = 0; i<10;i++) {
  connection.getSession().write("hello" + i);
}

   上面部分代码包含服务端的Connoctor建立,和filter chain 的设置,通过连接实例获取session对象之后,就可以调用write方法向session中写入数据。

mina服务端

mina服务器端,mina设置监听端口,监听网络请求,一个新的请求对应一个Connection实例,mina会建立一个新的mina Connection,并在connection中建立一个session,在服务端的filter chain中定义一些转换、编码、解码等等的功能负责把底层传输的对象拼装为更高一层的对象方便后续的处理,最后传输的数据被交给IOHandler,在hangdler中获取数据做数据处理即可。

 IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));

        acceptor.setHandler(new ServerHandler() );
        acceptor.getSessionConfig().setReadBufferSize(2048);
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
        try {
            // 服务器开始监听
            acceptor.bind( new InetSocketAddress(PORT) );
        }catch(Exception e){
            e.printStackTrace();
        }
}

以上代码就是服务器端建立过程,,服务器端的简历需要先新建一个NioSocketAcceptor对象,然后设置相应的filter chain和监听端口。

mina应用场景

当程序需要高并发,并且后面后持续改进,随着业务的增长,会面临高并发访问的服务器端和客户端的时候就可以运用mina,当然也有替代方案,如netty。
需要注意的是:mina一般默认的设置并发性不是最优,因此在实际应用中需要手动的设置一些参数,使其发挥更好的性能。

实例学习

1、以下是我编写的一个mina demo Client.java文件,用于创建Client客户端的connector:

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

/**
 * Created by User on 2017/6/2.
 */
public class Client {
    public static void main(String[] args) {
        final NioSocketConnector connector = new NioSocketConnector();
        ConnectFuture connection=null;
        connector.getFilterChain().addLast("logger", new LoggingFilter());
        connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
        connector.getSessionConfig().setIdleTime(IdleStatus.WRITER_IDLE,300);
        connector.setDefaultRemoteAddress(new InetSocketAddress("58.251.157.170",10024));

        connector.setHandler(new ClientHandler());
        ConnectFuture connect = connector.connect();
        connect.awaitUninterruptibly();

        connection = connect;

        for(int i = 0; i<10;i++) {
            connection.getSession().write("hello" + i);
        }

    }

}

2、然后 然后编写一个handler处理业务逻辑,创建ClientHandler.java 实现IoHandler接口,并重写接口中的方法

/**
 * Created by User on 2017/6/2.
 */
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
public class ClientHandler implements IoHandler{
    public void sessionCreated(IoSession ioSession) throws Exception {
        System.out.println("incomming 客户端:  session created !");

    }

    public void sessionOpened(IoSession ioSession) throws Exception {
        System.out.println("incomming 客户端: " + ioSession.getRemoteAddress());
    }

    public void sessionClosed(IoSession ioSession) throws Exception {

    }

    public void sessionIdle(IoSession ioSession, IdleStatus idleStatus) throws Exception {

    }

    public void exceptionCaught(IoSession ioSession, Throwable throwable) throws Exception {

    }

    public void messageReceived(IoSession ioSession, Object o) throws Exception {
        System.out.println("服务器返回的数据:" + o.toString());
    }
    public void messageSent(IoSession ioSession, Object o) throws Exception {

    }
    public void inputClosed(IoSession ioSession) throws Exception {

    }
}

以上几个比较重要的函数,函数是回调的时候自动调用
sessionCreated session 创建时调用;
sessionOpened session 打开时调用
messageReceived session 接收从服务器端发送回来的数据时调用

3、接下来写的是服务器端,Server.java 服务器端的主要工作是创建一个NioSocketAcceptor,对象并设置监听的端new InetSocketAddress(PORT)注意这里的端口号好和服务器端的端口号一致;


import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

/**
 * Created by User on 2017/6/4.
 */
public class Server {
    private static final int PORT = 10024;
    /**
     *
     */
    public Server() {
        // TODO Auto-generated constructor stub
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // 服务器端的主要对象
        IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));

        acceptor.setHandler(new ServerHandler() );
        acceptor.getSessionConfig().setReadBufferSize(2048);
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
        try {
            // 服务器开始监听
            acceptor.bind( new InetSocketAddress(PORT) );
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

开始监听之后服务器端发送过来的数据可以在服务器端的handler中处理;

4、服务器端的Handler,创建ServerHandler 类,并实现IoHandlerAdapter接口,其内容如下:

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

import java.util.Date;

/**
 * Created by User on 2017/6/4.
 */
public class ServerHandler extends IoHandlerAdapter {
    @Override
    public void exceptionCaught(IoSession session, Throwable cause)
            throws Exception {
        cause.printStackTrace();
    }

    /*
     * 这个方法是目前这个类里最主要的,
     * 当接收到消息,只要不是quit,就把服务器当前的时间返回给客户端
     * 如果是quit,则关闭客户端连接*/
    @Override
    public void messageReceived(IoSession session, Object message)
            throws Exception {
        String str = message.toString();
        if (str.trim().equalsIgnoreCase("quit")) {
            session.close();
            return;
        }
        Date date = new Date();
        System.out.println("hello"+str+"   :"+session.getRemoteAddress()+date.toString());

        session.write("i am recived : =>"+str);

    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        // TODO Auto-generated method stub
        super.sessionClosed(session);
        System.out.println("客户端与服务端断开连接.....");
    }
}

在messageReceived 方法中有一个session对象和object对象,Object对象即为接收到的消息,并且可以调用session中的write方法向客户端回馈信息,表示已经收到文件,或者处理后的结果返回给客户端。
5、maven项目,要在项目中添加mina的相关依赖,在pom.xmlzh中添加如下依赖

<dependencies>
        <dependency>
            <groupId>org.apache.minagroupId>
            <artifactId>mina-coreartifactId>
            <version>2.0.4version>
        dependency>
dependencies>

只需要添加一个mina-core就行


对比学习netty

   Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。netty和mina出自同一个人netty和mina有很多相同之处,但是mina效率更快,netty性能更稳,并且netty的文档比较丰富,目前来说netty社区的活跃度也比mina社区活跃度高,netty发展速度比mina快。
两者之间的比较如下:

  • 线程模型也基本一致,采用了Reactors in threads模型,即Main Reactor + Sub Reactors的模式;
  • mina、netty在线程模型上并没有太大的差异性,主要的差异还是在任务调度的粒度的不同;
  • mina将内核和一些特性的联系过于紧密,使得用户在不需要这些特性的时候无法脱离,相比下性能会有所下降;netty解决了这个设计问题;
  • netty基本的架构和mina几乎完全一样,使用时候思想上差不多;但是有很多细节的改进(比如说mina的IoSession每次读写完要调用flip(),netty的channel则不用,并支持zero copy)。
  • netty比mina使用起来更简单,netty可以看作是mina的一次重构
  • 两者都是基于NIO,NIO和IO有什么区别,接下来的博客将会介绍他们的区别和联系

你可能感兴趣的:(分布式,框架,高性能,mina)