天天看點

java使用netty

Netty為什麼會高效?回答就是良好的線程模型,和記憶體管理。在Java的NIO例子中就我将用戶端的操作單獨放在一個線程中處理了,這麼做的原因在于如果将用戶端連接配接串起來,後來的連接配接就要等前一個處理完,當然這并不意味着多線程比單線程有優勢,而是在于每個用戶端都需要進行讀取準備好的緩存資料,再執行一些業務邏輯。如果業務邏輯耗時很久,那麼順序執行的方式沒有多線程優勢大。另一個方面目前多核CPU很常見了,多線程是個不錯的選擇。這些在第一節就說明過,也提到過NIO并不是提升了IO操作的速度,而是減少了CPU的浪費時間,這些概念不能搞混。

netty作為一款服務端架構,其優勢在于自身良好的線程模型架構,關于netty的基本架構,我用幾張圖進行說明,netty主要處理方式有三種線程模型,1、 單線程 2、主從線程 3、主從多線程

1、事件分離器把接收到的客戶事件分發到不同的事件處理器中,在netty中,處理器定義為channelHandler:如下圖:

java使用netty

2、netty處理請求結構,了解起來就是,用戶端發起請求,服務端開辟一個兩個線程組,一個線程組負責接收請求,由于是線程組,可了解為線程池,能夠處理的并發請求量就提升了,然後是工作線程,主線程組把請求轉發給從線程組,也叫工作線程池,這個線程組負責執行具體的業務,

java使用netty

3、那麼最終的處理結構就是,

java使用netty

下面用具體的代碼來模拟一下上述過程的實作,這裡為了示範,做一個用戶端通路具體的端口号,由服務端傳回一條資訊,

4、pom檔案:

<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>5.0.0.Alpha2</version>
			<!-- <version>4.1.24.Final</version> -->
		</dependency>

5、測試主類,

           

package com.congge.sort.netty.day1;

import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.nio.NioServerSocketChannel;

/**

  • netty測試
  • @author asus

*/

public class HelloServer {

public static void main(String[] args) {
	
	//定義主線程組,用于接收用戶端請求,但是并不做任何邏輯處理
	EventLoopGroup bossGroup = new NioEventLoopGroup();
	//從線程組,主線程組會把相應的請求轉交給該線程組,由從線程組去做任務
	EventLoopGroup workerGroup = new NioEventLoopGroup();
	//建立netty伺服器
	ServerBootstrap serverBootstrap = new ServerBootstrap();
	try {
		//伺服器設定綁定兩個線程組,并設定相應的助手類【handler】
		serverBootstrap.group(bossGroup, workerGroup)
						.channel(NioServerSocketChannel.class)				//設定nio雙向通道
						.childHandler(new HelloServerInitializer());		//子處理器
		
		//啟動server并綁定端口号
		ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
		//關閉監聽的channel
		channelFuture.channel().closeFuture().sync();
	} catch (Exception e) {
		e.printStackTrace();
	}finally {
		bossGroup.shutdownGracefully();
		workerGroup.shutdownGracefully();
	}
	
}
           

}

package com.congge.sort.netty.day1;


import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

/**
 * 初始化配置器,channel注冊成功後,會執行裡面相應的初始化方法
 * @author asus
 *
 */
public class HelloServerInitializer extends ChannelInitializer<SocketChannel>{

	@Override
	protected void initChannel(SocketChannel channel) throws Exception {
		
		//channel擷取相應的管道
		ChannelPipeline pipeline = channel.pipeline();
		
		//為管道增加相應的handler,可了解為是攔截器,或者監聽器,監聽用戶端建立連接配接後的資訊
		//當請求到服務端,我們需要對寫出到用戶端的資料做編碼處理
		pipeline.addLast("httpCode",new HttpServerCodec());
		
		//添加自定義助手類,可添加多個
		pipeline.addLast("myHandler",new MyHandler());
		
	}

}

           
package com.congge.sort.netty.day1;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;

/**
 * 自定義助手類
 * @author asus
 *
 */
public class MyHandler extends SimpleChannelInboundHandler<HttpObject>{

	@Override
	protected void messageReceived(ChannelHandlerContext context, HttpObject msg) 
			throws Exception {
		
		//通過context的上下文擷取channel
		Channel channel = context.channel();
		
		if(msg instanceof HttpRequest){
			//擷取請求位址
			System.out.println(channel.remoteAddress());
		}
		
		//自定義相應用戶端資訊
		ByteBuf content = Unpooled.copiedBuffer("hello neyy <<<>>>",CharsetUtil.UTF_8);
		
		//侯建httpResponse對象
		FullHttpResponse response = new DefaultFullHttpResponse(
				HttpVersion.HTTP_1_1, 
				HttpResponseStatus.OK,
				content);
		
		//設定response對象的頭資訊
		response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
		response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
		
		//将資料刷到用戶端
		context.writeAndFlush(response);
		
	}

}