blob: 0ca551e45ac5174d81a1f435fb459446a8940c70 [file] [log] [blame]
<!DOCTYPE html>
<html class="writer-html5" lang="en" >
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="../img/favicon.ico" />
<title>Reactive - ServiceComb Java Chassis 开发指南</title>
<link rel="stylesheet" href="../css/theme.css" />
<link rel="stylesheet" href="../css/theme_extra.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/github.min.css" />
<script>
// Current page data
var mkdocs_page_name = "Reactive";
var mkdocs_page_input_path = "general-development/reactive.md";
var mkdocs_page_url = null;
</script>
<script src="../js/jquery-3.6.0.min.js" defer></script>
<!--[if lt IE 9]>
<script src="../js/html5shiv.min.js"></script>
<![endif]-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</head>
<body class="wy-body-for-nav" role="document">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
<div class="wy-side-scroll">
<div class="wy-side-nav-search">
<a href="../index.html" class="icon icon-home"> ServiceComb Java Chassis 开发指南
</a>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../toc.html">目录</a>
</li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../index.html">概述</a>
</li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../start/catalog.html">快速入门</a>
</li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../start/design.html">设计选型参考</a>
</li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../build-provider/definition/service-definition.html">微服务定义</a>
</li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../build-provider/catalog.html">开发服务提供者</a>
</li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../build-consumer/catalog.html">开发服务消费者</a>
</li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="catalog.html">通用功能开发</a>
</li>
</ul>
<p class="caption"><span class="caption-text">多样化的通信协议功能参考</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../transports/introduction.html">多协议介绍</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../transports/rest-over-servlet.html">REST over Servlet</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../transports/rest-over-vertx.html">REST over Vertx</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../transports/http2.html">REST over HTTP2</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../transports/highway-rpc.html">Highway</a>
</li>
</ul>
<p class="caption"><span class="caption-text">多样化的服务注册与发现功能参考</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../registry/introduction.html">注册发现说明</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../registry/service-center.html">使用服务中心</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../registry/local-registry.html">本地注册发现</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../registry/distributed.html">去中心化注册发现</a>
</li>
</ul>
<p class="caption"><span class="caption-text">管理服务配置</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../config/general-config.html">通用配置说明</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../config/read-config.html">在程序中读取配置信息</a>
</li>
</ul>
<p class="caption"><span class="caption-text">服务治理功能参考</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../references-handlers/intruduction.html">处理链介绍</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../references-handlers/loadbalance.html">负载均衡</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../references-handlers/ratelimit.html">限流</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../references-handlers/router.html">灰度发布</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../references-handlers/fault-injection.html">故障注入</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../references-handlers/governance.html">流量特征治理</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../references-handlers/fail-retry.html">快速失败和重试</a>
</li>
</ul>
<p class="caption"><span class="caption-text">网关功能参考</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../edge/open-service.html">介绍</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../edge/by-servicecomb-sdk.html">使用 Edge Service 做网关</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../edge/zuul.html">使用 `zuul` 和 `spring cloud gateway` 做网关</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../edge/nginx.html">nginx 网关简单介绍</a>
</li>
</ul>
<p class="caption"><span class="caption-text">安全特性参考</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../references-handlers/publickey.html">公钥认证</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../security/tls.html">使用TLS通信</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../security/shi-yong-rsa-ren-zheng.html">使用RSA认证</a>
</li>
</ul>
<p class="caption"><span class="caption-text">服务打包和运行</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../packaging/standalone.html">以standalone模式打包</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../packaging/web-container.html">以WEB容器模式打包</a>
</li>
</ul>
<p class="caption"><span class="caption-text">专题文章</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../using-java-chassis-in-spring-boot/using-java-chassis-in-spring-boot.html">在Spring Boot中使用java chassis</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../featured-topics/features.html">新功能介绍系列文章</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../featured-topics/compatibility.html">兼容问题和兼容性策略</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../featured-topics/upgrading.html">升级指导系列文章</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../featured-topics/performance.html">性能问题分析和调优</a>
</li>
</ul>
<p class="caption"><span class="caption-text">常用配置项参考</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../config-reference/rest-transport-client.html">REST Transport Client 配置项</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../config-reference/config-center-client.html">Config Center Client 配置项</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../config-reference/service-center-client.html">Service Center Client 配置项</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../config-reference/kie-client.html">ServiceComb Kie Client 配置项</a>
</li>
</ul>
<p class="caption"><span class="caption-text">常见问题</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../question-and-answer/faq.html">FAQ</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../question-and-answer/question_answer.html">Q & A</a>
</li>
<li class="toctree-l1"><a class="reference internal" href="../question-and-answer/interface-compatibility.html">微服务接口兼容常见问题</a>
</li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" role="navigation" aria-label="Mobile navigation menu">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">ServiceComb Java Chassis 开发指南</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content"><div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html" class="icon icon-home" alt="Docs"></a> &raquo;</li>
<li>Reactive</li>
<li class="wy-breadcrumbs-aside">
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div class="section" itemprop="articleBody">
<h2 id="producer">简单同步模式的Producer:</h2>
<p>示例代码:</p>
<pre><code class="language-java">@GetMapping(path = &quot;/hello/{name}&quot;)
public String hello(@PathVariable(name = &quot;name&quot;) String name){
return &quot;hello &quot; + name;
}
</code></pre>
<p>与此对应的处理流程如下:</p>
<p><img alt="" src="../assets/reactive/normalSync.png" /></p>
<p>这是传统典型的工作模型,核心思想是不阻塞网络线程,将业务放在独立的线程中处理(为了简化表达,executor中只画一个线程)</p>
<p>一般情况下,此模式问题不大。</p>
<h2 id="_1">嵌套同步调用:</h2>
<p>不是所有的业务都是简单处理一下,就可以直接应答,可能还需要调用其他微服务.</p>
<p>示例代码:</p>
<pre><code class="language-java">public interface Intf{
String hello(String name);
}
@GetMapping(path = &quot;/hello/{name}&quot;)
public String hello(@PathVariable(name = &quot;name&quot;) String name){
return &quot;from remote: hello &quot; + intf.hello(name);
}
</code></pre>
<p>与此对应的处理流程如下:</p>
<p><img alt="" src="../assets/reactive/nestedSync.png" /></p>
<p>根据这个流程的特点,可以看到会产生以下结果:</p>
<ul>
<li>
<p>因为是同步调用,在“Other microservices”未应答之前“Microservice A”的调用线程一直处于阻塞等待状态,不处理任何其他事务</p>
</li>
<li>
<p>当Executor中所有线程都在等待远程应答时,所有新的请求都只能在Queue中排队,得不到处理,此时整个系统相当于停止工作了</p>
</li>
<li>
<p>要增加处理能力,只能增加Executor中的线程数,而操作系统并不能无限地增加线程数,事实上增加线程数带来的收益是一个抛物线模型,超出一定的临界值后,系统的处理能力其实会下降,而这个临界值并不会太大</p>
</li>
<li>
<p>当业务逻辑中,需要多次进行远程同步操作时,会更加恶化这个现象</p>
</li>
</ul>
<h2 id="_2">嵌套同步调用的“错误”优化:</h2>
<p>针对前一场景,有人会认为将“Invoke producer method”丢进另一个线程池,可以解决问题,包括以下处理方式:</p>
<ul>
<li>
<p>在producer method打标注@Async,由AOP负责将对该方法的调用丢进其他线程池去</p>
</li>
<li>
<p>在producer method内部通过业务代码转移线程</p>
</li>
</ul>
<p>形成以下流程:</p>
<p><img alt="" src="../assets/reactive/wrongSyncOptimization.png" /></p>
<p>根据这个流程的特点,可以看到会产生以下结果:</p>
<ul>
<li>
<p>“Invoke producer method”必须立即返回,否则Executor线程还是得不到释放</p>
</li>
<li>
<p>“Invoke producer method”必须提供一个新的机制告知调用流程自己的返回值,不是最终返回值(当前没有这个机制)</p>
</li>
<li>
<p>虽然Executor线程释放了,但是Customer Executor,其实还是阻塞住,在等待远端应答,整个系统的阻塞状态并没有得到改变;而且还凭空多了一次线程切换</p>
</li>
<li>
<p>该机制看上去,唯一的作用,是释放了executor线程,让executor线程有机会处理其他请求,这相当于隔离仓的概念,处理速度慢的业务不要影响其他业务;但是这个概念serviceComb是可以直接支持的,可以配置指定的业务方法独占全新的executor,这样整个流程就跟“嵌套同步调用”完全一样,流程更简单,而不需要在“Invoke producer method”层次来做这个事情</p>
</li>
</ul>
<h2 id="reactive">纯Reactive机制</h2>
<p>示例代码:</p>
<pre><code class="language-java">public interface Intf{
CompletableFuture&lt;String&gt; hello(String name);
}
@GetMapping(path = &quot;/hello/{name}&quot;)
public CompletableFuture&lt;String&gt; hello(@PathVariable(name = &quot;name&quot;) String name){
CompletableFuture&lt;String&gt; future = new CompletableFuture&lt;&gt;();
intf.hello(name).whenComplete((result, exception) -&gt; {
if (exception == null) {
future.complete(&quot;from remote: &quot; + result);
return;
}
future.completeExceptionally(exception);
});
return future;
}
</code></pre>
<p>与此对应的处理流程如下:</p>
<p><img alt="" src="../assets/reactive/pureReactive.png" /></p>
<ul>
<li>
<p>与传统流程不同的是,所有功能都在eventloop中执行,并不会进行线程切换</p>
</li>
<li>
<p>橙色箭头走完后,对本线程的占用即完成了,不会阻塞等待应答,该线程可以处理其他任务</p>
</li>
<li>
<p>当收到远端应答后,由网络数据驱动开始走红色箭头的应答流程</p>
</li>
<li>
<p>只要有任务,线程就不会停止,会一直执行任务,可以充分利用cpu资源,也不会产生多余的线程切换,去无谓地消耗cpu。</p>
</li>
</ul>
<p>因为同步模式下,需要大量的线程来提升并发度,而大量的线程又带来线程切换的额外消耗。</p>
<p>测试数据表明,reactive模式,只需要消耗同步模式不到一半的cpu,即可达到或超过同步模式的tps,并且时延更低。</p>
<h2 id="reactive_1">混合Reactive机制</h2>
<p>Reactvie要求:所有在eventloop中执行的逻辑,不允许有任何的阻塞动作,包括不限于wait、sleep、巨大循环、同步查询DB等等。</p>
<p>serviceComb底层是基于vertx的,vertx生态中有jdbc、mq、zooKeeper等等各种丰富组件的reactive驱动,一般情况下都可以满足要求。</p>
<p>但是有的场景下,确实有的同步操作无法避免,比如:</p>
<ul>
<li>
<p>私有的安全加固的redis,只提供了同步驱动</p>
</li>
<li>
<p>较复杂的业务运算</p>
</li>
<li>
<p>……</p>
</li>
</ul>
<p>此时,可以将这些同步的逻辑抽取出来放到线程池中去处理,而其他部分仍然使用reactive的流程。</p>
<h2 id="reactive_2">关于reactive的一些说明:</h2>
<ul>
<li>
<p>Producer:</p>
</li>
<li>
<p>producer是否使用reactive与consumer如何调用,没有任何联系</p>
</li>
<li>
<p>当operation返回值为CompletableFuture类型时,默认此operation工作于reactive模式,此时如果需要强制此operation工作于线程池模式,需要在microservice.yaml中明确配置;假设某operation,其schemaId为sid,operationId为asyncQuery,则需要进行以下配置:</p>
</li>
</ul>
<pre><code>servicecomb:
executors:
Provider:
sid.asyncQuery: cse.executor.groupThreadPool
</code></pre>
<p>这里的cse.executor.groupThreadPool是serviceComb内置的默认线程池,用户可以任意指定为自己的定制线程池。</p>
<ul>
<li>
<p>Consumer:</p>
</li>
<li>
<p>consumer是否使用reactive与producer如何实现,没有任何联系</p>
</li>
<li>
<p>当前只支持透明RPC模式,使用JDK原生的CompletableFuture来承载此功能</p>
<p>completableFuture的when、then等等功能都可直接使用</p>
<p>但是completableFuture的async系列功能,其实是另外启线程池来执行功能,不建议使用</p>
<p>关于RxJava Observable的支持后续会加入</p>
<p>关于AsyncRestTemplate的支持后续会加入</p>
</li>
</ul>
</div>
</div><footer>
<hr/>
<div role="contentinfo">
<!-- Copyright etc -->
</div>
Built with <a href="https://www.mkdocs.org/">MkDocs</a> using a <a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<div class="rst-versions" role="note" aria-label="Versions">
<span class="rst-current-version" data-toggle="rst-current-version">
</span>
</div>
<script>var base_url = '..';</script>
<script src="../js/theme_extra.js" defer></script>
<script src="../js/theme.js" defer></script>
<script src="../search/main.js" defer></script>
<script defer>
window.onload = function () {
SphinxRtdTheme.Navigation.enable(true);
};
</script>
</body>
</html>