上一章我们分析了客户端调用服务端相关的源码,但是到了cluster里面的部分我们就没有分析了,本章将深入分析cluster和它的相关支持类。

1.clustersupport的创建过程,上一章的ReferConfig的initRef()方法中调用了相关的创建代码:

        for(Iterator iterator = protocols.iterator(); iterator.hasNext();)
{
ProtocolConfig protocol = (ProtocolConfig)iterator.next();
LoggerUtil.info((new StringBuilder("ProtocolConfig's")).append(protocol.getName()).toString());
Map params = new HashMap();
params.put(URLParamType.nodeType.getName(), "referer");
params.put(URLParamType.version.getName(), URLParamType.version.getValue());
params.put(URLParamType.refreshTimestamp.getName(), String.valueOf(System.currentTimeMillis()));
collectConfigParams(params, new AbstractConfig[] {
protocol, basicReferer, extConfig, this
});
collectMethodConfigParams(params, getMethods());
URL refUrl = new URL(protocol.getName(), localIp, 0, interfaceClass.getName(), params);
ClusterSupport clusterSupport = createClusterSupport(refUrl, configHandler, registryUrls);//创建clustersupport
clusterSupports.add(clusterSupport);
clusters.add(clusterSupport.getCluster());//获取对应的cluster
proxy = proxy != null ? proxy : refUrl.getParameter(URLParamType.proxy.getName(), URLParamType.proxy.getValue());
} private ClusterSupport createClusterSupport(URL refUrl, ConfigHandler configHandler, List registryUrls)
{
List regUrls = new ArrayList();
if(StringUtils.isNotBlank(directUrl) || "injvm".equals(refUrl.getProtocol()))
{
URL regUrl = new URL("local", "127.0.0.1", 0, com/weibo/api/motan/registry/RegistryService.getName());
if(StringUtils.isNotBlank(directUrl))
{
StringBuilder duBuf = new StringBuilder(128);
String dus[] = MotanConstants.COMMA_SPLIT_PATTERN.split(directUrl);
String as[];
int j = (as = dus).length;
for(int i = 0; i < j; i++)
{
String du = as[i];
if(du.contains(":"))
{
String hostPort[] = du.split(":");
URL durl = refUrl.createCopy();
durl.setHost(hostPort[0].trim());
durl.setPort(Integer.parseInt(hostPort[1].trim()));
durl.addParameter(URLParamType.nodeType.getName(), "service");
duBuf.append(StringTools.urlEncode(durl.toFullStr())).append(",");
}
} if(duBuf.length() > 0)
{
duBuf.deleteCharAt(duBuf.length() - 1);
regUrl.addParameter(URLParamType.directUrl.getName(), duBuf.toString());
}
}
regUrls.add(regUrl);
} else//走注册中心的方式
{
if(registryUrls == null || registryUrls.isEmpty())
throw new IllegalStateException(String.format("No registry to reference %s on the consumer %s , please config <motan:registry address=\"...\" /> in your spring config.", new Object[] {
interfaceClass, "127.0.0.1"
}));
URL url;
for(Iterator iterator = registryUrls.iterator(); iterator.hasNext(); regUrls.add(url.createCopy()))
url = (URL)iterator.next(); }
URL url;
for(Iterator iterator1 = regUrls.iterator(); iterator1.hasNext(); url.addParameter(URLParamType.embed.getName(), StringTools.urlEncode(refUrl.toFullStr())))
url = (URL)iterator1.next(); return configHandler.buildClusterSupport(interfaceClass, regUrls);//调用simpleconfighandler的创建clustersupport方法
} public <T> ClusterSupport<T> buildClusterSupport(Class<T> interfaceClass, List<URL> registryUrls) {
ClusterSupport<T> clusterSupport = new ClusterSupport<T>(interfaceClass, registryUrls);//创建cluster支持类,将业务接口和注册中心信息传递进去
clusterSupport.init();//初始化 return clusterSupport;
}

