无论是Dubbo,还是Spring Cloud,大家可能都不会感到陌生。

那什么是Dubbo Spring Cloud呢?使用Dubbo Spring Cloud可以实现什么目的?基于其实现的路由和负载均衡又是怎样的呢?

本文就带大家来一探究竟!

以下节选自《深入理解Spring Cloud与实战》一书。

什么是Dubbo Spring Cloud

Dubbo Spring Cloud是Spring Cloud Alibaba项目内部提供的一个可以使用Spring Cloud客户端RestTemplate或OpenFeign调用Dubbo服务的模块。

Apache Dubbo和Spring Cloud是两套架构完全不同的开发框架。

在讲解Dubbo Spring Cloud之前,我们先来看这个问题:Apache Dubbo暴露的服务都是接口级别的,而Spring Cloud暴露的服务是应用级别的,RestTemplate或OpenFeign发起调用服务都会有对应的URL Path、Query Parameter、Header等内容(这是HTTP协议调用),如何让这些内容关联Dubbo服务呢?

针对上述问题,Dubbo Spring Cloud实现了以应用为粒度的注册机制,每个Dubbo应用注册到注册中心后有且仅有一个服务。那么原先以接口为维度的那些接口信息去哪里了?

Dubbo Spring Cloud 定义了 DubboMetadataService 元数据服务的概念。这是一个专门用于存储 Dubbo 服务的元数据接口。DubboMetadataService 接口定义如下:

public interface DubboMetadataService {// 最核心的接口,用于获取 Dubbo 服务的 Rest 元数据    String getServiceRestMetadata();// 返回所有 Dubbo 服务的 ServiceKey 集合    Set<String> getAllServiceKeys();// 返回所有对外暴露的Dubbo服务。key为ServiceKey,value为URL的json格式    Map<String, String> getAllExportedURLs();// 基于接口名分组及版本获取到 URL 的 json 格式    String getExportedURLs(String serviceInterface, String group, String version);
}

核心方法 getServiceRestMetadata 获取 Dubbo 服务的 Rest 元数据是指:当一个 Dubbo 服务同时也被 SpringMVC 相关注解修饰时,SpringMVC 相关注解修饰的内容就是这些 Rest 元数据。这些 Rest 元数据由 RestMethodMetadata 类修饰,比如,这个 Dubbo 服务 RestService 接口,其定义如下:

@Service
@RestController
public class SpringRestService implements RestService {@Override   @GetMapping("/param")    public String param(@RequestParam String param) {return param;   }
}

RestService服务对应的Rest元数据内容如下:

RestMethodMetadata{method=MethodMetadata{name='param',
returnType='java.lang.String', params=[MethodParameterMetadata{index=0,
name='param', type='java.lang.String'}], method=public java.lang.String
com.alibaba.cloud.dubbo.service.SpringRestService.param(java.lang.String)},
request=RequestMetadata{method='GET', path='/param', params={param=[{param}]},
headers=[], consumes=[], produces=[]}, urlIndex=null, bodyIndex=null,
headerMapIndex=null, queryMapIndex=null, queryMapEncoded=false,
returnType='java.lang.String', bodyType='null', indexToName={0=[param]},
formParams=[], indexToEncoded={}}

除了SpringMVC相关注解,当Dubbo服务自身也暴露Rest协议的时候,这些JAX-RS相关注解修饰的内容也会被解析成Rest元数据。比如,这个Dubbo服务RestService接口的代码如下:

@Service(protocol = { "dubbo", "rest" })
@Path("/")
public class StandardRestService implements RestService {@Override    @Path("param")    @GET    public String param(@QueryParam("param") String param) { return param;    }
}

RestService服务对应的Rest元数据内容如下:

RestMethodMetadata{method=MethodMetadata{name='param',
returnType='java.lang.String', params=[MethodParameterMetadata{index=0,
name='param', type='java.lang.String'}], method=public java.lang.String
com.alibaba.cloud.dubbo.service.SpringRestService.param(java.lang.String)},
request=RequestMetadata{method='GET', path='/param', params={param=[{param}]},
headers=[], consumes=[], produces=[]}, urlIndex=null, bodyIndex=null,
headerMapIndex=null, queryMapIndex=null, queryMapEncoded=false,
returnType='java.lang.String', bodyType='null', indexToName={0=[param]},
formParams=[], indexToEncoded={}}

从这两个例子的RestMethodMetadata内容可看出,SpringMVC和JAX-RS的Rest元数据是一致的。

Rest元数据出现的意义是为了匹配RestTemplate或OpenFeign的HTTP协议内容,匹配HTTP协议内容的目的是为了让HTTP协议内容关联上Dubbo服务。

使用RestTemplate或OpenFeign调用Dubbo服务会经历以下过程:

(1)根据服务名得到注册中心的Dubbo服务DubboMetadataService。

(2)使用DubboMetadataService里提供的getServiceRestMetadata方法获取要使用的Dubbo服务和对应的Rest元数据。

(3)基于Dubbo服务和Rest元数据构造GenericService。

