在學(xué)習(xí)Netty之前镀赌,建議首先學(xué)習(xí)一個(gè)NIO宏怔,對(duì)關(guān)鍵的NIO組件有一個(gè)清醒認(rèn)識(shí)
總覽
- Bootstrap or ServerBootstrap
- EventLoop
- EventLoopGroup
- ChannelPipeline
- Future or ChannelFuture
- ChannelInitializer
- ChannelHandler
- ByteToMessageDecoder
- MessageToByteEncoder
ServerBootstrap
一個(gè)Netty應(yīng)用通常由一個(gè)Bootstrap開始踪少,它主要作用是配置整個(gè)Netty程序,串聯(lián)起各個(gè)組件刻剥。
EventLoop
一個(gè)EventLoop可以為多個(gè)Channel服務(wù)驰后。 EventLoopGroup會(huì)包含多個(gè)EventLoop
ChannelPipeline,ChannelHandler
從PipeLine這個(gè)單詞中可以看出來,是一個(gè)管道懊烤,處理連接梯醒。我們的業(yè)務(wù)代碼handler一般都是放在這個(gè)管道中的
那么疑問來了,這個(gè)管道中的處理順序是什么樣呢奸晴?
ChannelPipeline cp = channel.pipeline();
cp.addLast("encoder", new HttpResponseEncoder());//1.負(fù)責(zé)輸出
cp.addLast("decoder", new HttpRequestDecoder());//2.負(fù)責(zé)把客戶端的數(shù)據(jù)解碼
cp.addLast("handler", new HttpDispatchServerHandler());//3.自定義的業(yè)務(wù)處理器
按照我們執(zhí)行順序肯定不是根據(jù)添加順序來處理的冤馏,應(yīng)該是:2,把客戶端的數(shù)據(jù)解碼->3.對(duì)解碼數(shù)據(jù)處理->1.加密返回給客戶端。
那么 Netty
是怎么處理的呢寄啼?
ChannelHandler有兩個(gè)子類ChannelInboundHandler和ChannelOutboundHandler逮光,這兩個(gè)類對(duì)應(yīng)了兩個(gè)數(shù)據(jù)流向代箭,如果數(shù)據(jù)是從外部流入我們的應(yīng)用程序,我們就看做是inbound涕刚,相反便是outbound
ChannelInitializer
顧名思義,這個(gè)就是channel初始化的容器嗡综,在這個(gè)里面設(shè)置處理器
ServerBootstrap bootstrap = new ServerBootstrap();
bossGroup = new NioEventLoopGroup();//負(fù)責(zé)綁定channel到selector
workerGroup = new NioEventLoopGroup();//負(fù)責(zé)從selector中讀取事件
bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.localAddress(6969).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.childOption(ChannelOption.SO_KEEPALIVE, true).childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline cp = channel.pipeline();
cp.addLast("idleStateHandler", new IdleStateHandler(5, 5, 5, TimeUnit.SECONDS));
cp.addLast("decoder", new HttpRequestDecoder());
cp.addLast("encoder", new HttpResponseEncoder());
cp.addLast("aggregator", new HttpObjectAggregator(1048576));
cp.addLast("deflater", new HttpContentCompressor());
cp.addLast("handler", new HttpDispatchServerHandler());
cp.addLast("out", new AcceptorIdleStateTrigger());
}
}).option(ChannelOption.SO_BACKLOG, 128);
try {
channel = bootstrap.bind().awaitUninterruptibly().channel();
showBanner(6969);
} catch (Exception ie) {
throw new RuntimeException(ie);
}
ChannelFuture
Netty中的連接都可以是異步的,但是也可以設(shè)置為非異步
ChannelFuture
Channel
每個(gè)操作都會(huì)返回一個(gè) ChannelFutrue
因?yàn)槭钱惒降亩拍晕覀優(yōu)槊總€(gè)異步的結(jié)果极景,添加一個(gè)監(jiān)聽,比如:
# 當(dāng)完成關(guān)閉動(dòng)作,就執(zhí)行監(jiān)聽器內(nèi)容
f = f.channel().closeFuture().await();
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("success complete!!ok!!");
}
});
當(dāng)然還有一種方法驾茴, 就是await()盼樟,此返回會(huì)等待上一個(gè)操作完成,在進(jìn)行下一個(gè)操作锈至。但是推薦使用第一種晨缴。
ByteToMessageDecoder
解密器,可以自定義協(xié)議峡捡,通過集成改接口击碗,重寫 decode
方法把二進(jìn)制,轉(zhuǎn)換為我們系統(tǒng)可以處理的對(duì)象
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
* 把字節(jié)轉(zhuǎn)換為int
* 繼承抽象類ByteToMessageDecoder實(shí)現(xiàn)解碼器
*/
public class ByteToIntegerDecoder extends ByteToMessageDecoder {
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) throws Exception {
if (in.readableBytes() >= 4) { // Check if there are at least 4 bytes readable
int n = in.readInt();
System.err.println("ByteToIntegerDecoder decode msg is " + n);
out.add(n); //Read integer from inbound ByteBuf, add to the List of decodec messages
}
}
}
編碼器
將我們系統(tǒng)處理完的信息们拙,編碼成稍途,二進(jìn)制,傳出砚婆,給調(diào)用者
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class IntegerToByteEncoder extends MessageToByteEncoder<Integer> {
@Override
public void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out)
throws Exception {
System.err.println("IntegerToByteEncoder encode msg is " + msg);
out.writeInt(msg);
}
}
解碼后的數(shù)據(jù)怎么使用
對(duì)于加密后的數(shù)據(jù)械拍,可以直接強(qiáng)制轉(zhuǎn)換為我們解碼的對(duì)象
public class BusinessHandler extends ChannelInboundHandlerAdapter {
private Logger logger = LoggerFactory.getLogger(BusinessHandler.class);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//因?yàn)槲覀兊慕獯a其中指定是Int類型,所以我們就可以装盯,強(qiáng)制轉(zhuǎn)換為Int殊者,這里為了好理解,假如我們的解碼器验夯,中是轉(zhuǎn)換了Person,那么在我們的處理器中就摔刁,可以強(qiáng)制換換為Person
Person person = (Person) msg;
logger.info("BusinessHandler read msg from client :" + person);
}