先跑demo后,了解基本功能,后看源码。
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.73.Finalversion>
dependency>
客户端:
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
//客户端事件循环组 默认线程数为 cpu-core*2
NioEventLoopGroup group = new NioEventLoopGroup(1);
try {
Bootstrap bootstrap = new Bootstrap();
//设置bootstrap参数
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler());
}
});
System.out.println("netty client start...");
ChannelFuture cf = bootstrap.connect("127.0.0.1", 9090);
//启动客户端连接服务器
cf.channel().closeFuture().sync();
}finally {
group.shutdownGracefully();
}
}
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
/**
* 建连完成
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = Unpooled.copiedBuffer("HelloServer".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("收到服务端的消息:" + buf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("数据流读取完毕");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("捕获异常"+cause);
}
}
服务端:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//创建管理连接的线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(2);
//创建处理业务逻辑的线程组
EventLoopGroup workerGroup = new NioEventLoopGroup(10);
try {
//创建服务器端的启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
//配置启动参数(两个线程组)
bootstrap.group(bossGroup,workerGroup)
//服务器通道的实现
.channel(NioServerSocketChannel.class)
//将瞬时无法处理的请求加入队列
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("netty server start...");
//启动服务器(绑定端口号) bind是异步操作 sync是等待异步绑定结果的同步操作
ChannelFuture cf = bootstrap.bind(9090).sync();
//等待服务器监听端口关闭,closeFuture是异步操作
//通过sync同步等待通道关闭处理完毕,这里会阻塞等待通道关闭完成,内部调用的是Object.wait()方法
cf.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 当客户端与服务端完成连接时出发
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端连接通道建立完成");
}
/**
* 读取客户端发送的数据
*
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("收到客户端的消息:" + buf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("数据读取完毕");
ByteBuf buf = Unpooled.copiedBuffer("HelloClient".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
/**
* 异常捕获
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("捕获异常"+cause);
}
}
Classic Service Designs 经典服务设计
特点
Basic Reactor Design 响应式事件驱动模型
Using Multiple Reactors 多线程主从响应式事件驱动模型
网传的netty线程模型非常形象,易于理解从客户端到服务端的线程执行过程,及服务端两组线程池锁起到的作用。
Bootstrap 客户端启动引导类,集成其他模块
ServerBootstrap 服务端启动引导类,集成其他模块功能
以服务端的引导类为例,集成如一下其他模块
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)//集成通道类
.option(ChannelOption.SO_BACKLOG,1024)//阻塞队列长度
.childHandler(new ChannelInitializer<SocketChannel>() {//pipeline管道
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder",new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast(new NettyChatServerHandler());
}
});
NioEventLoopGroup 用于管理NioEventLoop生命周期的服务组,相当于线程池。
//当不定义线程池中线程数量
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));//获取机器中核心数量*2
nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads//指定线程池中线程数量,或默认使用double*coreSize
children = new EventExecutor[nThreads];//创建线程池
new ThreadPerTaskExecutor(newDefaultThreadFactory());//线程工厂
绑定线程,支持异步提交任务,线程启动时,执行NioEventLoop 的 run方法
任务队列
Selector
多路复用器,一个Selector可以监听多个连接的Channel事件。
selectionKey.OP_READ/OP_WRITE事件
Netty的网络通讯组件,能够执行网络IO操作,为用户提供:
NioSocketChannel,异步的客户端 TCP Socket 连接。
2 NioServerSocketChannel,异步的服务器端 TCP Socket 连接。
3 NioDatagramChannel,异步的 UDP 连接。
4 NioSctpChannel,异步的客户端 Sctp 连接。
5 NioSctpServerChannel,异步的 Sctp 服务器端连接。
6 这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件 IO
ChannelHandler是接口,处理IO事件或拦截IO操作,并将其转发到ChannelPipline,进行业务处理链中执行下一处理程序。
ChannelPipline中存在2种Handler,Inbound、Outbound分别处理入方向和出方向的任务。
实现方法可见于:
//普通抽象类处理器
ChannelInboundHandler //用于处理入站 I/O 事件。
ChannelOutboundHandler //用于处理出站 I/O 操作。
//适配器类
ChannelInboundHandlerAdapter //用于处理入站 I/O 事件。
ChannelOutboundHandlerAdapter //用于处理出站 I/O 操作。
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Scanner;
public class NettyChatClient {
public static void main(String[] args) throws InterruptedException {
//客户端事件循环组 默认线程数为 cpu-core*2
NioEventLoopGroup group = new NioEventLoopGroup(1);
try {
Bootstrap bootstrap = new Bootstrap();
//设置bootstrap参数
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new NettyChatClientHandler());
}
});
ChannelFuture cf = bootstrap.connect("127.0.0.1", 9090).sync();
Channel channel = cf.channel();
System.out.println("=========="+ channel.localAddress()+"==========");
//客户端输入内容
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
String msg = scanner.nextLine();
//通过channel发送到服务器
channel.writeAndFlush(msg);
}
//启动客户端连接服务器
cf.channel().closeFuture().sync();
}finally {
group.shutdownGracefully();
}
}
}
//业务逻辑之客户端pipeline管道
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class NettyChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg.trim());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("捕获异常"+cause);
}
}
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class NettyChatServer {
public static void main(String[] args) throws InterruptedException {
//设置2组线程,分别用于接收请求和处理业务
NioEventLoopGroup bossGroup = new NioEventLoopGroup(2);
NioEventLoopGroup workerGroup = new NioEventLoopGroup(8);
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
//阻塞队列
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder",new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast(new NettyChatServerHandler());
}
});
System.out.println("聊天室已开启...");
//启动服务器(绑定端口号) bind是异步操作 sync是等待异步绑定结果的同步操作
ChannelFuture cf = bootstrap.bind(9090).sync();
//等待服务器监听端口关闭,closeFuture是异步操作
//通过sync同步等待通道关闭处理完毕,这里会阻塞等待通道关闭完成,内部调用的是Object.wait()方法
cf.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
//业务逻辑之服务端pipeline管道
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
public class NettyChatServerHandler extends SimpleChannelInboundHandler<String> {
private List<Channel> channelList;//netty会创建多例NettyChatServerHandler对象 ---> 需要调整为单利
//GlobalEventExecutor.INSTANCE 是全局的事件执行器,是一个单例
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//时间戳
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public NettyChatServerHandler(){
this.channelList = new ArrayList<>();
}
/**
* 当客户端与服务端完成连接时出发
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channelGroup.writeAndFlush("[客户端]"+channel.remoteAddress() + "上线了"+ sdf.format(new java.util.Date()) + "\n");
channelGroup.add(channel);
System.out.println(ctx.pipeline().channel().remoteAddress() + "上线了");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Channel channel = ctx.channel();
channelGroup.forEach(ch -> {
if (channel != ch){
ch.writeAndFlush("[客户端] "+channel.remoteAddress()+ "发送了消息:"+msg);
}else {
ch.writeAndFlush("[自己] 发送了消息:"+msg);
}
});
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
channelGroup.writeAndFlush("[客户端] "+ ctx.channel().remoteAddress() + "退出群聊");
}
/**
* 异常捕获
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("捕获异常"+cause);
}
}
附件:
http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf [doug lea]