(4)服务调用过程中使用GenericService发起泛化调用。

调用Dubbo服务的步骤

下面是使用Dubbo Spring Cloud调用Dubbo服务的开发步骤

(1)引入spring-cloud-starter-dubbo依赖。

<dependency>    <groupId>com.alibaba.cloud</groupId>    <artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

加上依赖后,将注册中心改成 spring-cloud 注册中心:

dubbo.registry.address=spring-cloud://localhost

(2)Provider 端接口加上 SpringMVC 相关注解或使用JAX-RS暴露Rest协议。

① 加上 SpringMVC 相关注解。

@Service(version = "1.0.0")
@RestController
public class SpringRestService implements RestService {@Override    @GetMapping("/param")    public String param(@RequestParam String param) {log("/param", param);        return param;    }@Override    @PostMapping("/params")   public String params(@RequestParam int a, @RequestParam String b) {log("/params", a + b);        return a + b;   }
}

② 使用 JAX-RS 暴露 Rest 协议。

配置文件暴露 rest 协议:

dubbo.protocols.rest.name=rest
dubbo.protocols.rest.port=9090
dubbo.protocols.rest.server=netty

接口使用 JAX-RS 注解修饰:

@Service(version = "1.0.0", protocol = { "dubbo", "rest" })
@Path("/")
public class StandardRestService implements RestService {@Override    @Path("param")    @GET    public String param(@QueryParam("param") String param) {log("/param", param);        return param;    }@Override   @Path("params")   @POST   public String params(@QueryParam("a") int a, @QueryParam("b") String b) { log("/params", a + b);        return a + b;    }
}

(3)Consumer 客户端加上 @DubboTransported 注解。

RestTemplate 和 OpenFeign 客户端都支持 @DubboTransported 注解。

RestTemplate 的使用方式如下:

@Bean
@LoadBalanced
@DubboTransported
public RestTemplate restTemplate() { return new RestTemplate();
}

OpenFeign 的使用方式如下:

@FeignClient("nacos-provider-lb")
@DubboTransported(protocol = "dubbo")
public interface DubboFeignRestService {@GetMapping("/param")    String param(@RequestParam("param") String param);@PostMapping("/params")    String params(@RequestParam("b") String paramB, @RequestParam("a") int paramA);}

(4)使用 RestTemplate 或 OpenFeign 调用 Dubbo 服务。

使用 RestTemplate调用的方式如下:

