sofa-bolt源码阅读(1)-服务端的启动
Bolt服务器的核心类是RpcServer,启动的时候调用父类AbstractRemotingServer的startup方法。
com.alipay.remoting.AbstractRemotingServer#startup
@Override
public void startup() throws LifeCycleException {
super.startup();
try {
doInit();
logger.warn("Prepare to start server on port {} ", port);
if (doStart()) {
logger.warn("Server started on port {}", port);
} else {
logger.warn("Failed starting server on port {}", port);
throw new LifeCycleException("Failed starting server on port: " + port);
}
} catch (Throwable t) {
this.shutdown();// do stop to ensure close resources created during doInit()
throw new IllegalStateException("ERROR: Failed to start the Server!", t);
}
}
这里主要做了三件事
调用父类的startup()方法设置状态为启动
com.alipay.remoting.AbstractLifeCycle#startup
@Override
public void startup() throws LifeCycleException {
if (isStarted.compareAndSet(false, true)) {
return;
}
throw new LifeCycleException("this component has started");
}
调用实现类的doInit()进行实际的初始化工作
com.alipay.remoting.rpc.RpcServer#doInit @Override
protected void doInit() {
if (this.addressParser == null) {
this.addressParser = new RpcAddressParser();
}
if (this.switches().isOn(GlobalSwitch.SERVER_MANAGE_CONNECTION_SWITCH)) {
// in server side, do not care the connection service state, so use null instead of global switch
ConnectionSelectStrategy connectionSelectStrategy = new RandomSelectStrategy(null);
this.connectionManager = new DefaultServerConnectionManager(connectionSelectStrategy);
this.connectionManager.startup(); this.connectionEventHandler = new RpcConnectionEventHandler(switches());
this.connectionEventHandler.setConnectionManager(this.connectionManager);
this.connectionEventHandler.setConnectionEventListener(this.connectionEventListener);
} else {
this.connectionEventHandler = new ConnectionEventHandler(switches());
this.connectionEventHandler.setConnectionEventListener(this.connectionEventListener);
}
initRpcRemoting();
this.bootstrap = new ServerBootstrap();
this.bootstrap.group(bossGroup, workerGroup)
.channel(NettyEventLoopUtil.getServerSocketChannelClass())
.option(ChannelOption.SO_BACKLOG, ConfigManager.tcp_so_backlog())
.option(ChannelOption.SO_REUSEADDR, ConfigManager.tcp_so_reuseaddr())
.childOption(ChannelOption.TCP_NODELAY, ConfigManager.tcp_nodelay())
.childOption(ChannelOption.SO_KEEPALIVE, ConfigManager.tcp_so_keepalive()); // set write buffer water mark
initWriteBufferWaterMark(); // init byte buf allocator
if (ConfigManager.netty_buffer_pooled()) {
this.bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
} else {
this.bootstrap.option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT);
} // enable trigger mode for epoll if need
NettyEventLoopUtil.enableTriggeredMode(bootstrap); final boolean idleSwitch = ConfigManager.tcp_idle_switch();
final int idleTime = ConfigManager.tcp_server_idle();
final ChannelHandler serverIdleHandler = new ServerIdleHandler();
final RpcHandler rpcHandler = new RpcHandler(true, this.userProcessors);
this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override
protected void initChannel(SocketChannel channel) {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("decoder", codec.newDecoder());
pipeline.addLast("encoder", codec.newEncoder());
if (idleSwitch) {
pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, idleTime,
TimeUnit.MILLISECONDS));
pipeline.addLast("serverIdleHandler", serverIdleHandler);
}
pipeline.addLast("connectionEventHandler", connectionEventHandler);
pipeline.addLast("handler", rpcHandler);
createConnection(channel);
} /**
* create connection operation<br>
* <ul>
* <li>If flag manageConnection be true, use {@link DefaultConnectionManager} to add a new connection, meanwhile bind it with the channel.</li>
* <li>If flag manageConnection be false, just create a new connection and bind it with the channel.</li>
* </ul>
*/
private void createConnection(SocketChannel channel) {
Url url = addressParser.parse(RemotingUtil.parseRemoteAddress(channel));
if (switches().isOn(GlobalSwitch.SERVER_MANAGE_CONNECTION_SWITCH)) {
connectionManager.add(new Connection(channel, url), url.getUniqueKey());
} else {
new Connection(channel, url);
}
channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT);
}
});
}
这里的代码看似很复杂,其实主要是配置Netty服务器。Netty服务器的可配置选项通过ConfigManager来获取,Netty的业务处理在childHandler里面。
protected void initChannel(SocketChannel channel) {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("decoder", codec.newDecoder());
pipeline.addLast("encoder", codec.newEncoder());
if (idleSwitch) {
pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, idleTime,
TimeUnit.MILLISECONDS));
pipeline.addLast("serverIdleHandler", serverIdleHandler);
}
pipeline.addLast("connectionEventHandler", connectionEventHandler);
pipeline.addLast("handler", rpcHandler);
createConnection(channel);
}
初始化channel的方法里面有6个channelHandler
decoder 解码器
encoder 编码器
idleStateHandler
Netty自带的空闲处理器,用于触发IdleStateEvent。在这里配置了总空闲时间idleTime(默认值是90000),即idleTime时间内如果通道没有发生读写操作,将出发一个IdleStateEvent事件。
serverIdleHandler
配合idleStateHandler使用,用来处理IdleStateEvent。当触发该时间后,关闭客户端连接
@Override
public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
try {
logger.warn("Connection idle, close it from server side: {}",
RemotingUtil.parseRemoteAddress(ctx.channel()));
ctx.close();
} catch (Exception e) {
logger.warn("Exception caught when closing connection in ServerIdleHandler.", e);
}
} else {
super.userEventTriggered(ctx, evt);
}
}
connectionEventHandler
connect事件处理器,类型由枚举类ConnectionEventType定义
public enum ConnectionEventType {
CONNECT, CLOSE, EXCEPTION;
}
CONNECT
connect事件在RpcServer.createConnection()方法里触发了一次
CLOSE
close事件在连接断开时会触发
com.alipay.remoting.ConnectionEventHandler @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String remoteAddress = RemotingUtil.parseRemoteAddress(ctx.channel());
infoLog("Connection channel inactive: {}", remoteAddress);
super.channelInactive(ctx);
Attribute attr = ctx.channel().attr(Connection.CONNECTION);
if (null != attr) {
// add reconnect task
if (this.globalSwitch != null
&& this.globalSwitch.isOn(GlobalSwitch.CONN_RECONNECT_SWITCH)) {
Connection conn = (Connection) attr.get();
if (reconnectManager != null) {
reconnectManager.reconnect(conn.getUrl());
}
}
// trigger close connection event
onEvent((Connection) attr.get(), remoteAddress, ConnectionEventType.CLOSE);
}
}
EXCEPTION
exception异常事件在源代码里面没有触发的地方,应该是预留的。
无论是什么事件,最终都调用onEvent方法,转发给ConnectionEventListener,最终由用户自定义的ConnectionEventProcessor来处理具体逻辑
com.alipay.remoting.ConnectionEventHandler#onEvent private void onEvent(final Connection conn, final String remoteAddress,
final ConnectionEventType type) {
if (this.eventListener != null) {
this.eventExecutor.onEvent(new Runnable() {
@Override
public void run() {
ConnectionEventHandler.this.eventListener.onEvent(type, remoteAddress, conn);
}
});
}
} com.alipay.remoting.ConnectionEventListener#onEvent
public void onEvent(ConnectionEventType type, String remoteAddress, Connection connection) {
List<ConnectionEventProcessor> processorList = this.processors.get(type);
if (processorList != null) {
for (ConnectionEventProcessor processor : processorList) {
processor.onEvent(remoteAddress, connection);
}
}
}
handler
具体业务处理器,注册类为RpcHandler,调用流程图如下

