上文已经初步探讨了如何实现一个具体的transport,本文就来讨论一个具体的transport,本文讨论netty4的的相关实现。老规矩,看看motan-transport的目录结构。

其中最重要的类是啥,大声说出来,对,就是Netty4Client和Netty4Server。

图1-1motan-transport-netty4的源码结构


Netty4Client

首先看看NettyClinet的代码,最重要的就是open和request方法,可以看到open主要完成初始化工作。request主要请求和响应功能,heart表示心跳请求。

public class Netty4Client extends AbstractPoolClient implements StatisticCallback {
// 回收过期任务
private static ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4); // 异步的request,需要注册callback future
// 触发remove的操作有: 1) service的返回结果处理。 2) timeout thread cancel
protected ConcurrentMap<Long, NettyResponseFuture> callbackMap = new ConcurrentHashMap<Long, NettyResponseFuture>(); private ScheduledFuture<?> timeMonitorFuture = null; // 连续失败次数
private AtomicLong errorCount = new AtomicLong(0);
// 最大连接数
private int maxClientConnection = 0; private Bootstrap bootstrap; public Netty4Client(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);
} @Override
public Response request(Request request) throws TransportException {
if (!isAvailable()) {
throw new MotanServiceException("NettyChannel is unavaliable: url=" + url.getUri()
+ MotanFrameworkUtil.toString(request));
}
boolean async = url.getMethodParameter(request.getMethodName(), request.getParamtersDesc()
, URLParamType.async.getName(), URLParamType.async.getBooleanValue());
return request(request, async);
} @Override
public void heartbeat(Request request) {
// 如果节点还没有初始化或者节点已经被close掉了,那么heartbeat也不需要进行了
if (state.isUnInitState() || state.isCloseState()) {
LoggerUtil.warn("NettyClient heartbeat Error: state={} url={}", state.name(), url.getUri());
return;
} LoggerUtil.info("NettyClient heartbeat request: url={}", url.getUri()); try {
// async request后,如果service is
// available,那么将会自动把该client设置成可用
request(request, true);
} catch (Exception e) {
LoggerUtil.error("NettyClient heartbeat Error: url=" + url.getUri(), e);
}
} /**
* 请求remote service
* <p>
* <pre>
* 1) get connection from pool
* 2) async requset
* 3) return connection to pool
* 4) check if async return response, true: return ResponseFuture; false: return result
* </pre>
*
* @param request
* @param async
* @return
* @throws TransportException
*/
private Response request(Request request, boolean async) throws TransportException {
Channel channel = null; Response response = null; try {
// return channel or throw exception(timeout or connection_fail)
channel = borrowObject(); if (channel == null) {
LoggerUtil.error("NettyClient borrowObject null: url=" + url.getUri() + " "
+ MotanFrameworkUtil.toString(request));
return null;
} // async request
response = channel.request(request);
// return channel to pool
returnObject(channel);
} catch (Exception e) {
LoggerUtil.error(
"NettyClient request Error: url=" + url.getUri() + " " + MotanFrameworkUtil.toString(request), e);
//TODO 对特定的异常回收channel
invalidateObject(channel); if (e instanceof MotanAbstractException) {
throw (MotanAbstractException) e;
} else {
throw new MotanServiceException("NettyClient request Error: url=" + url.getUri() + " "
+ MotanFrameworkUtil.toString(request), e);
}
} // aysnc or sync result
response = asyncResponse(response, async); return response;
} /**
* 如果async是false,那么同步获取response的数据
*
* @param response
* @param async
* @return
*/
private Response asyncResponse(Response response, boolean async) {
if (async || !(response instanceof NettyResponseFuture)) {
return response;
} return new DefaultResponse(response);
} @Override
public synchronized boolean open() {
if (isAvailable()) {
LoggerUtil.warn("NettyServer ServerChannel already Opened: url=" + url);
return true;
} // 初始化netty client bootstrap
initClientBootstrap(); // 初始化连接池
initPool(); LoggerUtil.info("NettyClient finish Open: url={}", url); // 注册统计回调
StatsUtil.registryStatisticCallback(this); // 设置可用状态
state = ChannelState.ALIVE;
return state.isAliveState();
} /**
* 初始化 netty clientBootstrap
*/
private void initClientBootstrap() {
bootstrap = new Bootstrap(); NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group).channel(NioSocketChannel.class).handler(new Netty4ClientInitializer(url, codec, this)); bootstrap.option(ChannelOption.TCP_NODELAY, true);
bootstrap.option(ChannelOption.SO_KEEPALIVE, 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.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout);
} @Override
public synchronized void close() {
close(0);
} /**
* 目前close不支持timeout的概念
*/
@Override
public synchronized void close(int timeout) {
if (state.isCloseState()) {
LoggerUtil.info("NettyClient close fail: already close, url={}", url.getUri());
return;
} // 如果当前nettyClient还没有初始化,那么就没有close的理由。
if (state.isUnInitState()) {
LoggerUtil.info("NettyClient close Fail: don't need to close because node is unInit state: url={}",
url.getUri());
return;
} try {
// 取消定期的回收任务
timeMonitorFuture.cancel(true);
// 关闭连接池
pool.close();
// 清空callback
callbackMap.clear(); // 设置close状态
state = ChannelState.CLOSE;
// 解除统计回调的注册
StatsUtil.unRegistryStatisticCallback(this);
LoggerUtil.info("NettyClient close Success: url={}", url.getUri());
} catch (Exception e) {
LoggerUtil.error("NettyClient close Error: url=" + url.getUri(), e);
} } @Override
public boolean isClosed() {
return state.isCloseState();
} @Override
public boolean isAvailable() {
return state.isAliveState();
} @Override
public URL getUrl() {
return url;
} /**
* connection factory
*/
@Override
protected BasePoolableObjectFactory createChannelFactory() {
return new NettyChannelFactory(this);
} /**
* 增加调用失败的次数:
* <p>
* <pre>
* 如果连续失败的次数 >= maxClientConnection, 那么把client设置成不可用状态
* </pre>
*/
void incrErrorCount() {
long count = errorCount.incrementAndGet(); // 如果节点是可用状态,同时当前连续失败的次数超过限制maxClientConnection次,那么把该节点标示为不可用
if (count >= maxClientConnection && state.isAliveState()) {
synchronized (this) {
count = errorCount.longValue(); if (count >= maxClientConnection && state.isAliveState()) {
LoggerUtil.error("NettyClient unavailable Error: url=" + url.getIdentity() + " "
+ url.getServerPortStr());
state = ChannelState.UNALIVE;
}
}
}
} /**
* 重置调用失败的计数 :
* <p>
* <pre>
* 把节点设置成可用
* </pre>
*/
void resetErrorCount() {
errorCount.set(0); if (state.isAliveState()) {
return;
} synchronized (this) {
if (state.isAliveState()) {
return;
} // 如果节点是unalive才进行设置,而如果是 close 或者 uninit,那么直接忽略
if (state.isUnAliveState()) {
long count = errorCount.longValue(); // 过程中有其他并发更新errorCount的,因此这里需要进行一次判断
if (count < maxClientConnection) {
state = ChannelState.ALIVE;
LoggerUtil.info("NettyClient recover available: url=" + url.getIdentity() + " "
+ url.getServerPortStr());
}
}
}
} /**
* 注册回调的resposne
* <p>
* <pre>
*
* 进行最大的请求并发数的控制,如果超过NETTY_CLIENT_MAX_REQUEST的话,那么throw reject exception
*
* </pre>
*
* @param requestId
* @param nettyResponseFuture
* @throws MotanServiceException
*/
public void registerCallback(long requestId, NettyResponseFuture nettyResponseFuture) {
if (this.callbackMap.size() >= MotanConstants.NETTY_CLIENT_MAX_REQUEST) {
// reject request, prevent from OutOfMemoryError
throw new MotanServiceException("NettyClient over of max concurrent request, drop request, url: "
+ url.getUri() + " requestId=" + requestId, MotanErrorMsgConstant.SERVICE_REJECT);
} this.callbackMap.put(requestId, nettyResponseFuture);
} /**
* 统计回调接口
*/
@Override
public String statisticCallback() {
//避免消息泛滥,如果节点是可用状态,并且堆积的请求不超过100的话,那么就不记录log了
if (isAvailable() && callbackMap.size() < 100) {
return null;
} return String.format("identity: %s available: %s concurrent_count: %s", url.getIdentity(), isAvailable(),
callbackMap.size());
} /**
* 移除回调的response
*
* @param requestId
* @return
*/
public NettyResponseFuture removeCallback(long requestId) {
return callbackMap.remove(requestId);
} public Bootstrap getBootstrap() {
return bootstrap;
} /**
* 回收超时任务
*
* @author maijunsheng
*/
class TimeoutMonitor implements Runnable {
private String name; public TimeoutMonitor(String name) {
this.name = name;
} public void run() { long currentTime = System.currentTimeMillis(); for (Map.Entry<Long, NettyResponseFuture> entry : callbackMap.entrySet()) {
try {
NettyResponseFuture future = entry.getValue();
if (future.getCreateTime() + future.getTimeout() <currentTime) {
// timeout: remove from callback list, and then cancel
removeCallback(entry.getKey());
future.cancel();
}
} catch (Exception e) {
LoggerUtil.error(
name + " clear timeout future Error: uri=" + url.getUri() + " requestId=" + entry.getKey(),
e);
}
}
}
}
}