restTemplate.getForEntity(“http://dubbo-provider-service/param?param=deepinspringcloud”, String.class);

使用 OpenFeign调用的方式如下:

dubboFeignRestService.param(“deepinspringcloud”);

再谈路由和负载均衡

基于Netflix Ribbon的Spring Cloud 负载均衡设计了以下两个核心接口:

  • 路由对应的ILoadBalancer 接口,获取服务的 Server 实例列表。

  • 负载均衡对应的 IRule 接口,从服务的 Server 实例列表中根据负载均衡算法获取一个实例。

Spring Cloud应用的流量控制本质上就是对 Server 列表的控制:

  • 自定义 ILoadBalancer 接口,重写获取 Server 列表的逻辑(找出与当前请求匹配的 Server 列表)。

  • 自定义 IRule 接口,从所有的 Server 列表里找出与当前请求匹配的 Server。

很明显,第一种基于 ILoadBalancer 的方式更加合理。

我们来看 Dubbo 路由 Router 的实现。

Route 方法会从 Invoker 列表中过滤一批 Invoker,得到另一批 Invoker 列表:

public interface Router extends Comparable<Router> {<T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;}

然后 Router 在RouterChain 里被使用:

public class RouterChain<T> {public List<Invoker<T>> route(URL url, Invocation invocation) { List<Invoker<T>> finalInvokers = invokers;       for (Router router : routers) { finalInvokers = router.route(finalInvokers, url, invocation);   }      return finalInvokers;    }
}

RouterChain中的Router列表可以随意被添加,开发者可以基于SPI添加各式各样的Router。

笔者认为 Duboo 在路由侧的实现更加优雅。在Spring Cloud的设计中,Ribbon的路由设计与Request(流量)请求信息是解耦的,而 Dubbo 的 Router与Invocation(流量)是绑定的,这意味着路由过程可以直接基于流量特征进行动态操作,无须引入类似 ThreadLocal 的方式来传递流量特征。

另外,我们提到过Spring Cloud默认提供的ZoneAvoidanceRule这个IRule负载均衡策略,它内部会依赖ServerStats去根据Server状态摘除异常节点。

Spring Cloud 提供的其他 IRule 负载均衡策略并没有这个能力,如果想在自定义的 IRule 负载均衡也拥有摘除异常节点的能力,需要在代码里配合 ServerStats 使用。

▊ 更多相关内容请看《深入理解Spring Cloud与实战》一书!

▊《深入理解Spring Cloud与实战

方剑 编著

  • Spring Cloud Alibba创始人倾力打造

  • 巨献理论与实践相结合,核心知识点辅以案例讲解

本书共分10章,主要介绍Spring Cloud各个核心组件的设计原理,以及目前流行的Spring Cloud Alibba和 Netflix组件,并且剖析Spring Cloud对流处理、批处理,以及目前业界流行的Serverless的支持。在介绍各部分内容时,本书将理论与实践相结合,对每个核心知识点都给出了具体的案例应用,以帮助读者掌握核心组件的设计理念。

本书适合对Spring Cloud感兴趣并且想透彻理解Spring Cloud的读者阅读,也适合正在进行微服务选型的开发者阅读。

抽奖赠书

截止时间:2021年3月15日 17:00

如何抽奖:点击下方卡片,关注并回复关键词 :20210310 

下次你更希望我们送哪本书呢?

留言告诉我们!

赠书:深入理解 Spring Cloud 与实战相关推荐

  1. 《深入理解Spring Cloud与微服务构建》出版啦!

    作者简介 方志朋,毕业于武汉理工大学,CSDN博客专家,专注于微服务.大数据等领域,乐于分享,爱好开源,活跃于各大开源社区.著有<史上最简单的Spring Cloud教程>,累计访问量超过 ...

  2. 《深入理解 Spring Cloud 与微服务构建》第十章 路由网关 Spring Cloud Zuul

    <深入理解 Spring Cloud 与微服务构建>第十章 路由网关 Spring Cloud Zuul 文章目录 <深入理解 Spring Cloud 与微服务构建>第十章 ...

  3. 【Spring Cloud Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间这里只贴出关键部分代码的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证 ...

  4. 福利!送书《深入理解Spring Cloud与微服务构建》5本

    ​点击关注异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 点击此处参加活动--​直达活动传送门 作者回顾​ ​我从2017年初开始,着手于微服务的研究和开发,陆陆续续在博客等自媒体中 ...

  5. 《深入理解 Spring Cloud 与微服务构建》第十八章 使用 Spring Security OAuth2 和 JWT 保护微服务系统

    <深入理解 Spring Cloud 与微服务构建>第十八章 使用 Spring Security OAuth2 和 JWT 保护微服务系统 文章目录 <深入理解 Spring Cl ...

  6. 《深入理解 Spring Cloud 与微服务构建》第十七章 使用 Spring Cloud OAuth2 保护微服务系统

    <深入理解 Spring Cloud 与微服务构建>第十七章 使用 Spring Cloud OAuth2 保护微服务系统 文章目录 <深入理解 Spring Cloud 与微服务构 ...

  7. 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解

    <深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  8. 《深入理解 Spring Cloud 与微服务构建》第十五章 微服务监控 Spring Boot Admin

    <深入理解 Spring Cloud 与微服务构建>第十五章 微服务监控 Spring Boot Admin 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  9. 《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth

    <深入理解 Spring Cloud 与微服务构建>第十四章 服务链路追踪 Spring Cloud Sleuth 文章目录 <深入理解 Spring Cloud 与微服务构建> ...

最新文章

  1. 容器云之K8s自动化安装方式的选择
  2. 2014西安 H 有向图博弈 UVALive-7042
  3. java基本类型是类吗_Java基本数据类型和引用类型(一)
  4. 从构建分布式秒杀系统聊聊验证码
  5. 数据分析python有趣分享_Python有趣|数据分析三板斧
  6. mysql 5.5主从同步_MySQL5.5+配置主从同步并结合ThinkPHP5设置分布式数据库
  7. 从Java程序员进阶到架构师,6大核心技能要领详解
  8. 利用R和Octave绘制函数图像和求解方程
  9. 解决Flink案例DataStream中使用keyBy(0),keyBy弃用的问题
  10. flutter 刷脸_支付宝刷脸认证 - osc_bkdv2it5的个人空间 - OSCHINA - 中文开源技术交流社区...
  11. js获取DIV的位置坐标的三种方法!
  12. Python 破解验证码
  13. 使用uniapp时十分方便的登录静态模板
  14. ubuntu18.04安装有道词典等常用软件
  15. 电视剧《一代枭雄》观后感
  16. 红黑联盟现场实地渗透测试培训第一期
  17. wex5使用java语言_WeX5学习笔记之调用后端服务
  18. linux系统的超级管理员,系统的超级管理员:root《 Linux 文件与目录权限 》
  19. java有符号和无符号右移
  20. 【深度学习数据增强处理】imgaug Augment Polygons 对标注图片和polygons的数据增强

热门文章

  1. CUDA下的GPU编程入门--第一个CUDA程序
  2. 关闭占用指定端口的进程
  3. linux脚本编写图形,shell图形化界面脚本实现
  4. Mac中的文件如何拷贝到硬盘中?
  5. Access数据类型
  6. JavaScript下的setTimeout(fn,0)意味着什么?
  7. 复习之JavaScript基本语法(三)——getElement[...]方法使用
  8. 从球场捡拾矿泉水瓶的老人,看市场经济下的供求关系
  9. ML和PR相关书籍及下载
  10. shell正则表达式及一些排序命令(sort、uniq、tr)