motan源码分析六:客户端与服务器的通信层分析
本章将分析motan的序列化和底层通信相关部分的代码。
1.在上一章中,有一个getrefers的操作,来获取所有服务器的引用,每个服务器的引用都是由DefaultRpcReferer来创建的
public DefaultRpcReferer(Class<T> clz, URL url, URL serviceUrl) {
super(clz, url, serviceUrl);
endpointFactory =
ExtensionLoader.getExtensionLoader(EndpointFactory.class).getExtension(
url.getParameter(URLParamType.endpointFactory.getName(), URLParamType.endpointFactory.getValue()));//通过spi加载NettyEndpointFactory
client = endpointFactory.createClient(url);//创建client
}
2.NettyClient的创建过程及源码分析
public Client createClient(URL url) {
LoggerUtil.info(this.getClass().getSimpleName() + " create client: url={}", url);
return createClient(url, heartbeatClientEndpointManager);//创建client
}
private Client createClient(URL url, EndpointManager endpointManager) {
Client client = innerCreateClient(url);//调用NettyEndpointFactory的创建client的方法
endpointManager.addEndpoint(client);//添加心跳管理
return client;
}
protected Client innerCreateClient(URL url) {
return new NettyClient(url);//返回NettyClient对象
}
public NettyClient(URL url) {
super(url);
maxClientConnection = url.getIntParameter(URLParamType.maxClientConnection.getName(),
URLParamType.maxClientConnection.getIntValue());
timeMonitorFuture = scheduledExecutor.scheduleWithFixedDelay(
new TimeoutMonitor("timeout_monitor_" + url.getHost() + "_" + url.getPort()),
MotanConstants.NETTY_TIMEOUT_TIMER_PERIOD, MotanConstants.NETTY_TIMEOUT_TIMER_PERIOD,
TimeUnit.MILLISECONDS);
LoggerUtil.info("client's:"+url.getUri());
}
3.Netty相关的连接建立是通过open()方法进行的
public synchronized boolean open() {
if (isAvailable()) {
return true;
}
// 初始化netty client bootstrap
initClientBootstrap();
// 初始化连接池
initPool();
LoggerUtil.info("NettyClient finish Open: url={}", url);
// 注册统计回调
StatsUtil.registryStatisticCallback(this);
// 设置可用状态
state = ChannelState.ALIVE;
return state.isAliveState();
}
private void initClientBootstrap() {
bootstrap = new ClientBootstrap(channelFactory);
bootstrap.setOption("keepAlive", true);
bootstrap.setOption("tcpNoDelay", true);
// 实际上,极端情况下,connectTimeout会达到500ms,因为netty nio的实现中,是依赖BossThread来控制超时,
// 如果为了严格意义的timeout,那么需要应用端进行控制。
int timeout = getUrl().getIntParameter(URLParamType.requestTimeout.getName(), URLParamType.requestTimeout.getIntValue());
if (timeout <= 0) {
throw new MotanFrameworkException("NettyClient init Error: timeout(" + timeout + ") <= 0 is forbid.",
MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
}
bootstrap.setOption("connectTimeoutMillis", timeout);
// 最大响应包限制
final int maxContentLength = url.getIntParameter(URLParamType.maxContentLength.getName(),
URLParamType.maxContentLength.getIntValue());
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new NettyDecoder(codec, NettyClient.this, maxContentLength));//解码器
pipeline.addLast("encoder", new NettyEncoder(codec, NettyClient.this));//编码器
pipeline.addLast("handler", new NettyChannelHandler(NettyClient.this, new MessageHandler() {//业务处理的handler
@Override
public Object handle(Channel channel, Object message) {
Response response = (Response) message;
NettyResponseFuture responseFuture = NettyClient.this.removeCallback(response.getRequestId());//移调异步处理response信息
if (responseFuture == null) {
LoggerUtil.warn(
"NettyClient has response from server, but resonseFuture not exist, requestId={}",
response.getRequestId());
return null;
}
if (response.getException() != null) {
responseFuture.onFailure(response);
} else {
responseFuture.onSuccess(response);
}
return null;
}
}));
return pipeline;
}
});
}
4.连接池
protected void initPool() {
poolConfig = new GenericObjectPool.Config();//使用了GenericObjectPool作为连接池
poolConfig.minIdle =
url.getIntParameter(URLParamType.minClientConnection.getName(), URLParamType.minClientConnection.getIntValue());//最小连接数,配置中为2个
poolConfig.maxIdle =
url.getIntParameter(URLParamType.maxClientConnection.getName(), URLParamType.maxClientConnection.getIntValue());//最大连接数,配置中为10个
poolConfig.maxActive = poolConfig.maxIdle;
poolConfig.maxWait = url.getIntParameter(URLParamType.requestTimeout.getName(), URLParamType.requestTimeout.getIntValue());
poolConfig.lifo = url.getBooleanParameter(URLParamType.poolLifo.getName(), URLParamType.poolLifo.getBooleanValue());
poolConfig.minEvictableIdleTimeMillis = defaultMinEvictableIdleTimeMillis;
poolConfig.softMinEvictableIdleTimeMillis = defaultSoftMinEvictableIdleTimeMillis;
poolConfig.timeBetweenEvictionRunsMillis = defaultTimeBetweenEvictionRunsMillis;
factory = createChannelFactory();//创建chanalfactory
pool = new GenericObjectPool(factory, poolConfig);
boolean lazyInit = url.getBooleanParameter(URLParamType.lazyInit.getName(), URLParamType.lazyInit.getBooleanValue());
if (!lazyInit) {
for (int i = 0; i < poolConfig.minIdle; i++) {//初始化2个长连接
try {
pool.addObject();
LoggerUtil.info("init client's connection :"+i);
} catch (Exception e) {
LoggerUtil.error("NettyClient init pool create connect Error: url=" + url.getUri(), e);
}
}
}
}
5.NettyChannelFactory
public class NettyChannelFactory extends BasePoolableObjectFactory {
private String factoryName = "";
private NettyClient nettyClient;
public NettyChannelFactory(NettyClient nettyClient) {
super();
this.nettyClient = nettyClient;
this.factoryName = "NettyChannelFactory_" + nettyClient.getUrl().getHost() + "_"
+ nettyClient.getUrl().getPort();
}
@Override
public Object makeObject() throws Exception {//创建连接时会调用
NettyChannel nettyChannel = new NettyChannel(nettyClient);//创建channel
nettyChannel.open();//打开channel
return nettyChannel;
}
}
6.NettyChannel
public class NettyChannel implements com.weibo.api.motan.transport.Channel {
private volatile ChannelState state = ChannelState.UNINIT;
private NettyClient nettyClient;
private org.jboss.netty.channel.Channel channel = null;
private InetSocketAddress remoteAddress = null;
private InetSocketAddress localAddress = null;
public NettyChannel(NettyClient nettyClient) {
this.nettyClient = nettyClient;
this.remoteAddress = new InetSocketAddress(nettyClient.getUrl().getHost(), nettyClient.getUrl().getPort());//服务器host和port
}
public synchronized boolean open() {//打开连接
if (isAvailable()) {
LoggerUtil.warn("the channel already open, local: " + localAddress + " remote: " + remoteAddress + " url: "
+ nettyClient.getUrl().getUri());
return true;
}
try {
ChannelFuture channleFuture = nettyClient.getBootstrap().connect(
new InetSocketAddress(nettyClient.getUrl().getHost(), nettyClient.getUrl().getPort()));//打开连接
long start = System.currentTimeMillis();
int timeout = nettyClient.getUrl().getIntParameter(URLParamType.connectTimeout.getName(), URLParamType.connectTimeout.getIntValue());
if (timeout <= 0) {
throw new MotanFrameworkException("NettyClient init Error: timeout(" + timeout + ") <= 0 is forbid.",
MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
}
// 不去依赖于connectTimeout
boolean result = channleFuture.awaitUninterruptibly(timeout, TimeUnit.MILLISECONDS);
boolean success = channleFuture.isSuccess();
if (result && success) {
channel = channleFuture.getChannel();
if (channel.getLocalAddress() != null && channel.getLocalAddress() instanceof InetSocketAddress) {
localAddress = (InetSocketAddress) channel.getLocalAddress();
}
state = ChannelState.ALIVE;
return true;
}
boolean connected = false;
if(channleFuture.getChannel() != null){
connected = channleFuture.getChannel().isConnected();
}
if (channleFuture.getCause() != null) {
channleFuture.cancel();
throw new MotanServiceException("NettyChannel failed to connect to server, url: "
+ nettyClient.getUrl().getUri()+ ", result: " + result + ", success: " + success + ", connected: " + connected, channleFuture.getCause());
} else {
channleFuture.cancel();
throw new MotanServiceException("NettyChannel connect to server timeout url: "
+ nettyClient.getUrl().getUri() + ", cost: " + (System.currentTimeMillis() - start) + ", result: " + result + ", success: " + success + ", connected: " + connected);
}
} catch (MotanServiceException e) {
throw e;
} catch (Exception e) {
throw new MotanServiceException("NettyChannel failed to connect to server, url: "
+ nettyClient.getUrl().getUri(), e);
} finally {
if (!state.isAliveState()) {
nettyClient.incrErrorCount();//增加错误次数
}
}
}
}
本章知识点总结:
1.使用netty作为底层通讯框架;
2.每个refer对应一个nettyclient和一个nettychannel,nettychannel是由工厂类创建;
3.每个client在初始化时,最少创建2个长连接,由配置决定;
4.使用了GenericObjectPool来作为连接池;
5.当每个client的连续调用出错数达到阀值时,将自动设置此client为不可用;
6.心跳操作由客户端发起,只针对不可用状态的client。
motan源码分析六:客户端与服务器的通信层分析的更多相关文章
- Zookeeper 源码(六)Leader-Follower-Observer
Zookeeper 源码(六)Leader-Follower-Observer 上一节介绍了 Leader 选举的全过程,本节讲解一下 Leader-Follower-Observer 服务器的三种角 ...
- Netty 源码解析(五): Netty 的线程池分析
今天是猿灯塔“365篇原创计划”第五篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...
- Dubbo源码剖析六之SPI扩展点的实现之Adaptive功能实现原理
接Dubbo源码剖析六之SPI扩展点的实现之getExtensionLoader - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)继续分析Adaptive功能实现原理.Adaptive的主 ...
- Dubbo源码剖析六之SPI扩展点的实现之getExtension
上文Dubbo源码剖析六之SPI扩展点的实现之getExtensionLoader - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中分析了getExtensionLoader,本文继续分 ...
- Celery 源码解析六:Events 的实现
在 Celery 中,除了远程控制之外,还有一个元素可以让我们对分布式中的任务的状态有所掌控,而且从实际意义上来说,这个元素对 Celery 更为重要,这就是在本文中将要说到的 Event. 在 Ce ...
- 一点一点看JDK源码(六)java.util.LinkedList前篇之链表概要
一点一点看JDK源码(六)java.util.LinkedList前篇之链表概要 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.什么 ...
- motan源码分析八:涉及到底层的客户端调用
之前我们分析了客户端调用服务端的源码,但是没有涉及到通讯层和序列化层,本文将之前讲过的内容做一次串联. 1.上层通过动态代理调用refer的call,每个refer又对应一个nettyclient,下 ...
- motan源码分析四:客户端调用服务
在第一章中,我们分析了服务的发布与注册,本章中将简单的分析一下客户端调用服务的代码及流程,本文将以spring加载的方式进行分析. 1.在DemoRpcClient类的main()方法中加载类: Ap ...
- motan源码分析五:cluster相关
上一章我们分析了客户端调用服务端相关的源码,但是到了cluster里面的部分我们就没有分析了,本章将深入分析cluster和它的相关支持类. 1.clustersupport的创建过程,上一章的Ref ...
随机推荐
- Hibernate HQL查询:
Hibernate HQL查询:Criteria查询对查询条件进行了面向对象封装,符合编程人员的思维方式,不过HQL(Hibernate Query Lanaguage)查询提供了更加丰富的和灵活的查 ...
- (转)ThinkPHP Where 条件中使用表达式
转之--http://www.cnblogs.com/martin1009/archive/2012/08/24/2653718.html Where 条件表达式格式为: $map['字段名'] = ...
- 在eclipse中新建Dynamic web project时选择2.5和3.0的区别(里面涉及servlet和tomcat的问题)
1.是指servlet的版本,是2.5的还是3.0的 servlet3.0以后支持异步 2.dynamic web module和对应的TOMCAT 版本 http://blog.sina.com.c ...
- SQL利用临时表实现动态列、动态添加列
--方法一--------------------------------------------------------------------- declare @sql as varchar(1 ...
- Objective-C 笔记二 类、对象和方法
对象就是一个物件.面向对象的程序设计可以看成一个物件和你想对它做的事情.这与C语言不同,C语言通常称为过程性语言.在C语言中,通常是先考虑要做什么,然后才关注对象,这几乎总是与面相对象的思考过程相反. ...
- Ubuntu12.04安装insight-6.8
insight是在Linux下一个比较好用的GDB的前端 insight首页:http://sourceware.org/insight/index.php 在这里下载源码:insight-6.8.t ...
- 3.题目:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制。
public static void main(String[] args) { Scanner scanner=new Scanner(System.in); ...
- 修路方案(nyoj)
算法:次小生成树 描述 南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路. 现在已经知道哪些城市之间可以修路,如果修路,花费是多少. 现在 ...
- java web 学习(2)
今天突然想到写的测试代码最好随时取出来,在不同的机器上不用老是拷来拷去,还真找着了免费的Svn, svn://www.svn999.com/luhouxiang.javastudy,暂时学习的工程代码 ...
- 试用ubuntu-12.04.3-desktop-amd64(二)
首先说明,采用主机+虚拟机+ubuntu的形式,更具体的则为Win7-64bit + VMWare + ubuntu-12.04.3-desktop-amd64 进入ubuntu后首先考虑到的就是怎么 ...