Chapter 4 Netty In Action: Transports, nettyinaction

Source: Internet
Author: User
Tags configuration settings

Chapter 4 Netty In Action: Transports, nettyinaction

Note: This article is from Netty In Action;

NOTE: If your original translation is reprinted, please indicate the source!

Content of this Chapter

  • Transports)
  • NIO (non-blocking IO, New IO), OIO (Old IO, blocking IO), Local (Local), Embedded (Embedded)
  • Use-case)
  • APIs (Interface)
A very important task of network applications is data transmission. The process of data transmission varies depending on which transport is used, but the transmission method is the same: all are transmitted using bytecode. The process and method of data transmission in Java Development network programs are abstracted. We do not need to pay attention to the underlying interfaces, you only need to use Java APIs or other network frameworks such as Netty to transmit data. Both sending and receiving data are bytecode. Nothing more, nothing less.

If you have worked using the network interface provided by Java, you may have encountered the situation that you want to switch from blocked transmission to non-blocking transmission. This switching is more difficult, because the APIs used for blocking IO and non-blocking IO are very different, Netty provides upper-layer transmission implementation interfaces to simplify this situation. We can make the written code as generic as possible without relying on some implementations related to APIs. When we want to switch the transmission mode, we don't need to spend a lot of energy and time restructuring the code.

This chapter describes unified APIs and how to use them. It compares Netty APIs with Java APIs to show you why Netty is easier to use. This chapter also provides some excellent use case code for the best use of Netty. Netty does not require any other network framework or network programming experience. If netty is used, it is helpful but not necessary to understand Netty. Let's take a look at the transmission work in the world.

