Building simple and high-performance NiO applications based on Mina

Source: Internet
Author: User
Tags epoll

Mina is a Java server with a very good C/S architecture. Here I turn to an article about how it is used.

Preface
Mina is the latest Java communication framework developed by trustin Lee. The main function of the communication framework is to encapsulate underlying Io operations and provide advanced operation APIs. Well-known communication frameworks include C ++ ace and Python twisted, while Java communication frameworks include quickserver, netty2, Cindy, and grizzly.

In June 2004, trustin Lee released a communication framework, netty2, which is the first communication framework of the event model architecture in Java. Cindy also draws on many ideas. Due to the poor architecture of netty2, trustin Lee, after joining the Apache directory group by the end of 2004, rewritten the entire framework named Mina. Mina is a communication framework based on Java NiO. Java introduced NiO from 1.4, providing a non-blocking and high-performance I/O underlying layer.

Currently, there are not many Mina products, and Apache directory and openfire (an XMPP product developed by jive) are well-known) red5 (friends studying flash streaming media FLV technology should be very aware of this, competitors of Adobe FMS, and video websites in China.

At the beginning of, the company's new project needed to implement a socket server using Java, and compared netty2, Cindy, quickserver, and Mina. At that time, netty2 had stopped development and could not find the official website and code. After comparing the other three frameworks, I decided to select Mina, which was relatively short of documentation and had fewer use groups, from the past one year's experience, I feel very good. Mina has a clear architecture and is very convenient for custom expansion. After the release of 1.0, the official website has been enriched and many documents have been added. More and more people have heard of Mina. Later, JDK 1.5 was released with the built-in concurrent of JDK instead of backport-util-concurrent. Currently, both 1.0 and 1.1 exist, but no new features are added. Only the bug fix version is released. The new features are all implemented in 2.0. 2.0 has adjusted the architecture and improved the performance, it is still under development.

Basic Features
Java NiO supports TCP and UDP protocols, and supports communication between RS232 and VM. Since Mina has a clear architecture, you can easily implement an underlying network protocol. Currently, blocking Io is not supported. It seems that there is no plan to support it. Of course you can implement a blocking model on it. However, based on my experience, non-blocking Io is more suitable for Server programming.

A filter model similar to servletfilter. This is what I think is the essence of Mina. By introducing the filter model, we can separate some non-business functions with a clearer hierarchy and the idea of AOP, you can easily perform functions such as log, protocol conversion, and compression, as well as dynamically add or remove functions during running.
You can directly use the underlying bytebuffer, or use the user-defined message object and encoding method.
Highly customizable thread model, single thread, one thread pool, or multiple thread pools similar to Seda.
SSL support, attack defense and traffic control, mock test friendliness, JMX support, and spring integration. Do you need more.

 

A simple example
Mina is very simple to use. I have worked on traditional Java Socket development for a while before, but I have never understood Java Nio very well, but Mina will soon get started. Mina encapsulates the cumbersome part of NiO, this allows you to focus more on business functions. Let's look at a simple example, a common example, a time server.

Our goal is to respond to requests from multiple clients and then return the current system time of the server. For traditional Java Socket programs, we need to create a new thread to respond to each accept client connection, which limits the overall load capacity of the system, in addition, we must manually write connection management and other code. Let's take a look at how Mina works.

First, we will download Mina 1.1 from the official website. Here we assume that JDK is a version later than 1.5. If JDK 1.4 is used, download Mina 1.0. Mina 1.0 is almost the same as 1.1, however, JDK 1.5 or above is strongly recommended for better performance.

After Uncompressing the package, you can see many jar packages. The specific functions of each package are not described here. You can import all the packages into the project. It is worth noting that Mina uses a slf4j logstore, which has the potential to ban common-logging. Here is our main program, which is very simple.

First, we need an ioacceptor. Here we select a socketacceptor, that is, the TCP protocol.

Then, we add the LOG filter and Protocol encoding filter to the application.

Finally, we need to bind the acceptor to port 8123 of the Local Machine and use timeserverhandler to implement the Protocol.

Timeserverhandler is the place where we implement specific business functions. Iohandleradapter provides seven event methods. All we need to do is to pick the events we need to respond to and reload them. In my example, I have overloaded two methods. Sessioncreated is called during client connection. Generally, some initialization operations are performed here. Here I just print a piece of information. Messagereceived is the central part of the entire handler. Every message sent from the client is converted into a call to this method. Because the Protocol encoding filter is added, the object MSG obtained here is a string instead of the default bytebuffer (protocolcodecfilter will be described in detail below ). Here we implement a very simple business function. If the user inputs quit, the connection is disconnected; otherwise, the current time is entered. It can be seen that iosession encapsulates the operation on the current connection.

So far, we have implemented a time server.

01. public  class  TimeServer { 02.    public  static  void  main(String[] args)  throws  IOException { 03.      IoAcceptor acceptor =  new  SocketAcceptor(); 04.     05.      SocketAcceptorConfig cfg =  new  SocketAcceptorConfig(); 06.      cfg.getFilterChain().addLast(  "logger" new  LoggingFilter() ); 07.      cfg.getFilterChain().addLast(  "codec" new  ProtocolCodecFilter(  new  TextLineCodecFactory())); 08.     09.      acceptor.bind(  new  InetSocketAddress( 8123 ),  new  TimeServerHandler(), cfg); 10.      System.out.println( "Time server started." ); 11.    } 12. }

 

01. public  class  TimeServerHandler  extends  IoHandlerAdapter { 02.    public  void  messageReceived(IoSession session, Object msg)  throws  Exception { 03.      String str = (String) msg; 04.      if "quit" .equalsIgnoreCase(str) ) { 05.      session.close(); 06.      return ; 07.    } 08.     09.    Date date =  new  Date(); 10.    session.write( date.toString() ); 11.    System.out.println( "Message written..." ); 12.    } 13.     14.    public  void  sessionCreated(IoSession session)  throws  Exception { 15.    System.out.println( "Session created..." ); 16.    } 17. }

 

 

