上一篇粗略的介绍了一下netty,本篇将详细介绍Netty的服务器的启动过程。

ServerBootstrap

看过上篇事例的人,可以知道ServerBootstrap是Netty服务端启动中扮演着一个重要的角色。

它是Netty提供的一个服务端引导类,继承自AbstractBootstrap

ServerBootstrap主要包括两部分:bossGroupworkerGroup。其中bossGroup主要用于绑定端口,接收来自客户端的请求,接收到请求之后,就会把这些请求交给workGroup去处理。就像现实中的老板和员工一样,自己开个公司(绑定端口),到外面接活(接收请求),使唤员工干活(让worker去处理)。

端口绑定

端口绑定之前,会先check引导类(ServerBootstrap)的bossGroup和workerGroup有没有设置,之后再调用doBind。

    private ChannelFuture doBind(final SocketAddress localAddress) {
// 初始化并注册一个channel,并将chanelFuture返回
final ChannelFuture regFuture = initAndRegister();
// 得到实际的channel(初始化和注册的动作可能尚未完成)
final Channel channel = regFuture.channel();
// 发生异常时,直接返回
if (regFuture.cause() != null) {
return regFuture;
}
// 当到这chanel相关处理已经完成时
if (regFuture.isDone()) {
// 到这可以确定channel已经注册成功
ChannelPromise promise = channel.newPromise();
// 进行相关的绑定操作
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// 注册一般到这就已经完成,到以防万一
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
// 添加一个监听器
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
// 修改注册状态为成功(当注册成功时不在使用全局的executor,使用channel自己的,详见 https://github.com/netty/netty/issues/2586)
promise.registered();
// 进行相关的绑定操作
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}

上面的代码主要有两部分:初始化并注册一个channel和绑定端口。

初始化并注册一个channel

    final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 生产各新channel
channel = channelFactory.newChannel();
// 初始化channel
init(channel);
} catch (Throwable t) {
if (channel != null) {
// 注册失败时强制关闭
channel.unsafe().closeForcibly();
// 由于channel尚未注册好,强制使用GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
// 注册channel
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}

channel的初始化方法:

    void init(Channel channel) throws Exception {
// 获取bossChannel的可选项Map
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
// 获取bossChannel的属性Map
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e : attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
ChannelPipeline p = channel.pipeline();
// 设置worker的相关属性
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 添加handler到pipeline
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
// 通过EventLoop将ServerBootstrapAcceptor到pipeline中,保证它是最后一个handler
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}

channel的注册方法,最终是调用doRegister,不同的channel有所不同,下面以Nio为例:

    protected void doRegister() throws Exception {
boolean selected = false;
for (; ; ) {
try {
// 直接调用java的提供的Channel的注册方法
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}

绑定端口

最终调用的是NioServerSocketChannel的doBind方法。

    protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}

到这就完成了netty服务端的整个启动过程。

文中帖的代码注释全在:https://github.com/KAMIJYOUDOUMA/nettyForAnalysis.git , 有兴趣的童鞋可以关注一下。


本篇到此结束,如果读完觉得有收获的话,欢迎点赞、关注、加公众号【贰级天災】,查阅更多精彩历史!!!

Netty源码分析(二):服务端启动的更多相关文章

  1. Netty源码分析之服务端启动过程

    一.首先来看一段服务端的示例代码: public class NettyTestServer { public void bind(int port) throws Exception{ EventL ...

  2. Netty源码分析之服务端启动

    Netty服务端启动代码: public final class EchoServer { static final int PORT = Integer.parseInt(System.getPro ...

  3. zookeeper源码分析之一服务端启动过程

    zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...

  4. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  5. Netty 4源码解析:服务端启动

    Netty 4源码解析:服务端启动 1.基础知识 1.1 Netty 4示例 因为Netty 5还处于测试版,所以选择了目前比较稳定的Netty 4作为学习对象.而且5.0的变化也不像4.0这么大,好 ...

  6. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  7. Nacos(二)源码分析Nacos服务端注册示例流程

    上回我们讲解了客户端配置好nacos后,是如何进行注册到服务器的,那我们今天来讲解一下服务器端接收到注册实例请求后会做怎么样的处理. 首先还是把博主画的源码分析图例发一下,让大家对整个流程有一个大概的 ...

  8. 4. 源码分析---SOFARPC服务端暴露

    服务端的示例 我们首先贴上我们的服务端的示例: public static void main(String[] args) { ServerConfig serverConfig = new Ser ...

  9. Netty源码分析之服务启动

    本节主要分析server的启动过程. Netty是基于Nio实现的,所以也离不开selector.serverSocketChannel.socketChannel和selectKey等,只不过Net ...

  10. TeamTalk源码分析之服务端描述

    TTServer(TeamTalk服务器端)主要包含了以下几种服务器: LoginServer (C++): 登录服务器,分配一个负载小的MsgServer给客户端使用 MsgServer (C++) ...

随机推荐

  1. app微信支付-java服务端接口 支付-查询-退款

    个人不怎么看得懂微信的文档,看了很多前辈的写法,终于调通了,在这里做一下记录. 首先来定义各种处理类(微信支付不需要特殊jar包,很多处理需要自己封装,当然也可以自己写完打个jar包) 参数要用jdo ...

  2. APP界面设计 大概总结

    APP界面设计大概总结 首先,你得有个Android Studio 其次,你得学会有耐心的对它 最后,要适应它习惯它了解它 来看看APP的基本步骤 先有资源 再是界面布局 下来承载布局Activity ...

  3. 别人的Linux私房菜(21)基础系统设置与备份策略

    网络设置,手动设置IP,DHCP自动获取. 以太网协议开发出来的网卡ethN,N为数字. CentOS7对网卡命名的规则:eno代表由主板BIOS内建立的网卡,ens1由主板BIOS内建的PCI-E界 ...

  4. sklearn svm基本使用

    SVM基本使用 SVM在解决分类问题具有良好的效果,出名的软件包有libsvm(支持多种核函数),liblinear.此外python机器学习库scikit-learn也有svm相关算法,sklear ...

  5. 有return的情况下try catch finally的执行顺序(转)

    结论:1.不管有没有出现异常,finally块中代码都会执行:2.当try和catch中有return时,finally仍然会执行:3.finally是在return后面的表达式运算后执行的(此时并没 ...

  6. Docker优势以及与传统虚拟机对比(1)

    docker优势 1.更快速地交付和部署: 2.更高的虚拟化(不需要额外的hypervisor支持,是内核级的虚拟化,实现更高的性能呢和效率): 3.更轻松的迁移和扩展: 4.更简单的管理  与传统的 ...

  7. pageHelper的使用步骤,省略sql语句中的limit

    1.引架包.注意版本问题 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId& ...

  8. 第二次scrum

    scrum说明 在第一次已有的基础上,进行了具体的实现.完成了具体的界面设计,还有各个栏目,如:发帖,搜索,禁言等. 类图 依次是外观模式图,发帖图,禁言图. 外观模式图 发帖图 禁言图 团队成员 潘 ...

  9. python之路(九)-函数装饰器

    装饰器 某公司的基础业务平台如下: def f1(): print('这是f1业务平台') def f2(): print('这是f2业务平台') def f3(): print('这是f3业务平台' ...

  10. 数独计算(C#)

    计算零到多个可能的数独结果,并打印到Console中. 调用方法 MainController mc = new MainController(); mc.Do(); 输入 数独数据 类型为int[, ...