Netty的特性
- 設計統一的API,適用於不同的協議(阻塞和非阻塞)基於靈活、可擴展的事件驅動模型高度可定製的線程模型可靠的無連接數據Socket支持(UDP)
- 性能更好的吞吐量,低延遲更省資源儘量減少不必要的內存拷貝
- 安全完整的SSL/TLS和STARTTLS的支持能在Applet與Android的限制環境運行良好
- 健壯性不再因過快、過慢或超負載連接導致OutOfMemoryError不再有在高速網絡環境下NIO讀寫頻率不一致的問題
- 易用完善的JavaDoc,用戶指南和樣例簡潔簡單僅信賴於JDK1.5
Netty 在哪些行業得到了應用?
互聯網行業
隨着網站規模的不斷擴大,系統併發訪問量也越來越高,傳統基於 Tomcat 等 Web 容器的垂直架構已經無法滿足需求,需要拆分應用進行服務化,以提高開發和維護效率。從組網情況看,垂直的架構拆分之後,系統採用分佈式部署,各個節點之間需要遠程服務調用,高性能的 RPC 框架必不可少,Netty 作爲異步高性能的通信框架,往往作爲基礎通信組件被這些 RPC 框架使用。
典型的應用有
阿里分佈式服務框架 Dubbo 的 RPC 框架使用 Dubbo 協議進行節點間通信,Dubbo 協議默認使用 Netty 作爲基礎通信組件,用於實現各進程節點之間的內部通信。它的架構圖如下:
其中,服務提供者和服務消費者之間,服務提供者、服務消費者和性能統計節點之間使用 Netty 進行異步/同步通信。
除了 Dubbo 之外,淘寶的消息中間件 RocketMQ 的消息生產者和消息消費者之間,也採用 Netty 進行高性能、異步通信。
除了阿里系和淘寶系之外,很多其它的大型互聯網公司或者電商內部也已經大量使用 Netty 構建高性能、分佈式的網絡服務器。
遊戲行業
無論是手遊服務端、還是大型的網絡遊戲,Java 語言得到了越來越廣泛的應用。Netty 作爲高性能的基礎通信組件,它本身提供了 TCP/UDP 和 HTTP 協議棧,非常方便定製和開發私有協議棧。賬號登陸服務器、地圖服務器之間可以方便的通過 Netty 進行高性能的通信,架構示意圖如下:
大數據領域
經典的 Hadoop 的高性能通信和序列化組件 Avro 的 RPC 框架,默認採用 Netty 進行跨節點通信,它的 Netty Service 基於 Netty 框架二次封裝實現。
大數據計算往往採用多個計算節點和一個/N個彙總節點進行分佈式部署,各節點之間存在海量的數據交換。由於 Netty 的綜合性能是目前各個成熟 NIO 框架中最高的,因此,往往會被選中用作大數據各節點間的通信。
企業軟件
企業和 IT 集成需要 ESB,Netty 對多協議支持、私有協議定製的簡潔性和高性能是 ESB RPC 框架的首選通信組件。事實上,很多企業總線廠商會選擇 Netty 作爲基礎通信組件,用於企業的 IT 集成。
通信行業
Netty 的異步高性能、高可靠性和高成熟度的優點,使它在通信行業得到了大量的應用。
簡單實現服務器端和客戶端
NettyServer.java
package netty;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
/**
* Author: yexx
*
*/
public class NettyServer {
public static void main(String[] args) {
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
// Set up the default event pipeline.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new StringDecoder(), new StringEncoder(), new ServerHandler());
}
});
// Bind and start to accept incoming connections.
Channel bind = bootstrap.bind(new InetSocketAddress(8000));
System.out.println("Server已經啓動,監聽端口: " + bind.getLocalAddress() + ", 等待客戶端註冊。。。");
}
private static class ServerHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (e.getMessage() instanceof String) {
String message = (String) e.getMessage();
System.out.println("Client發來:" + message);
e.getChannel().write("Server已收到剛發送的:" + message);
System.out.println("\n等待客戶端輸入。。。");
}
super.messageReceived(ctx, e);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
super.exceptionCaught(ctx, e);
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("有一個客戶端註冊上來了。。。");
System.out.println("Client:" + e.getChannel().getRemoteAddress());
System.out.println("Server:" + e.getChannel().getLocalAddress());
System.out.println("\n等待客戶端輸入。。。");
super.channelConnected(ctx, e);
}
}
}
NettyClient.java
package netty;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
/**
* Author: yexx
*
*/
public class NettyClient {
public static void main(String[] args) {
// Configure the client.
ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
// Set up the default event pipeline.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new StringDecoder(), new StringEncoder(), new ClientHandler());
}
});
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress("localhost", 8000));
// Wait until the connection is closed or the connection attempt fails.
future.getChannel().getCloseFuture().awaitUninterruptibly();
// Shut down thread pools to exit.
bootstrap.releaseExternalResources();
}
private static class ClientHandler extends SimpleChannelHandler {
private BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (e.getMessage() instanceof String) {
String message = (String) e.getMessage();
System.out.println(message);
e.getChannel().write(sin.readLine());
System.out.println("\n等待客戶端輸入。。。");
}
super.messageReceived(ctx, e);
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("已經與Server建立連接。。。。");
System.out.println("\n請輸入要發送的信息:");
super.channelConnected(ctx, e);
e.getChannel().write(sin.readLine());
}
}
}
運行可以自己試一試