open的最核心功就是各种设置。

initClientBootstrap流程中
bootstrap.group(group).channel(NioSocketChannel.class).handler(new Netty4ClientInitializer(url, codec, this));
这句中的Netty4ClientInitializer泪中,指定如下3个handler. Netty4Decoder:协议的解码器 Netty4Encoder:协议的编码器
Netty4ClientHandler:对服务器的返回结果的处理。
 public class Netty4ClientInitializer extends ChannelInitializer<SocketChannel> {

     private URL url;

     private Codec codec;

     private Netty4Client client;

     public Netty4ClientInitializer(URL url, Codec codec, Netty4Client client) {
this.url = url;
this.codec = codec;
this.client = client;
} @Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
// 最大响应包限制
int maxContentLength = url.getIntParameter(URLParamType.maxContentLength.getName(),
URLParamType.maxContentLength.getIntValue());
p.addLast("decoder", new Netty4Decoder(codec, client, maxContentLength));
p.addLast("encoder", new Netty4Encoder(codec, client));
p.addLast("handler", new Netty4ClientHandler(client)); }
}

编码和解码是非常重要的过程,有个模块专门负责这个过程,现在不表。

Netty4ClientHandler主要是request()中对响应的处理,最终执行就是以下FutureListener中的operationComplete相关代码。
 
 if (result && writeFuture.isSuccess()) {
response.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (future.isSuccess() || (future.isDone() && ExceptionUtil.isBizException(future.getException()))) {
// 成功的调用
nettyClient.resetErrorCount();
} else {
// 失败的调用
nettyClient.incrErrorCount();
}
}
});
return response;
}
 

     

