**用netty寫個hello world**
要成爲架構師,瞭解一些框架內部的通信機制的必不可少的,所以最近學習了netty的相關知識,本次就先用netty寫個“hello world”來體驗下。
Netty或多或少應該都聽說過,我這裏就不用在贅述他的背景,netty的內部通訊模式有多種:
NIO io.netty.channel.socket.nio 使用java.nio.channels 包作爲基礎——基於選擇器的方式
Epoll io.netty.channel.epoll 由 JNI 驅動的 epoll()和非阻塞 IO。這個傳輸支持只有在Linux 上可用的多種特性,按照作者的話來說在linux下使用這種通訊模式能獲得更高的性能,能減少GC次數.將NioEventLoopGroup替換爲EpollEventLoopGroup , 並且將NioServerSocketChannel.class 替換爲EpollServerSocketChannel.class 即可。
OIO io.netty.channel.socket.oio 使用java.net 包作爲基礎——使用阻塞流(相當於BIO)
Local io.netty.channel.local 可以在VM 內部通過管道進行通信的本地傳輸
Embedded io.netty.channel.embedded Embedded 傳輸,允許使用ChannelHandler 而又不需要一個真正的基於網絡的傳輸。在測試ChannelHandler 實現時非常有用
這裏主要是用NIO的方式,NIO的原理會單獨分享和實現,netty的幫助程序員封裝起了NIO的使得代碼編寫起來會比較簡單,瞭解一個新的東西要先從大的方向去了解,netty的核心組件:
1.channel java的channel是網絡io和文件io通用,netty的channel只包含網絡通訊的功能。
netty的channel並沒有繼承java的channel;
2、EventLoop、EventLoopGroup
3.事件和ChannelHandler
應用程序開發人員的角度來看,Netty 的主要組件是ChannelHandler,
它充當了所有處理入站和出站數據的應用程序邏輯的地方。
Netty 以適配器類的形式提供了大量默認的ChannelHandler 實現,
幫我們簡化應用程序處理邏輯的開發過程。
開始:
先寫客戶端:
public class NettyClient {
private ChannelFuture f;
public void connect(String ip,Integer port){
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
//程序啓動器
Bootstrap b =new Bootstrap();
try {
b.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyClientHandler());
}
});
f = b.connect(ip, port).sync();/*連接到遠程節點,阻塞等待直到連接完成*/
f.channel().closeFuture().sync();/*阻塞,直到channel關閉*/
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
int port =8080;
NettyClient client = new NettyClient();
client.connect("localhost",port);
}
Netty是 Reactor模型的是基於事件驅動的,所以可以看到具體業務是由handler來處理, ChannelPipeline 提供了ChannelHandler 鏈的容器,並定義了用於在該鏈上傳播入站和出站事件流的API。
public class NettyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
protected NettyClientHandler() {
super();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
// System.out.println("hello , i am client");
ByteBuf buf= Unpooled.copiedBuffer("hello server",CharsetUtil.UTF_8);
ctx.writeAndFlush(buf);
}
/* @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
ByteBuf buf= (ByteBuf) msg;
System.out.println("accept msg:" + buf.toString(CharsetUtil.UTF_8));
}*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("client accept:"+byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
服務端:
public void bind(Integer port){
EventLoopGroup bossGroup=new NioEventLoopGroup();//selector[]
EventLoopGroup workGroup=new NioEventLoopGroup();
ServerBootstrap b =new ServerBootstrap();
try {
b.group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyServerOutBandHandler());
socketChannel.pipeline().addLast(new NettyServerInBandHandler());
}
});
ChannelFuture f =b.bind(port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
Integer port =8080;
NettyServer nettyServer= new NettyServer();
nettyServer.bind(port);
}
ChannelPipeline是有入棧出站順序的,通常把出站的放在前面,因爲有執行順序會先執行出站的讀,可以做檢測過濾相關操作
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("server accept:"+byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
System.out.println("讀完");
ctx.writeAndFlush(Unpooled.copiedBuffer("hello client 3",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
定義了一個出站的handler和一個入棧的
public class NettyServerOutBandHandler extends ChannelOutboundHandlerAdapter {
public NettyServerOutBandHandler() {
super();
}
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
super.connect(ctx, remoteAddress, localAddress, promise);
System.out.println("連接成功!");
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
super.read(ctx);
// System.out.println(ctx.read());
// ctx.writeAndFlush(Unpooled.copiedBuffer("hello client1",CharsetUtil.UTF_8));
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
super.write(ctx, msg, promise);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello client2",CharsetUtil.UTF_8));
}
}
運行結果,先啓動服務端,後啓動客戶端