Netty高效能大容量Socket併發 一
阿新 • • 發佈:2019-01-27
Netty效能測試
Netty是由JBOSS提供的一個Java開源框架。Netty提供非同步的、事件驅動的網路應用程式框架和工具,用以快速開發高效能、高可靠性的網路伺服器和客戶端程式。Netty 是一個基於NIO的客戶,伺服器端程式設計框架,使用Netty 可以確保你快速和簡單的開發出一個網路應用,例如實現了某種協議的客戶,服務端應用。Netty相當簡化和流線化了網路應用的程式設計開發過程,例如,TCP和UDP的socket服務開發。
Netty簡化socket程式設計,並沒有使寫出來的Socket效率變低,我寫了一個所有都使用預設配置的服務端,模擬最常用的RPC方案,傳輸協議使用4位元組長度加json串的方式來傳輸, 測試服務端的通訊效率,測試結果如下。
從測試結果看,Netty效能是非常高的,在所有使用預設配置的情況下,單臺伺服器能夠達到4萬次請求解析,作為RPC框架是足夠用的。還有一個有趣的現象是每次都建立連線和重用連線的差別不大,效能損耗對應用層幾乎沒影響,但是大家如果在應用環境中使用每次新建的情況,一定要進行壓測,確認沒影響後再使用。
測試用例說明
- 部署1臺Netty伺服器,部署8臺併發測試客戶端,每個客戶端跑1024個併發;
- 分為1次連線請求1000次資料和1次連線請求1次資料迴圈1000次;
- Netty伺服器為Cent OS 6.5 64位,阿里雲8核16G記憶體,JVM使用預設配置,沒有進行任何調優;
- 併發客戶端為Windows Server 2008 R2 64位企業版,阿里雲8核16G記憶體,NET Framework 4.5執行環境,沒有進行任何調優;
- 通訊資料格式為:4位元組長度+JSON串,伺服器負責接收JSON串,進行解析和返回;
Netty服務端程式碼
主程式入口- publicclass AppNettyServer {
- publicstaticvoid main(String[] args) throws Exception {
- System.out.print("Hello Netty\r\n");
- int port = 9090;
- if (args != null && args.length > 0) {
-
try
- port = Integer.valueOf(args[0]);
- } catch (NumberFormatException e) {
- System.out.print("Invalid Port, Start Default Port: 9090");
- }
- }
- try {
- NettyCommandServer commandServer = new NettyCommandServer();
- System.out.print("Start listen socket, port " + port + "\r\n");
- commandServer.bind(port);
- } catch (Exception e) {
- System.out.print(e.getMessage());
- }
- }
- }
- publicclass NettyCommandServer {
- publicstatic InternalLogger logger = InternalLoggerFactory.getInstance(NettyCommandServer.class);
- publicvoid bind(int port) throws Exception {
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- try {
- ServerBootstrap serverBootstrap = new ServerBootstrap();
- serverBootstrap.group(bossGroup, workerGroup);
- serverBootstrap.channel(NioServerSocketChannel.class);
- serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
- serverBootstrap.handler(new LoggingHandler());
- serverBootstrap.childHandler(new NettyChannelHandler());
- ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
- channelFuture.channel().closeFuture().sync();
- } finally {
- bossGroup.shutdownGracefully();
- workerGroup.shutdownGracefully();
- }
- }
- privateclass NettyChannelHandler extends ChannelInitializer<SocketChannel> {
- @Override
- protectedvoid initChannel(SocketChannel socketChannel)
- throws Exception {
- socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(ByteOrder.BIG_ENDIAN, 64 * 1024, 0, 4, 0, 4, true));
- socketChannel.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8")));
- socketChannel.pipeline().addLast(new NettyCommandHandler());
- }
- }
- }
- publicclass NettyCommandHandler extends ChannelHandlerAdapter {
- privateint counter = 0;
- @Override
- publicvoid channelRead(ChannelHandlerContext ctx, Object msg) {
- try {
- String body = (String) msg;
- JsonDataObject request = JsonUtil.fromJson(body, JsonDataObject.class);
- counter = counter + 1;
- JsonDataObject response = new JsonDataObject();
- response.setCode(0);
- response.setMsg("Success");
- response.setData(counter+"");
- String respJson = JsonUtil.toJson(response);
- byte[] respUtf8 = respJson.getBytes("UTF-8");
- int respLength = respUtf8.length;
- ByteBuf respLengthBuf = PooledByteBufAllocator.DEFAULT.buffer(4);
- respLengthBuf.writeInt(respLength);
- respLengthBuf.order(ByteOrder.BIG_ENDIAN);
- ctx.write(respLengthBuf);
- ByteBuf resp = PooledByteBufAllocator.DEFAULT.buffer(respUtf8.length);
- resp.writeBytes(respUtf8);
- ctx.write(resp);
- } catch (Exception e) {
- NettyCommandServer.logger.error(e.getMessage() + "\r\n");
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- e.printStackTrace(pw);
- pw.flush();
- sw.flush();
- NettyCommandServer.logger.error(sw.toString());
- }
- }
- @Override
- publicvoid channelReadComplete(ChannelHandlerContext ctx) {
- ctx.flush();
- }
- @Override
- publicvoid exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
- ctx.close();
- }
- }
- publicclass SocketClient : Object
- {
- private TcpClient tcpClient;
- public SocketClient()
- {
- tcpClient = new TcpClient();
- tcpClient.Client.Blocking = true;
- }
- publicvoid Connect(string host, int port)
- {
- tcpClient.Connect(host, port);
- }
- publicvoid Disconnect()
- {
- tcpClient.Close();
- }
- publicstring SendJson(string json)
- {
- byte[] bufferUTF8 = Encoding.UTF8.GetBytes(json);
- int jsonLength = System.Net.IPAddress.HostToNetworkOrder(bufferUTF8.Length); //轉換為網路位元組順序,大頭結構
- byte[] bufferLength = BitConverter.GetBytes(jsonLength);
- tcpClient.Client.Send(bufferLength, 0, bufferLength.Length, SocketFlags.None); //傳送4位元組長度
- tcpClient.Client.Send(bufferUTF8, 0, bufferUTF8.Length, SocketFlags.None); //傳送Json串內容
- byte[] bufferRecvLength = newbyte[sizeof(int)];
- tcpClient.Client.Receive(bufferRecvLength, sizeof(int), SocketFlags.None); //獲取長度
- int recvLength = BitConverter.ToInt32(bufferRecvLength, 0);
- recvLength = System.Net.IPAddress.NetworkToHostOrder(recvLength); //轉為本地位元組順序
- byte[] bufferRecvUtf8 = newbyte[recvLength];
- tcpClient.Client.Receive(bufferRecvUtf8, recvLength, SocketFlags.None);
- string recvCommand = Encoding.UTF8.GetString(bufferRecvUtf8, 0, recvLength);
- return recvCommand;
- }
- }