Mina Architecture
Here, I borrowed an image from the trustin Lee PPT in Asia 2006 to introduce the Mina architecture.
Remote peer is the client, and the box below is the main structure of Mina. The arrows between the boxes represent the data flow.

We can compare the previous example to see the architecture diagram. ioservice is the entrance to the entire Mina and is responsible for underlying Io operations. The client processes the messages sent by the client. The ioacceptor we just used is an ioservice. It is abstracted as an ioservice because mina processes server and client programming in the same architecture. Another subclass of ioservice is ioconnector, which is used for clients. However, based on my experience, it is inconvenient to use non-blocking Models for client programming. You 'd better seek other blocking communication frameworks.

Ioservice converts data into one event and passes it to iofilterchain. You can add a series of iofilters for various functions. The author's suggestion is to use iofilter to implement some functional and non-business-related code, so that the entire application structure is clearer and code reuse is convenient.

Events processed by iofilter are sent to iohandler, and then the specific business logic is implemented here. This part is very simple. If you have experience using swing, you will find that it is very similar to swing events. What you need to do is simply to reload the methods you need, then write specific business functions. Among them, the most important method is messagereceived.

It is worth noting that an iosession class, each iosession instance represents this connection, and we need to perform any operations on the connection through this class.

Messages sent from iohandler to the client by calling methods such as iosession. Write are transmitted in the reverse order of the input data until ioservice sends the data to the client.

This is all about Mina, isn't it easy.

Next, I will introduce in detail the three main classes involved in writing specific code: iohandler, iosession, and iofilter.
Iohandler

Mina implements an internal event model, while iohanlder is the final response location for all events. The name of each method clearly indicates the meaning of the event. Messagereceived is an event that receives client messages. We should implement the business logic here. Messagesent is an event for the server to send messages, which is generally not used. Sessionclosed is a client disconnection event. You can perform resource recycling and other operations here. It is worth noting that there are two events for client connection: sessioncreated and sessionopened. The two are slightly different. sessioncreated is triggered by the I/O processor thread, and sessionopened is, triggered by the Business thread, because mina has very few I/O processor threads, if we really need to use sessioncreated, it must also be a time-consuming operation. Generally, we should put the service initialization function in the sessionopened event.