对于客户端请求的处理交给UserProcessor, 可以调用RpcServer类的registerUserProcessor注册自定义的业务。
调用dostart启动服务器
com.alipay.remoting.rpc.RpcServer#doStart @Override
protected boolean doStart() throws InterruptedException {
this.channelFuture = this.bootstrap.bind(new InetSocketAddress(ip(), port())).sync();
return this.channelFuture.isSuccess();
}
sofa-bolt源码阅读(1)-服务端的启动的更多相关文章
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- Netty 4源码解析:服务端启动
Netty 4源码解析:服务端启动 1.基础知识 1.1 Netty 4示例 因为Netty 5还处于测试版,所以选择了目前比较稳定的Netty 4作为学习对象.而且5.0的变化也不像4.0这么大,好 ...
- Netty源码阅读(一) ServerBootstrap启动
Netty源码阅读(一) ServerBootstrap启动 转自我的Github Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速 ...
- TeamTalk源码分析之服务端描述
TTServer(TeamTalk服务器端)主要包含了以下几种服务器: LoginServer (C++): 登录服务器,分配一个负载小的MsgServer给客户端使用 MsgServer (C++) ...
- netty4.1.6源码2-------创建服务端的channel
1. netty在哪里调用jdk底层的socket去创建netty服务端的socket. 2. 在哪里accept连接. 服务端的启动: 1. 调用jdk底层的api去创建jdk的服务端的channe ...
- 4. 源码分析---SOFARPC服务端暴露
服务端的示例 我们首先贴上我们的服务端的示例: public static void main(String[] args) { ServerConfig serverConfig = new Ser ...
- Netty源码分析之服务端启动过程
一.首先来看一段服务端的示例代码: public class NettyTestServer { public void bind(int port) throws Exception{ EventL ...
- Spring Cloud系列(三):Eureka源码解析之服务端
一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-starter-netflix-eureka-ser ...
随机推荐
- dbgview 在windows 10 中关闭后再次打开时无法“capture kernel”
DbgView 是一个免费的用于抓取log 的工具,可以捕获并输出OutputDebugString()函数的输出,以及输出windows driver 中dbgprint 的log,对于window ...
- SaltStack schedule功能
1.确保计划中没有作业 salt.states.schedule.absent(name, **kwargs) 2.确保在计划中禁用的作业 salt.states.schedule.disabled( ...
- java连接外部接口获取数据工具类
package com.yqzj.util; import org.apache.log4j.LogManager;import org.apache.log4j.Logger; import jav ...
- Spring IOC 和AOP
Spring是什么? Spring是一个轻量级的IoC和AOP容器框架. IOC:IOC就是控制反转,控制反转指的是把创建对象和管理对象之间的依赖关系交给了IOC容器来管理.以前new对象由程序员来控 ...
- memcached_高可用
memcached高可用 一.magent 1.安装 cd /usr/local/mkdir ./magentcd ./magentwget -c http://memagent.googlecode ...
- php先响应后处理
php响应异步请求或者返回时效要求高的接口中,可以先响应输出,再执行逻辑处理保存数据等任务 ob_end_clean(); ob_start(); echo '{"data":&q ...
- java作业-----方法重载
满足方法重载的条件:1.方法名相同 2.参数类型不同,参数个数不同,参数类型的顺序不同. 同时,方法的返回值不作为方法重载的判断条件.
- 牛客-富豪凯匹配串(bitset)
题目传送门 sol1:用bitset来维护,其实感觉挺暴力的,不怎么会用bitset,借着这道题学习一下. bitset暴力维护 #include "bits/stdc++.h" ...
- 62)PHP,提示跳转
由于就是一个普通的页面展示,提示的样式,可以轻松定制! 语法: Header()后的代码会执行,因此在处理好跳转相关操作后,脚本也应该被DIE掉! 案例中实现跳转 封装一个可以完成跳转的方法,需要时, ...
- Zabbix - 修改默认的 Web 访问URL
背景 CentOS 7 64 Zabbix 4 LTS 使用RPM在线方式安装 修改过程 # 修改默认的访问路径:http://x.x.x.x/zabbix 修改为 http://x.x.x.x/ v ...