2.clustersupport的init和prepare方法

    public void init() {

        prepareCluster();

        URL subUrl = toSubscribeUrl(url);
for (URL ru : registryUrls) {//循环注册中心的url String directUrlStr = ru.getParameter(URLParamType.directUrl.getName());
// 如果有directUrl,直接使用这些directUrls进行初始化,不用到注册中心discover
if (StringUtils.isNotBlank(directUrlStr)) {
List<URL> directUrls = parseDirectUrls(directUrlStr);
if (!directUrls.isEmpty()) {
notify(ru, directUrls);
LoggerUtil.info("Use direct urls, refUrl={}, directUrls={}", url, directUrls);
continue;
}
} // client 注册自己,同时订阅service列表
Registry registry = getRegistry(ru);//获取zookeeper的注册中心
registry.subscribe(subUrl, this);//注册自己并订阅服务
} boolean check = Boolean.parseBoolean(url.getParameter(URLParamType.check.getName(), URLParamType.check.getValue()));
if (!CollectionUtil.isEmpty(cluster.getReferers()) || !check) {
cluster.init();//初始化集群
if (CollectionUtil.isEmpty(cluster.getReferers()) && !check) {
LoggerUtil.warn(String.format("refer:%s", this.url.getPath() + "/" + this.url.getVersion()), "No services");
}
return;
} throw new MotanFrameworkException(String.format("ClusterSupport No service urls for the refer:%s, registries:%s",
this.url.getIdentity(), registryUrls), MotanErrorMsgConstant.SERVICE_UNFOUND);
} private void prepareCluster() {
String clusterName = url.getParameter(URLParamType.cluster.getName(), URLParamType.cluster.getValue());//集群名称
String loadbalanceName = url.getParameter(URLParamType.loadbalance.getName(), URLParamType.loadbalance.getValue());//负载均衡名称
String haStrategyName = url.getParameter(URLParamType.haStrategy.getName(), URLParamType.haStrategy.getValue());//ha高可用名称 cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(clusterName);//获取具体的集群对象
LoadBalance<T> loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName);//获取具体的负载均衡方式,目前motan支持6种负载方式
HaStrategy<T> ha = ExtensionLoader.getExtensionLoader(HaStrategy.class).getExtension(haStrategyName);//获取高可用的方式,目前支持两种failfast和failover方式
cluster.setLoadBalance(loadBalance);
cluster.setHaStrategy(ha);
cluster.setUrl(url);
}

3.负载均衡,motan支持6种方式,分别是:轮训、随机、hash、本地服务优先、权重可配置、低并发优先,具体代码可见com.weibo.api.motan.cluster.loadbalance目录,本文我们主要看一下轮训的方式:

public class RoundRobinLoadBalance<T> extends AbstractLoadBalance<T> {

    private AtomicInteger idx = new AtomicInteger(0);

    @Override
protected Referer<T> doSelect(Request request) {
List<Referer<T>> referers = getReferers();//获取所有服务器的引用 int index = idx.incrementAndGet();//自增
for (int i = 0; i < referers.size(); i++) {
Referer<T> ref = referers.get((i + index) % referers.size());//利用自增数去模,达到轮训的目的
if (ref.isAvailable()) {
return ref;
}
}
return null;
} @Override
protected void doSelectToHolder(Request request, List<Referer<T>> refersHolder) {
List<Referer<T>> referers = getReferers(); int index = idx.incrementAndGet();
for (int i = 0; i < referers.size(); i++) {
Referer<T> referer = referers.get((i + index) % referers.size());
if (referer.isAvailable()) {
refersHolder.add(referer);
}
}
}
}

4.motan支持failfast和failover两种方式,failfast只调用一次,如果失败则直接返回失败,failover循环调用若干次,直到成功或循环结束后

    public Response call(Request request, LoadBalance<T> loadBalance) {

        List<Referer<T>> referers = selectReferers(request, loadBalance);//获取所有的引用
if (referers.isEmpty()) {
throw new MotanServiceException(String.format("FailoverHaStrategy No referers for request:%s, loadbalance:%s", request,
loadBalance));
}
URL refUrl = referers.get(0).getUrl();
// 先使用method的配置
int tryCount =
refUrl.getMethodParameter(request.getMethodName(), request.getParamtersDesc(), URLParamType.retries.getName(),
URLParamType.retries.getIntValue());//获取重试次数
// 如果有问题,则设置为不重试
if (tryCount < 0) {
tryCount = 0;
} for (int i = 0; i <= tryCount; i++) {
Referer<T> refer = referers.get(i % referers.size());//循环调用
try {
request.setRetries(i);
return refer.call(request);
} catch (RuntimeException e) {
// 对于业务异常,直接抛出
if (ExceptionUtil.isBizException(e)) {
throw e;//业务异常退出调用
} else if (i >= tryCount) {
throw e;
}
LoggerUtil.warn(String.format("FailoverHaStrategy Call false for request:%s error=%s", request, e.getMessage()));
}
} throw new MotanFrameworkException("FailoverHaStrategy.call should not come here!");
}

本章知识点总结:

1.一个cluster有一个cluster的支持类,有一个ha,有一个loadbalance;

2.motan支持6种负载均衡方式;

3.motan支持failover的ha方式;