Careful readers may find that the previous example inherits iohandleradapter, which is actually an empty Implementation of iohanlder, so that we don't need to reload events that are not of interest.

Iosession
Iosession is an interface, which is used in many places in Mina. It reflects the idea of interface-oriented programming. It provides the operation functions for the current connection, as well as the storage function of user-defined attributes, which is very important. Iosession is thread-safe, that is, we can operate iosession freely in a multi-threaded environment, which brings great benefits to development. Let's take a look at the specific methods provided. I will list some common and important methods.

Here, I divide the iosession method into three categories.

The first type is the connection operation function.
There are two main methods: sending messages to the client and disconnecting. It can be seen that the write variable is an object, but what type should be passed in? It depends on whether protocolcodecfilter is used. If protocolcodecfilter is used, the message may be a string or a user-defined JavaBean. By default, message is a bytebuffer. Bytebuffer is a class of Mina. It has the same name as Java. NiO. bytebuffer. Mina 2.0 will change it to iobuffer to avoid misunderstanding.

Another noteworthy thing is the future class. Mina is a non-blocking communication framework. One obvious manifestation is that calling the iosession. Write method will not be blocked. After the user calls the write method, the message content will be sent to the underlying layer for sending. It is unknown when the message will be sent. Of course, after actually calling write, the data is almost immediately sent, benefiting from the high performance of NiO. However, if we have to confirm that the message is sent and then perform some processing, we need to use the future class. Below is a very common code

 

 

 

 

By calling Future. join, the program will be blocked until the message processing is complete. We can also use future. iswritten to determine whether the message is successfully sent.

Here, by the way, I found that the message sending will be automatically merged. To put it simply, if in a short period of time, when two write operations are performed on the same iosession, the client may only receive one message, which is followed by two messages sent by the server. This design can save network overhead in high concurrency, and the actual use process of the author has a very good effect. However, if this behavior causes the client to work abnormally, you can also disable it through parameters.

Class 2: attribute storage operations.
Generally, our system has a user State, and we need to store user attributes on the connection. iosession attribute is such a function. For example, if two connections are connected to the server at the same time, one connection is user A, the user ID is 13, the other connection is user B, and the user ID is 14, we can call iosession after the user logs on successfully. setattribute ("login_id", 13), and then in the subsequent operation, use iosession. getattribute ("login_id") obtains the current logon user ID and performs corresponding operations. Simply put, it is a function similar to httpsession. Of course, the specific implementation methods are different.

Category 3: connection status.
I will not talk about it here. From the method name, we can know its specific functions.

Iofilter
Filters are an important feature of Mina. Iofilter is also an interface, but it is relatively complicated. Here we will not list its methods. In simple terms, iofilter is like servletfilter. It performs some specific operations before or after an event is processed by iohandler, but it is more complex than servletfilter and can process many types of events, besides 7 events of iohandler, some internal events can be operated.

Mina provides some common iofilter implementations, such as loggingfilter, blacklistfilter, compressionfilter, and sslfilter. These filters are relatively simple, by reading their source code, we can further understand the implementation of filters. Here I will introduce two filters: protocolcodecfilter and executorfilter.

Protocolcodecfilter
The content transmitted over the network is actually a binary stream, but our business functions are not, or we should not directly operate on the binary stream. By default, the message sent to iohandler by Mina is a bytebuffer. If we operate bytebuffer directly on iohandler, a large amount of protocol analysis code will be mixed with the actual business code. The most suitable method is to convert bytebuffer to string or JavaBean in iofilter. protocolcodecfilter is a filter of such a function.

It is easy to use protocolcodecfilter. We only need to add protocolcodecfilter to the filterchain, but we need to provide a protocolcodecfactory. In fact, protocolcodecfilter only implements the filter function. It will hand over the final conversion work to the encode and decode obtained from protocolcodecfactory. If we need to write our protocolcodec, we should start with protocolcodecfactory. Mina has several built-in protocolcodecfactory, which are commonly used: objectserializationcodecfactory and textlinecodecfactory.

Objectserializationcodecfactory is used to convert the serialized content of a Java object directly with bytebuffer. It is suitable for both ends of Java. Textlinecodecfactory is the conversion of string and bytebuffer. To put it bluntly, it is the text. For example, you can use it to implement an SMTP server or POP server. In most cases, I use