4.1 Case Study: switching transmission modes
In order to let you imagine how to transport, I will start with a simple application, this application does nothing, just accept client connection and send "Hi !" The string message is sent to the client, and the connection is disconnected after the message is sent. I will not explain in detail the implementation of this process. It is just an example. 4.1.1 using Java I/O and NIO
We will not use Netty to implement this example. The following code is an example of implementing IO blocking:
package netty.in.action;import java.io.IOException;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;import java.nio.charset.Charset;/** * Blocking networking without Netty * @author c.k * */public class PlainOioServer {public void server(int port) throws Exception {//bind server to portfinal ServerSocket socket = new ServerSocket(port);try {while(true){//accept connectionfinal Socket clientSocket = socket.accept();System.out.println("Accepted connection from " + clientSocket);//create new thread to handle connectionnew Thread(new Runnable() {@Overridepublic void run() {OutputStream out;try{out = clientSocket.getOutputStream();//write message to connected clientout.write("Hi!\r\n".getBytes(Charset.forName("UTF-8")));out.flush();//close connection once message written and flushedclientSocket.close();}catch(IOException e){try {clientSocket.close();} catch (IOException e1) {e1.printStackTrace();}}}}).start();//start thread to begin handling}}catch(Exception e){e.printStackTrace();socket.close();}}}
The above method is very simple, but this blocking mode will have serious problems in the case of large connections, such as client connection timeout, serious server response delay. To solve this problem, we can use an Asynchronous Network to process all the concurrent connections, but the problem is that NIO and OIO APIs are completely different, therefore, a network application developed with OIO wants to use NIO to reconstruct the code, which is almost re-developed.
The following code is an example of using Java NIO:
package netty.in.action;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Iterator;/** * Asynchronous networking without Netty * @author c.k * */public class PlainNioServer {public void server(int port) throws Exception {System.out.println("Listening for connections on port " + port);//open Selector that handles channelsSelector selector = Selector.open();//open ServerSocketChannelServerSocketChannel serverChannel = ServerSocketChannel.open();//get ServerSocketServerSocket serverSocket = serverChannel.socket();//bind server to portserverSocket.bind(new InetSocketAddress(port));//set to non-blockingserverChannel.configureBlocking(false);//register ServerSocket to selector and specify that it is interested in new accepted clientsserverChannel.register(selector, SelectionKey.OP_ACCEPT);final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());while (true) {//Wait for new events that are ready for process. This will block until something happensint n = selector.select();if (n > 0) {//Obtain all SelectionKey instances that received eventsIterator<SelectionKey> iter = selector.selectedKeys().iterator();while (iter.hasNext()) {SelectionKey key = iter.next();iter.remove();try {//Check if event was because new client ready to get acceptedif (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel client = server.accept();System.out.println("Accepted connection from " + client);client.configureBlocking(false);//Accept client and register it to selectorclient.register(selector, SelectionKey.OP_WRITE, msg.duplicate());}//Check if event was because socket is ready to write dataif (key.isWritable()) {SocketChannel client = (SocketChannel) key.channel();ByteBuffer buff = (ByteBuffer) key.attachment();//write data to connected clientwhile (buff.hasRemaining()) {if (client.write(buff) == 0) {break;}}client.close();//close client}} catch (Exception e) {key.cancel();key.channel().close();}}}}}}
As you can see, even if they implement the same functions, the code is completely different. Next we will use Netty to implement the same function.
4.1.2 use I/O and NIO in Netty
The following code uses Netty as an example of blocking IO written by the network framework:
Package netty. in. action; import java.net. inetSocketAddress; import io. netty. bootstrap. serverBootstrap; import io. netty. buffer. byteBuf; import io. netty. buffer. unpooled; import io. netty. channel. channel; import io. netty. channel. channelFuture; import io. netty. channel. channelFutureListener; import io. netty. channel. channelHandlerContext; import io. netty. channel. channelInboundHandlerAdapter; import io. netty. channel. ChannelInitializer; import io. netty. channel. eventLoopGroup; import io. netty. channel. nio. nioEventLoopGroup; import io. netty. channel. socket. oio. oioServerSocketChannel; import io. netty. util. charsetUtil; public class NettyOioServer {public void server (int port) throws Exception {final ByteBuf buf = Unpooled. unreleasableBuffer (Unpooled. copiedBuffer ("Hi! \ R \ n ", CharsetUtil. UTF_8); // event loop group EventLoopGroup group = new NioEventLoopGroup (); try {// used to guide the server to configure ServerBootstrap B = new ServerBootstrap (); // use OIO blocking mode B. group (group ). channel (OioServerSocketChannel. class ). localAddress (new InetSocketAddress (port) // specify ChannelInitializer to initialize handlers. childHandler (new ChannelInitializer <Channel> () {@ Overrideprotected void initChannel (Channel ch) throws Exception {// Add an "inbound" handler to ChannelPipelinech. pipeline (). addLast (new ChannelInboundHandlerAdapter () {@ Overridepublic void channelActive (ChannelHandlerContext ctx) throws Exception {// after the connection, write the message to the client and close the connection to ctx. writeAndFlush (buf. duplicate ()). addListener (ChannelFutureListener. CLOSE) ;}}) ;}}); // The bound Server accepts the connection ChannelFuture f = B. bind (). sync (); f. channel (). closeFuture (). sync ();} catch (Exception e) {// release all resource groups. shutdownGracefully ();}}}
The above code implements the same functions, but the structure is clear, which is only one of Netty's advantages. 4.1.3 asynchronous support in Netty
The following code uses Netty to implement Asynchronization. It can be seen that it is very convenient to switch Netty from OIO to NIO.
Package netty. in. action; import io. netty. bootstrap. serverBootstrap; import io. netty. buffer. byteBuf; import io. netty. buffer. unpooled; import io. netty. channel. channelFuture; import io. netty. channel. channelFutureListener; import io. netty. channel. channelHandlerContext; import io. netty. channel. channelInboundHandlerAdapter; import io. netty. channel. channelInitializer; import io. netty. channel. eventLoopGroup; import Io. netty. channel. nio. nioEventLoopGroup; import io. netty. channel. socket. socketChannel; import io. netty. channel. socket. nio. nioServerSocketChannel; import io. netty. util. charsetUtil; import java.net. inetSocketAddress; public class NettyNioServer {public void server (int port) throws Exception {final ByteBuf buf = Unpooled. unreleasableBuffer (Unpooled. copiedBuffer ("Hi! \ R \ n ", CharsetUtil. UTF_8); // event loop group EventLoopGroup group = new NioEventLoopGroup (); try {// used to guide the server to configure ServerBootstrap B = new ServerBootstrap (); // use NIO asynchronous mode B. group (group ). channel (NioServerSocketChannel. class ). localAddress (new InetSocketAddress (port) // specify ChannelInitializer to initialize handlers. childHandler (new ChannelInitializer <SocketChannel> () {@ Overrideprotected void initChannel (SocketChannel ch) throws Exception {// Add an "inbound" handler to ChannelPipelinech. pipeline (). addLast (new ChannelInboundHandlerAdapter () {@ Overridepublic void channelActive (ChannelHandlerContext ctx) throws Exception {// after the connection, write the message to the client and close the connection to ctx. writeAndFlush (buf. duplicate ()). addListener (ChannelFutureListener. CLOSE) ;}}) ;}}); // The bound Server accepts the connection ChannelFuture f = B. bind (). sync (); f. channel (). closeFuture (). sync ();} catch (Exception e) {// release all resource groups. shutdownGracefully ();}}}
Because Netty uses the same API to implement each transmission, it does not care about what you use to implement it. Netty implements transmission through Channel interfaces, ChannelPipeline, and ChannelHandler. 4.2 Transport API
The core of the transfer API is the Channel interface, which is used for all outbound operations. The class hierarchy of the Channel interface is as follows:
As shown in, each Channel is allocated with a ChannelPipeline and ChannelConfig. ChannelConfig is responsible for setting and storing configurations and allows updating them during runtime. Transmission usually has specific configuration settings, only for transmission, no other implementation. ChannelPipeline holds the ChannelHandler instances used to process the "inbound" and "outbound" data transmitted by the channel. The implementation of ChannelHandler allows you to change the data status and transmit data. This book describes ChannelHandler in detail. ChannelHandler is a key concept of Netty.
Now we can use ChannelHandler to do the following:
  • When transferring data, convert the data from one format to another
  • Exception notification
  • Channel becomes valid or invalid for notification
  • Notification when Channel is registered or deregistered from EventLoop
  • Notifies users of specific events
These ChannelHandler instances are added to ChannelPipeline and executed sequentially in ChannelPipeline. It is similar to a chain. readers who have used Servlet may be more familiar with it. ChannelPipeline implements the interception filter mode, which means we connect different ChannelHandler to intercept and process data or events that pass through ChannelPipeline. You can think of ChannelPipeline as a UNIX pipeline, which allows different command chains (ChannelHandler is equivalent to a command ). You can also add ChannelHandler instances to ChannelPipeline or delete ChannelPipeline instances as needed, which helps us build highly flexible Netty programs. In addition, you can access the specified ChannelPipeline and ChannelConfig on the Channel itself. Channel provides many methods, as shown in the following list:
  • EventLoop () returns the EventLoop allocated to the Channel.
  • Pipeline (), returns the ChannelPipeline allocated to the Channel
  • IsActive (), returns whether the Channel is activated. If it is activated, it is equivalent to a remote connection.
  • LocalAddress (), returns the bound local SocketAddress
  • RemoteAddress (), return the bound remote SocketAddress
  • Write (), write data to a remote client, data is transmitted through ChannelPipeline
These methods will become increasingly familiar later. Now we only need to remember that all our operations are run on the same interface. The high flexibility of Netty allows you to refactor with different transmission implementations. To write data to a remote connected client, you can call the Channel. write () method. The Code is as follows:
Channel channel = ...//Create ByteBuf that holds data to writeByteBuf buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8);//Write dataChannelFuture cf = channel.write(buf);//Add ChannelFutureListener to get notified after write completescf.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) {//Write operation completes without errorif (future.isSuccess()) {System.out.println(.Write successful.);} else {//Write operation completed but because of errorSystem.err.println(.Write error.);future.cause().printStacktrace();}}});
A Channel is thread-safe. It can be operated by multiple different threads. In a multi-threaded environment, all methods are safe. Because the Channel is secure, we store references to the Channel and use it to write data to a remotely connected client during learning. This is also true for multithreading. The following code is a simple multi-threaded example:
final Channel channel = ...//Create ByteBuf that holds data to writefinal ByteBuf buf = Unpooled.copiedBuffer("your data",CharsetUtil.UTF_8);//Create Runnable which writes data to channelRunnable writer = new Runnable() {@Overridepublic void run() {channel.write(buf.duplicate());}};//Obtain reference to the Executor which uses threads to execute tasksExecutor executor = Executors.newChachedThreadPool();// write in one thread//Hand over write task to executor for execution in threadexecutor.execute(writer);// write in another thread//Hand over another write task to executor for execution in threadexecutor.execute(writer);
In addition, this method ensures that the written messages are written in the same order by writing them. For more information, see the Netty API documentation. 4.3 Netty's transmission implementation Netty comes with some transport protocols. Although it does not support all transmission protocols, it is enough for us to use it. The transmission protocol of the Netty application depends on the underlying protocol. This section describes the transmission protocol in Netty. The transmission methods in Netty are as follows:
  • NIO, io. netty. channel. socket. nio, based on java. nio. channels toolkit, uses selector as the basic method.
  • OIO, io. netty. channel. socket. oio, based on the java.net toolkit, use blocking stream.
  • Local, io. netty. channel. local, used for Local communication between virtual machines.
  • Embedded, io. netty. channel. embedded, is Embedded for transmission. It allows ChannelHandler to be used in transportation without a real network. It can be very useful to test the implementation of ChannelHandler.
4.3.1 NIO-Nonblocking I/O
NIO transmission is currently the most commonly used method. It provides a completely asynchronous way to operate all I/O by using the selector. NIO is provided from Java 1.4. In NIO, we can register a channel or obtain the status of a channel change. The channel status has the following changes:
  • A new Channel is accepted and ready
  • Channel Connection completed
  • The Channel has data and is ready to read.
  • Channel sends data
After processing the changed status, you need to reset their status and use a thread to check whether there are prepared channels. If yes, execute related events. Here, only one registered event may be ignored. The operations supported by the SelectionKey are defined as follows:
  • OP_ACCEPT, notified when a new connection exists
  • OP_CONNECT, notified after connection is complete
  • OP_READ, which is notified when data is ready for reading.
  • OP_WRITE, notified when data is written to the Channel
NIO transmission in Netty receives and sends data based on this model, and provides its own interfaces to users through encapsulation, which completely hides internal implementations. As mentioned above, Netty hides internal implementation details and exposes abstracted APIs for use. The following is a processing flowchart:
NIO also has a certain delay in the processing process. If the number of connections is not large, the delay is generally within milliseconds, but its throughput is still higher than the OIO mode. In Netty, NIO is transmitted as "zero-file-copy", that is, zero file Replication. This mechanism allows programs to transmit content faster and more efficiently from the file system, zero replication means that our application will not copy the sent data to the JVM stack for processing, but directly operate from the kernel space. Next we will discuss OIO transmission, which is blocked. 4.3.2 OIO-Old blocking I/O
OIO is the Socket interface provided in java. java initially only provides blocked sockets, blocking will lead to low program performance. The following is the OIO processing flowchart. For more information, see other relevant materials.

4.3.3 Local-In VM transport
Netty includes local transmission, which uses the same API for communication between virtual machines. The transmission is completely asynchronous. Each Channel uses a unique SocketAddress. The client uses SocketAddress for connection and is registered on the server for long-term operation. Once the Channel is closed, it is automatically logged out and cannot be used by the client. The behavior of connecting to the Local Transmission Server is almost the same as that of other transmission implementations. Note that you can only use them on local servers and clients. Local is not bound to any Socket, and the value provides communication between JVM processes. 4.3.4 Embedded transport
Netty also includes embedded transmission. Compared with other transmission implementations described earlier, is Netty a real transmission? If it is not a real transfer, what can we do with it? Embedded transport allows easier use of different ChannelHandler interactions, which is also easier to embed into other ChannelHandler instances and use them like a helper class. It is generally used to test the specific ChannelHandler implementation. You can also use some ChannelHandler in ChannelHandler for extension. To achieve this purpose, it comes with a specific Channel implementation, namely: embeddedChannel. 4.4 When will each transmission mode be used?
Do not go into details. refer to the following list:
  • OIO, used for low connections, low latency, and blocking
  • NIO, used for high connections
  • Local, used for communication within the same JVM
  • Embedded, used to test ChannelHandler



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.