learn.lianglianglee.com/专栏/SpringCloud微服务实战(完)/14 Spring Cloud 与 Dubbo 冲突吗——强强联合.md.html
2022-05-11 18:57:05 +08:00

953 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>14 Spring Cloud 与 Dubbo 冲突吗——强强联合.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/SpringCloud微服务实战/00 开篇导读.md.html">00 开篇导读.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/01 以真实“商场停车”业务切入——需求分析.md.html">01 以真实“商场停车”业务切入——需求分析.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/02 具象业务需求再抽象分解——系统设计.md.html">02 具象业务需求再抽象分解——系统设计.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/03 第一个 Spring Boot 子服务——会员服务.md.html">03 第一个 Spring Boot 子服务——会员服务.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/04 如何维护接口文档供外部调用——在线接口文档管理.md.html">04 如何维护接口文档供外部调用——在线接口文档管理.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/05 认识 Spring Cloud 与 Spring Cloud Alibaba 项目.md.html">05 认识 Spring Cloud 与 Spring Cloud Alibaba 项目.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/06 服务多不易管理如何破——服务注册与发现.md.html">06 服务多不易管理如何破——服务注册与发现.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/07 如何调用本业务模块外的服务——服务调用.md.html">07 如何调用本业务模块外的服务——服务调用.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/08 服务响应慢或服务不可用怎么办——快速失败与服务降级.md.html">08 服务响应慢或服务不可用怎么办——快速失败与服务降级.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/09 热更新一样更新服务的参数配置——分布式配置中心.md.html">09 热更新一样更新服务的参数配置——分布式配置中心.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/10 如何高效读取计费规则等热数据——分布式缓存.md.html">10 如何高效读取计费规则等热数据——分布式缓存.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/11 多实例下的定时任务如何避免重复执行——分布式定时任务.md.html">11 多实例下的定时任务如何避免重复执行——分布式定时任务.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/12 同一套服务如何应对不同终端的需求——服务适配.md.html">12 同一套服务如何应对不同终端的需求——服务适配.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/13 采用消息驱动方式处理扣费通知——集成消息中间件.md.html">13 采用消息驱动方式处理扣费通知——集成消息中间件.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/SpringCloud微服务实战/14 Spring Cloud 与 Dubbo 冲突吗——强强联合.md.html">14 Spring Cloud 与 Dubbo 冲突吗——强强联合.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/15 破解服务中共性问题的繁琐处理方式——接入 API 网关.md.html">15 破解服务中共性问题的繁琐处理方式——接入 API 网关.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/16 服务压力大系统响应慢如何破——网关流量控制.md.html">16 服务压力大系统响应慢如何破——网关流量控制.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/17 集成网关后怎么做安全验证——统一鉴权.md.html">17 集成网关后怎么做安全验证——统一鉴权.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/18 多模块下的接口 API 如何统一管理——聚合 API.md.html">18 多模块下的接口 API 如何统一管理——聚合 API.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/19 数据分库后如何确保数据完整性——分布式事务.md.html">19 数据分库后如何确保数据完整性——分布式事务.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/20 优惠券如何避免超兑——引入分布式锁.md.html">20 优惠券如何避免超兑——引入分布式锁.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/21 如何查看各服务的健康状况——系统应用监控.md.html">21 如何查看各服务的健康状况——系统应用监控.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/22 如何确定一次完整的请求过程——服务链路跟踪.md.html">22 如何确定一次完整的请求过程——服务链路跟踪.md.html</a>
</li>
<li>
<a href="/专栏/SpringCloud微服务实战/23 结束语.md.html">23 结束语.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>14 Spring Cloud 与 Dubbo 冲突吗——强强联合</h1>
<p>微服务开发选型,到底是基于 Dubbo 还是 Spring Cloud相信不少开发的小伙伴都有拿这两个项目作过作比较的经历。本章节就带你走近这两个项目二者究竟是竞争发展还是融合共赢。</p>
<h3>项目发展简介</h3>
<p>我们还是先来看看 Dubbo 的发展历史:</p>
<ol>
<li>2012 年由阿里开源,在很短时间内,被许多互联网公司所采用。</li>
<li>由于公司策略发生变更2014 年 10 月项目停止维护,版本静止于 <a href="https://github.com/apache/dubbo/releases/tag/dubbo-2.4.11">dubbo-2.4.11</a>。处于非维护期间,当当网基于分支重新开源了 DubboX 框架。</li>
<li>2017 年 9 月,阿里宣布重启 Dubbo 项目,重新发布新版本 <a href="https://github.com/apache/dubbo/releases/tag/dubbo-2.5.4">dubbo-2.5.4</a>,并将其作为社区开源产品长期推进下去,此后版本迭代开始重新发力。</li>
<li>2018 年 2 月,阿里将 Dubbo 捐献给 Apache 基金会孵化。</li>
<li>2019 年 5 月Apache Dubbo 正式升级为顶级项目。</li>
</ol>
<p>Dubbo 定位于高性能、轻量级的开源 Java RPC 框架随着社区的不断丰富Dubbo 生态越来越繁荣。</p>
<p><img src="assets/7ccc59c0-9b1f-11ea-9731-db13af306b3f" alt="img" /></p>
<p>官方为快速开发者上手 Dubbo 应用,仿照 start.spring.io推出快速生成基于 Spring Boot 的 Dubbo 项目的网站:<a href="http://start.dubbo.io/">http://start.dubbo.io/</a>。更详细的文档,可到官网查看。</p>
<p>Spring Cloud 的历史很短Spring Cloud 源于 Spring来梳理下 Spring 的发展情况:</p>
<ol>
<li>最早可以追溯到 2002 年,由 Rod Johnson 撰写一本名为&quot;Expoert One-on-One J2EE &quot;设计和开发的书。</li>
<li>2003 年 2 月左右RodJuergen 和 Yann 于 开始合作开发项目Yann 为新框架创造了“Spring”的名字。Yann Caroff 在早期离开了团队Rod Johnson 在 2012 年离开Juergen Hoeller 仍然是 Spring 开发团队的积极成员。</li>
<li>2007 年 11 月,在 Rod 领导下管理 Interface21 项目更名为 SpringSource。</li>
<li>2007 年SpringSource 从基准资本获得了 A 轮融资1000 万美元SpringSource 在此期间收购了多家公司,如 HypericG2One 等。</li>
<li>2009 年 8 月SpringSource 以 4.2 亿美元被 VMWare 收购。</li>
<li>2012 年 7 月Rod Johnson 离开了团队。</li>
<li>2013 年 4 月VMware 和 EMC 通过 GE 投资创建了一家名为 Pivotal 的合资企业。所有的 Spring 应用项目都转移到了 Pivotal。</li>
<li>2014 年 Pivotal 发布了 Spring Boot。</li>
<li>2015 年,戴尔又并购了 EMC。</li>
<li>2015 年 Pivotal 发布了 Spring Cloud。</li>
<li>2018 年 Pivotal 公司在纽约上市。</li>
</ol>
<p>从漫长的发展历史中,可见 Spring 的发展也是一波三折。事实上,做 Java 开发基本绕不开 SpringSpring 社区对 Java 的发展有着极大的影响力,而 Spring Cloud 则是基于 Spring、 Spring Boot 生态提供了一整套开箱即用的全家桶式的解决方案,极大的方便了开发者快速上手微服务开发,背后的商业公司更是为其提供了强大的支撑,同时不少核心项目组件能看到 Netflix OSS 的身影,如 Eureka 等,均在 Netflix 线上的分布式生产环境中已经得到很好的技术验证,无形中增强了信用背书。</p>
<p>Dubbo 在国内有较大的市场影响力,但国际市场上 Spring Cloud 的占有率要比 Dubbo 大,毕竟原生的英文环境及 Spring 社区的庇荫都是生态繁荣的优势。随着 Dubbo 正式成为 Apache 顶级项目后,相信未来在国际市场上的采用度会越来越高。</p>
<h3>技术选型困扰</h3>
<p>二者的交集是发现在 2015 年左右,一方面 Dubbo 在国内应用广泛,以简单易上手、高性能著称,遗憾之处在于社区几乎停滞。而此时 Spring Cloud 以全新姿态面世,基于 Spring Boot 的约定优于配置的原则,在 Java 轻量级开发中迅速传播开来,但组件种类多、资料少、学习曲线高也是不争的事实。</p>
<p>早期大家做技术选型时经常会将二者拿出来作比较典型的可参照《微服务架构的基础框架选择Spring Cloud 还是 Dubbo》一文。2016 年公司在做技术选型时,我同样也面临这个问题,鉴于当时的业务需求及团队的技术储备能力,最终还是选择了处在非维护期的 Dubbo后期无法满足需求时再考虑重构。</p>
<p>Spring Cloud 早期的服务注册中心是基于 EurekaDubbo 采用的注册中心是 ZooKeeper一套服务存在两个服务管理方案复杂度相当高又各自在各自的领域内有各自的解决方案要整合起来也非易事。</p>
<p>近两年来 Spring Cloud Alibaba 的出现,这种二选一的局面得到了极大的改善。一方面,可以替代原项目中一些不再维护的项目功能。另一方面,可以将阿里技术生态与 Spring Cloud 生态融合起来。二者都可以采用 Nacos 作为服务注册中心,同时也完美替代 Spring Cloud Config 提供了更简洁直观的配置管理,降低了复杂度。另外,也为 Spring Cloud 生态中也引入了 RPC 解决方案——Dubbo与 REST 方式形成互补。</p>
<h3>二者融合实战</h3>
<p>现在我们就通过一个业务功能——会员通过积分兑换来洗车券去洗车,将两个项目融合在一起。</p>
<h4><strong>新增 parking-carwash 父项目</strong></h4>
<p>此模块需要完成对外提供 RPC 接口的功能,代码结构如下</p>
<p><img src="assets/e4caf860-9b1f-11ea-8fdb-97e36705b18a" alt="img" /></p>
<p>下属两个子项目模块api 项目只是简单的 Java 项目,构建成 jar 包供外部项目依赖调用serv 项目基于 Spring Boot 提供实际业务服务,以 jar 的形式独立运行。</p>
<h4><strong>parking-carwash-serv 服务提供者</strong></h4>
<p>在 api 模块中编写接口,同时将对应的实体放在这里,以便被依赖时正常使用。参照之前的方式配置基本的基础组件,再引入 Dubbo 相关的 jar配置如下</p>
<pre><code class="language-xml">&lt;!-- 必须包含 spring-boot-starter-actuator 包,不然启动会报错。 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-actuator&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;!-- Dubbo Spring Cloud Starter --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-dubbo&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre>
<p>为体验下 yml 配置文件的应用情况,本项目中引入 bootstrap.yml 文件,完全可以采用一个 application 配置文件:</p>
<pre><code class="language-yaml">spring:
application:
name: carwash-service
main:
allow-bean-definition-overriding: true
cloud:
nacos:
discovery:
enabled: true
register-enabled: true
server-addr: 127.0.0.1:8848
</code></pre>
<p>application.properties 中配置 Dubbo</p>
<pre><code class="language-properties"># dubbo config
dubbo.protocols.dubbo.name=dubbo
dubbo.protocols.dubbo.port=-1
dubbo.scan.base-packages=com.mall.parking.carwash.serv.service
dubbo.registry.address=spring-cloud://127.0.0.1
dubbo.registry.register=true
dubbo.application.qos.enable=false
#此配置项为了防止 nacos 大量的 naming 日志输出而配置
logging.level.com.alibaba.nacos.client.naming=error
</code></pre>
<p>编写接口及实现类:</p>
<pre><code class="language-java">public interface WashService {
int wash(String json) throws BusinessException;
}
@Service(protocol = &quot;dubbo&quot;)
@Slf4j
public class WashServiceImpl implements WashService {
@Autowired
CarWashMapper carWashMapper;
@Override
public int wash(String json) throws BusinessException {
CarWash carWash = JSONObject.parseObject(json, CarWash.class);
int rtn = carWashMapper.insertSelective(carWash);
log.info(&quot;car wash data = &quot; + json + &quot;&gt; write suc...&quot;);
return rtn;
}
}
</code></pre>
<p>注意:@Service 注解不再使用 Spring 的,而是采用 Dubbo 提供的注解 org.apache.dubbo.config.annotation.Service注释中同时提供了多种属性值用于配置接口的多种特性比如服务分组、服务版本、服务注册是否延迟、服务重试次数等等依实际使用情况而定。</p>
<p>Application 启动类,与一般 Spring Cloud 的启动类无异。启动后,在 Nacos 的服务列表中可以看到本模块的服务已经注册成功。</p>
<h4><strong>parking-member 服务消费者</strong></h4>
<p>在前期构建完成的 parking-member 项目中引入 Dubbo 的 jar 和 api 接口 jar。</p>
<pre><code class="language-xml">&lt;!-- Dubbo Spring Cloud Starter --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-dubbo&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.mall.parking.root&lt;/groupId&gt;
&lt;artifactId&gt;parking-carwash-api&lt;/artifactId&gt;
&lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p>application.properties 配置:</p>
<pre><code class="language-properties">#dubbo config
dubbo.registry.address=nacos://localhost:8848
dubbo.application.qos.enable=false
dubbo.cloud.subscribed-services=carwash-service
spring.main.allow-bean-definition-overriding=true
#不检测服务提供者是否在线,生产环境建议开启
dubbo.consumer.check=false
#more naming logs output,config this to avoid more log output
logging.level.com.alibaba.nacos.client.naming=error
</code></pre>
<p>编写服务调用类:</p>
<pre><code class="language-java"> @Reference
WashService washService;//像调用本地 jar 一样,调用服务
/**
* {&quot;plateNo&quot;:&quot;湘 AG7890&quot;,&quot;ticketCode&quot;:&quot;Ts0999&quot;}
*
* @param json
* @return
* @throws BusinessException
*/
@PostMapping(&quot;/wash&quot;)
public CommonResult&lt;Integer&gt; wash(String json) throws BusinessException {
log.debug(&quot;add vehicle = &quot; + json);
CommonResult&lt;Integer&gt; result = new CommonResult&lt;&gt;();
int rtn = washService.wash(json);
result.setRespData(rtn);
return result;
}
</code></pre>
<h4><strong>测试</strong></h4>
<p>服务提供者启动后,再启动会员模块服务,使用 Postman访问 vehicle/wash 方法,可以看到服务正常调用,数据写入 park-carwash 数据库。</p>
<p>至此,我们将 Dubbo 与 Spring Cloud 两大项目完美整合到一个项目中,项目中既可以用到 RPC 框架的高效能,也可以享受到全家桶的便利性。</p>
<p>留下一题思考题:</p>
<ul>
<li>有两种引入 Dubbo 的 starter 方式spring-cloud-starter-dubbo 和 dubbo-spring-boot-starter这两种方式有什么区别呢</li>
</ul>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/SpringCloud微服务实战/13 采用消息驱动方式处理扣费通知——集成消息中间件.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/SpringCloud微服务实战/15 破解服务中共性问题的繁琐处理方式——接入 API 网关.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709975a8aabd3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>