Textlinecodecfactory.
Here we mention the order of iofilter. iofilter is sequential. For example, adding loggingfilter first and then protocolcodecfilter are different from adding protocolcodecfilter first and then loggingfilter, the former loggingfilter writes the content of logs to bytebuffer, while the latter writes logs to specific classes after conversion, such as string. In actual use, the filter sequence must be properly processed.

Executorfilter
Another important filter is executorfilter. Here, I need to first describe the Mina thread working mode. By default, Mina processes all client messages in a single thread, that is, even if you run on an 8-CPU machine, it may only use one CPU. In addition, if a message processing time is too long, other messages will wait and the overall throughput will decrease. Many of my friends complained about the poor performance of Mina because they didn't add executorfilter. Executorfilter is well designed. You can read the source code carefully. It combines messages of the same connection and calls messages in sequence, so that two threads can process the same connection at the same time.

1. IoAcceptor acceptor = ...; 2. IoServiceConfig acceptorConfig = acceptor.getDefaultConfig(); 3. acceptorConfig.setThreadModel(ThreadModel.MANUAL);
The iofitler sequence problem is mentioned again here. Generally, executorfilter is placed after protocolcodecfilter, because we do not need to execute protocolcodec in multiple threads, the performance of protocolcodec with a single thread is relatively high, and the specific business logic may also design database operations, so it is more suitable for running in different threads.

 

Optimization Guide
By default, the performance of the Mina configuration is not very high, in part because mina still retains the architecture of the initial version, and the other reason is the development of JVM.

1. IoAcceptor acceptor =  new  SocketAcceptor(Runtime.getRuntime().availableProcessors() +  1 , Executors.newCachedThreadPool());
First, disable the default threadmodel settings.  Threadmodel is a simple thread implementation for ioservice. However, it is too weak to cause a large number of problems in the concurrent environment. Threadmodel is directly canceled in Mina 2.0. You should use executorfilter to implement the thread. 1. acceptor.getDefaultConfig().getFilterChain().addLast( "threadPool" new  ExecutorFilter(Executors.newCachedThreadPool());

Then we add the I/O processing thread.
Each acceptor/connector uses a thread to process the connection, and then sends the connection to I/O processor for read/write operations. We can only modify the number of threads used by I/O processor, use the following code to set  Of course, executorfilter is to be added. The above has been described in detail.  During the development process, I encountered an outofmemoryerror multiple times. After research, I found the cause. By default, Mina uses direct memory to implement the bytebuffer pool (hereinafter referred to as direct buffer), and uses JNI to open up a space in the memory, this solution is a very good feature in earlier Mina versions, because at the initial stage of Mina development, the JVM is not as powerful as it is, and the direct buffer with pool effect has better performance. However, when we use commands such as-XMS-xmx to increase the memory available for JVM, it only increases the heap memory space, while the direct memory space does not increase, in actual use of Mina, outofmemoryerror occurs frequently. If you really want to use direct memory, you can use the-XX: maxdirectmemorysize option to set it. However, I do not recommend this because the latest tests show that direct memory performs worse than heap in modern JVMs. Some readers may wonder why the heap is used instead of the pool and GC is required. That's because the jvm gc capability is already strong, and pool synchronization is also a performance issue in the concurrent environment. We can use this code to set  Mina 2.0 has changed Direct Memory Allocation to heap by default to provide the best performance and stability. 1. ByteBuffer.setUseDirectBuffers( false ); 2. ByteBuffer.setAllocator( new  SimpleByteBufferAllocator());
The last optimization technique is to deploy your application on Linux and enable Java NiO to use the Linux epoll function. You may not have heard of epoll yet, but you should have heard of Lighttpd, nginx, squid, and so on. Thanks to epoll, they provide high network performance and occupy a very small amount of system resources. JDK 6 has enabled the epoll configuration by default. Therefore, we recommend that you deploy your application on JDK 6, because JDK 6 also has other optimization features. If your application must be deployed on JDK 5, you can enable the epoll support through parameters.

Building simple and high-performance NiO applications based on Mina

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.