IDE
服务端netty启动代码
/**
* 开启及服务线程
*/
public void start() {
// 从配置文件中(application.yml)获取服务端监听端口号
int port = nettyConfig.getPort();
serverBootstrap.group(boss, work)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO));
try {
//设置事件处理
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("ping", new IdleStateHandler(60, 20, 600, TimeUnit.SECONDS));
pipeline.addLast(new StringEncoder(Charset.forName("UTF-8")));
pipeline.addLast(new StringDecoder(Charset.forName("UTF-8")));
pipeline.addLast(channelHandlerAdapter);
}
});
LOGGER.info("netty服务器在[{}]端口启动监听", port);
ChannelFuture f = serverBootstrap.bind(port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
LOGGER.info("[出现异常] 释放资源");
boss.shutdownGracefully();
work.shutdownGracefully();
}
DatagramChannel channel = new NioDatagramChannel();
}
serverBootstrap.group(boss, work)中的boss ,work 都是NioEventLoopGroup对象,依据官方的文档只要设置就好 后面解释
/**
* 创建bootstrap
*/
ServerBootstrap serverBootstrap = new ServerBootstrap();
/**
* BOSS
*/
EventLoopGroup boss = new NioEventLoopGroup();
/**
* Worker
*/
EventLoopGroup work = new NioEventLoopGroup();
/**
* 通道适配器
*/
@Resource
private ServerChannelHandlerAdapter channelHandlerAdapter;
/**
* NETT服务器配置类
*/
@Resource
private NettyConfig nettyConfig;
ServerChannelHandlerAdapter 是自定义的用户处理消息类 继承于ChannelHandlerAdapter 用户可以从这个adapter的方法做处理 channelActive:连接成功 channelRead是获取管道信息...
这里贴上自己处理的adapter 增加了群发功能
package com.ruoyi.framework.netty;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@ChannelHandler.Sharable
public class ServerChannelHandlerAdapter extends ChannelHandlerAdapter {
/**
* 日志处理
*/
private Logger logger = LoggerFactory.getLogger(ServerChannelHandlerAdapter.class);
/**
* 注入请求分排器
*/
@Resource
private RequestDispatcher dispatcher;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
logger.info("channelActive");
ctx.writeAndFlush(Unpooled.copiedBuffer("Heartbeat", CharsetUtil.UTF_8));
String uuid = ctx.channel().id().asLongText();
logger.info("uuid" + uuid);
GroupChannelMap.getInstance().addUUIDChannel(uuid, ctx.channel());
logger.info("uuid hashCode" + GroupChannelMap.getInstance().getChannelMap().hashCode());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
GroupChannelMap.getInstance().removeUUIDChannel(ctx.channel().id().asLongText());
}
@Override
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
super.disconnect(ctx, promise);
logger.info("disconnect");
GroupChannelMap.getInstance().removeUUIDChannel(ctx.channel().id().asLongText());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
logger.info("channelRead 收到数据:" + msg);
/* MethodInvokeMeta invokeMeta = (MethodInvokeMeta) msg;
// 屏蔽toString()方法
if (invokeMeta.getMethodName().endsWith("toString()")
&& !"class java.lang.String".equals(invokeMeta.getReturnType().toString()))
logger.info("客户端传入参数 :{},返回值:{}",
invokeMeta.getArgs(), invokeMeta.getReturnType());
dispatcher.dispatcher(ctx, invokeMeta);*/
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.WRITER_IDLE) {
//时间内没有写
ctx.writeAndFlush(Unpooled.copiedBuffer("Heartbeat", CharsetUtil.UTF_8));
} else if (event.state() == IdleState.READER_IDLE) { //时间内没有读
logger.info("READER_IDLE"+ctx.channel().id().asLongText());
GroupChannelMap.getInstance().removeUUIDChannel(ctx.channel().id().asLongText());
} else if (event.state() == IdleState.ALL_IDLE) { //时间内没有写和读
logger.info("ALL_IDLE");
GroupChannelMap.getInstance().removeUUIDChannel(ctx.channel().id().asLongText());
}
}
}
}
源码跟进
serverBootstrap.bind(port).sync()的bind方法可进入到
AbstractBootstrap
public ChannelFuture bind(int inetPort) {
return this.bind(new InetSocketAddress(inetPort));
}
public ChannelFuture bind(SocketAddress localAddress) {
this.validate();
if (localAddress == null) {
throw new NullPointerException("localAddress");
} else {
return this.doBind(localAddress);
}
}
此处调用方法validate 起到验证作用,校验bind之前的参数是否设置有误
public B validate() {
if (this.group == null) {
throw new IllegalStateException("group not set");
} else if (this.channelFactory == null) {
throw new IllegalStateException("channel or channelFactory not set");
} else {
return this;
}
}
之后我们再进入dobind 方法 有initAndRegister()和doBind0() 进入initAndRegister 会发现 在这里创建了关键的管道channel 并随后调用init 初始化 最后再注册(group注册到channel中 后面解释)
final ChannelFuture initAndRegister() {
Channel channel = this.channelFactory().newChannel();
try {
this.init(channel);
} catch (Throwable var3) {
channel.unsafe().closeForcibly();
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
ChannelFuture regFuture = this.group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
下面看看Channel是如何被创建的,依据方法名可知是通过工厂类创建的,再该类发现 channelFactory赋值的时候是在 AbstractBootstrap
的构造函数中或 channel方法中创建并赋值 看下面代码。 可我初始化代码只调用了后者,这里创建的是个反射的工厂类
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
} else {
return this.channelFactory((io.netty.channel.ChannelFactory)(new ReflectiveChannelFactory(channelClass)));
}
}
/** @deprecated */
@Deprecated
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
} else if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
} else {
this.channelFactory = channelFactory;
return this;
}
}
进入工厂类 发现是通过反射创建的Channel,而这个clazz 正是我们刚开始调用的 NioServerSocketChannel.class 。结果一圈发现Channel早已设置好,就等bind方法一触发。
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
} else {
this.clazz = clazz;
}
}
public T newChannel() {
try {
return (Channel)this.clazz.newInstance();
} catch (Throwable var2) {
throw new ChannelException("Unable to create Channel from class " + this.clazz, var2);
}
}
NioServerSocketChannel的创建过程
继续追踪channel是如何创建的 附带的又创建了什么 ?他们又有什么作用?
首先进入类 代码
依据3个构造方法可以知道,由provider创建了ServerSocketChannel provider 暂且不理睬,还有一个channelConfige也被创建了
依据类名 推测得知这个是关于channel的配置清单。
继续跟踪super方法 会进入到messageChannel 和abstractChannel
public class NioServerSocketChannel extends AbstractNioMessageChannel implements ServerSocketChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false);
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
private final ServerSocketChannelConfig config;
private static java.nio.channels.ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException var2) {
throw new ChannelException("Failed to open a server socket.", var2);
}
}
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(SelectorProvider provider) {
this(newSocket(provider));
}
public NioServerSocketChannel(java.nio.channels.ServerSocketChannel channel) {
super((Channel)null, channel, 16);
this.config = new NioServerSocketChannel.NioServerSocketChannelConfig(this, this.javaChannel().socket());
}
}
AbstractChannel中的构造方法中 发现生成了 id、unsafe、pipeline。id 是channel的唯一标识,unsafe进入接口上层发现是 管理channel的生命周期。发现其内部方法有(注册,绑定,连接,断开,开始读写....)而pipleline 可以理解为channel管道设置的关卡, 方便用户包装流体 如加密解密,自定义协议,编码格式.....
protected AbstractChannel(Channel parent) {
this.parent = parent;
this.id = DefaultChannelId.newInstance();
this.unsafe = this.newUnsafe();
this.pipeline = new DefaultChannelPipeline(this);
}
至此 ,我们总算把一个服务端channel创建完毕了,顺带把其他重大组件也给番出来了
- channel
- channelConfig
- channelid
- unsafe
- pipeline
- channelhandler
在newchannel 之后 进入initandregister的init方法
init方法
大致可以理解为 channel的属性设置。options,attrrs,以及 加入一个ServerBootstrapAcceptor。
options,atrtrs 具体干啥 暂且放过它,反正也被扔进config内部去了。void initChannel 中新增加一个连接处理器,再往内部注入一个什么什么接收处理器....也暂且放过他。
void init(Channel channel) throws Exception {
Map<ChannelOption<?>, Object> options = this.options();
synchronized(options) {
channel.config().setOptions(options);
}
Map<AttributeKey<?>, Object> attrs = this.attrs();
synchronized(attrs) {
Iterator i$ = attrs.entrySet().iterator();
while(true) {
if (!i$.hasNext()) {
break;
}
Entry<AttributeKey<?>, Object> e = (Entry)i$.next();
AttributeKey<Object> key = (AttributeKey)e.getKey();
channel.attr(key).set(e.getValue());
}
}
ChannelPipeline p = channel.pipeline();
if (this.handler() != null) {
p.addLast(new ChannelHandler[]{this.handler()});
}
final EventLoopGroup currentChildGroup = this.childGroup;
final ChannelHandler currentChildHandler = this.childHandler;
final Entry[] currentChildOptions;
synchronized(this.childOptions) {
currentChildOptions = (Entry[])this.childOptions.entrySet().toArray(newOptionArray(this.childOptions.size()));
}
final Entry[] currentChildAttrs;
synchronized(this.childAttrs) {
currentChildAttrs = (Entry[])this.childAttrs.entrySet().toArray(newAttrArray(this.childAttrs.size()));
}
p.addLast(new ChannelHandler[]{new ChannelInitializer<Channel>() {
public void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ChannelHandler[]{new ServerBootstrap.ServerBootstrapAcceptor(currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
}
}});
}
register0()
再次进入到 initandRegister的 register方法中去 饶一圈我们再回来进入doBind0 就会发现开始调用jdk的方法绑定端口号了。
Channel channel = this.channelFactory().newChannel();
ChannelFuture regFuture = this.group().register(channel);
往上层走入EventLoopGroup 再往下实现走入 EmbeddedEventLoop中
public interface EventLoopGroup extends EventExecutorGroup {
EventLoop next();
ChannelFuture register(Channel var1);
ChannelFuture register(Channel var1, ChannelPromise var2);
}
public ChannelFuture register(Channel channel) {
return this.register(channel, new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(Channel channel, ChannelPromise promise) {
channel.unsafe().register(this, promise);
return promise;
}
突然发现又进入到了之前创建的channel 番出来的unsafe 进入到他的实现类去AbstractChanel
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
} else if (promise == null) {
throw new NullPointerException("promise");
} else if (AbstractChannel.this.isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
} else if (!AbstractChannel.this.isCompatible(eventLoop)) {
promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
} else {
if (AbstractChannel.this.eventLoop == null) {
AbstractChannel.this.eventLoop = AbstractChannel.this.new PausableChannelEventLoop(eventLoop);
} else {
AbstractChannel.this.eventLoop.unwrapped = eventLoop;
}
if (eventLoop.inEventLoop()) {
this.register0(promise);
} else {
try {
eventLoop.execute(new OneTimeTask() {
public void run() {
AbstractUnsafe.this.register0(promise);
}
});
} catch (Throwable var4) {
AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var4);
}
}
}
}
进入到register0中一路跟下去 会发现最终会回调到 用户自定义的adapter 监听处理事件中去 这期间 在register0方法中开始 调用 isActive()方法,这个是jdk的socket方法了。判断是否channel已被激活(socket 绑定address)。
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
return;
}
boolean firstRegistration = this.neverRegistered;
AbstractChannel.this.doRegister();
this.neverRegistered = false;
AbstractChannel.this.registered = true;
AbstractChannel.this.eventLoop.acceptNewTasks();
this.safeSetSuccess(promise);
AbstractChannel.this.pipeline.fireChannelRegistered();
if (firstRegistration && AbstractChannel.this.isActive()) {
AbstractChannel.this.pipeline.fireChannelActive();
}
} catch (Throwable var3) {
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var3);
}
}
isActive() 调用jdk 在NioServerSocketChannel类中
public boolean isActive() {
return this.javaChannel().socket().isBound();
}
doBind0()
继续跟进会进入到 abstractChannel 类 (可以用debug追溯) 调用doBind方法 开始调用jdk的socket绑定ip 成功之后再调用fireChannelActivie 即回调到我们自定义的adapter中去。此时整个channel 创建完毕 连接成功。
public final void bind(SocketAddress localAddress, ChannelPromise promise) {
if (promise.setUncancellable() && this.ensureOpen(promise)) {
if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.isRoot()) {
AbstractChannel.logger.warn("A non-root user can't receive a broadcast packet if the socket is not bound to a wildcard address; binding to a non-wildcard address (" + localAddress + ") anyway as requested.");
}
boolean wasActive = AbstractChannel.this.isActive();
try {
AbstractChannel.this.doBind(localAddress);
} catch (Throwable var5) {
this.safeSetFailure(promise, var5);
this.closeIfClosed();
return;
}
if (!wasActive && AbstractChannel.this.isActive()) {
this.invokeLater(new OneTimeTask() {
public void run() {
AbstractChannel.this.pipeline.fireChannelActive();
}
});
}
this.safeSetSuccess(promise);
}
}
protected void doBind(SocketAddress localAddress) throws Exception {
this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
}
本文详细解析了Netty服务端的启动流程,包括从配置文件读取端口、设置服务线程组、初始化通道处理器、绑定端口直至监听状态的全过程。深入探讨了NioEventLoopGroup的作用,ServerBootstrap的配置,以及Channel、Pipeline和Handler的联动机制。
1281

被折叠的 条评论
为什么被折叠?