motan源码分析五:cluster相关的更多相关文章

  1. redis源码分析(五)--cluster(集群)结构

    Redis集群 Redis支持集群模式,集群中可以存在多个master,每个master又可以拥有多个slave.数据根据关键字映射到不同的slot,每一个master负责一部分的slots,数据被存 ...

  2. motan源码分析六:客户端与服务器的通信层分析

    本章将分析motan的序列化和底层通信相关部分的代码. 1.在上一章中,有一个getrefers的操作,来获取所有服务器的引用,每个服务器的引用都是由DefaultRpcReferer来创建的 pub ...

  3. Vue系列---理解Vue.nextTick使用及源码分析(五)

    _ 阅读目录 一. 什么是Vue.nextTick()? 二. Vue.nextTick()方法的应用场景有哪些? 2.1 更改数据后,进行节点DOM操作. 2.2 在created生命周期中进行DO ...

  4. vuex 源码分析(五) action 详解

    action类似于mutation,不同的是Action提交的是mutation,而不是直接变更状态,而且action里可以包含任意异步操作,每个mutation的参数1是一个对象,可以包含如下六个属 ...

  5. ABP源码分析五:ABP初始化全过程

    ABP在初始化阶段做了哪些操作,前面的四篇文章大致描述了一下. 为个更清楚的描述其脉络,做了张流程图以辅助说明.其中每一步都涉及很多细节,难以在一张图中全部表现出来.每一步的细节(会涉及到较多接口,类 ...

  6. MPTCP 源码分析(五) 接收端窗口值

    简述:      在TCP协议中影响数据发送的三个因素分别为:发送端窗口值.接收端窗口值和拥塞窗口值. 本文主要分析MPTCP中各个子路径对接收端窗口值rcv_wnd的处理.   接收端窗口值的初始化 ...

  7. jQuery 源码分析(五) map函数 $.map和$.fn.map函数 详解

    $.map() 函数用于使用指定函数处理数组中的每个元素(或对象的每个属性),并将处理结果封装为新的数组返回,该函数有三个参数,如下: elems Array/Object类型 指定的需要处理的数组或 ...

  8. Vue.js 源码分析(五) 基础篇 方法 methods属性详解

    methods中定义了Vue实例的方法,官网是这样介绍的: 例如:: <!DOCTYPE html> <html lang="en"> <head&g ...

  9. motan源码分析十一:部分特性

    本章将描述motan部分的特性并对源码进行分析. 1.requestid的维护,使用了当前时间左移20位,再和一个自增变量组合 public class RequestIdGenerator { ); ...

随机推荐

  1. webpack 配置 (支持 React SCSS ES6 编译打包 和 模块热更新 / 生成SourceMap)

    1.首先是目录结构 |-node_modules/ #包文件 |-build/ #静态资源生成目录 |-src/ #开发目录 |-js/ |-index.js #入口文件 |-app.js #Reac ...

  2. Oracle 用户(user)和模式(schema)的区别

    概述: (一)什么Oracle叫用户(user): A user is a name defined in the database that can connect to and access ob ...

  3. iOS开发网络资源整理-持续更新

    本文记录iOS开发相关的网络社区和博客 1.objc中国 网址:http://objccn.io 简介:onevcat创建,项目的成立源于国内 Objective-C 社区对 objc.io 的翻译活 ...

  4. 玩转iOS 9的UIDynamics(转)

    转自 http://www.cocoachina.com/ios/20150716/12613.html 本文由CocoaChina翻译小组成员AGSpider(微博)翻译自fancypixel的博客 ...

  5. SxsTrace工具使用方法(转)

    http://blog.sina.com.cn/s/blog_494e45fe0102dtt3.html Windows7平台上有一个强大的SxsTrace工具,可以跟踪调试应用程序运行时需要的动态库 ...

  6. linux常用svn命令(转载)

     原地址:http://www.rjgc.net/control/content/content.php?nid=4418       1.将文件checkout到本地目录svn checkout p ...

  7. 浅析a标签的4个伪类 .

    关于伪类,大家最熟悉的还是a标签的4个伪类::link        有链接属性时:visited    链接地址已被访问过:active     被用户激活(在鼠标点击与释放之间发生的事件):hov ...

  8. Automatically watermark all uploaded photos (给所有上传的相片加水印)

    Hello, This mod automatically watermark all uploaded photos. Price: FREE, enjoy. You will have to ed ...

  9. python运维开发之第六天

    Python面向对象 python从设计之初就已经是一门面向对象的语言,在python中创建一个类和对象很容易. 面向对象简介:类(class),类变量,object(基类),实例变量,构造函数,封装 ...

  10. hdu 4548 第六周H题(美素数)

    第六周H题 - 数论,晒素数 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u   De ...