RPC框架motan: 通信框架netty之Netty4Client的更多相关文章

  1. RPC框架motan: 通信框架netty( 1)

    服务器端编程都离不开底层的通信框架,在我们刚学习java的时候,主要接触都是Socket和ServerSocket 的阻塞编程,后来开始了解NIO,这种非阻塞的编程模式,它可以一个线程管理很多的Soc ...

  2. 成熟的C#网络通信框架介绍——ESFramework通信框架

    (转自:http://www.cnblogs.com/zhuweisky/archive/2010/08/12/1798211.html) ESFramework通信框架是一套性能卓越.稳定可靠.强大 ...

  3. SpringBoot2+Netty打造通俗简版RPC通信框架(升级版)

    背景         上篇文章我简单的介绍了自己打造的通俗简版RPC通信框架,这篇是对简版的增强~         如果大家对此项目还感兴趣的话,可到码云上瞄瞄:Netty-RPC         上 ...

  4. SpringBoot2+Netty打造通俗简版RPC通信框架

    2019-07-19:完成基本RPC通信! 2019-07-22:优化此框架,实现单一长连接! 2019-07-24:继续优化此框架:1.增加服务提供注解(带版本号),然后利用Spring框架的在启动 ...

  5. 为什么选择Netty作为基础通信框架?

    在开始之前,我先讲一个亲身经历的故事:曾经有两个项目组同时用到了NIO编程技术,一个项目组选择自己开发NIO服务端,直接使用JDK原生的API,结果两个多月过去了,他们的NIO服务端始终无法稳定,问题 ...

  6. Spark1.6之后为何使用Netty通信框架替代Akka

    解决方案: 一直以来,基于Akka实现的RPC通信框架是Spark引以为豪的主要特性,也是与Hadoop等分布式计算框架对比过程中一大亮点. 但是时代和技术都在演化,从Spark1.3.1版本开始,为 ...

  7. 微博轻量级RPC框架Motan正式开源:支撑千亿调用

    支撑微博千亿调用的轻量级 RPC 框架 Motan 正式开源了,项目地址为https://github.com/weibocom/motan. 微博轻量级RPC框架Motan正式开源 Motan 是微 ...

  8. RPC通信框架——RCF介绍

    现有的软件中用了大量的COM接口,导致无法跨平台,当然由于与Windows结合的太紧密,还有很多无法跨平台的地方.那么为了实现跨平台,支持Linux系统,以及后续的分布式,首要任务是去除COM接口. ...

  9. Dobbo的继任者?试用微博RPC框架Motan

    从14年开始就陆续看到新浪微博RPC框架Motan的介绍,时隔两年后,微博团队终于宣布开源轻量级RPC框架Motan,项目地址: https://github.com/weibocom/motan/ ...

随机推荐

  1. 【Linux高频命令专题(23)】tar

    概述 通过SSH访问服务器,难免会要用到压缩,解压缩,打包,解包等,这时候tar命令就是是必不可少的一个功能强大的工具.linux中最流行的tar是麻雀虽小,五脏俱全,功能强大. tar命令可以为li ...

  2. C#操作.csv文件Demo

    1.使用OleDB操作.csv文件,比较费时 public static DataTable GetDataTableFromCsv(string path,bool isFirstRowHeader ...

  3. iOS Container View Controller

    一.UIViewController 做iOS开发的经常会和UIViewController打交道,从类名可知UIViewController属于MVC模型中的C(Controller),说的更具体点 ...

  4. 深入探索 Java 热部署

    在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作.对于某些大型的应用来 ...

  5. List应用举例

    1.集合的嵌套遍历 学生类: package listexercise; /** * Created by gao on 15-12-9. */ public class Student { priv ...

  6. windows和mac下分别配置虚拟主机

    windows下配置 1.找到apache的配置文件,httpd.conf 2.找到 LoadModule rewrite_module modules/mod_rewrite.so 去掉前边的# 3 ...

  7. Android Things:Raspberry Pi 3 B 刷入 Android Things

    参考文章: http://www.andtuts.com/a-beginners-guide-to-raspberry-pi-3-b-and-android-things/?utm_source=An ...

  8. Android开发之定义app在手机的安装位置

    定义app在手机的安装位置,可以通过在清单文件中添加属性 android:installLocation="" 该属性有三个值:auto(自动),preferExternal(外部 ...

  9. poj 3258 River Hopscotch(二分+贪心)

    题目:http://poj.org/problem?id=3258 题意: 一条河长度为 L,河的起点(Start)和终点(End)分别有2块石头,S到E的距离就是L. 河中有n块石头,每块石头到S都 ...

  10. MSSQL大全

    一.基础 1.说明:创建数据库CREATE DATABASE database-name 2.说明:删除数据库drop database dbname3.说明:备份sql server--- 创建 备 ...