架构演变
-
单一应用架构
所有功能部署在一个应用上,用于简化增删改查工作量的数据访问框架(ORM)是关键
-
垂直应用架构
将应用拆分成互不相干的几个应用,以提升效率,此时,用于加速前端页面开发的Web框架(MVC)是关键
-
分布式服务架构
将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键
-
流动计算架构
增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率,此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键
Dubbo架构
节点角色说明
节点
角色说明
Provider
暴露服务的服务提供方
Consumer
调用远程服务的服务消费方
Registry
服务注册与发现的注册中心
Monitor
统计服务的调用次数和调用时间的监控中心
Container
服务运行容器
调用关系说明
- 服务容器负责启动,加载,运行服务提供者
- 服务提供者在启动时,向注册中心注册自己提供的服务
- 服务消费者在启动时,向注册中心订阅自己所需的服务
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
Dubbo架构特性
-
连通性
- 服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
- 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
- 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含 网络开销
- 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
- 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
- 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
- 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
- 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
-
健壮性
- 监控中心宕掉不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
-
伸缩性
- 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
- 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者
-
升级性
- 当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。下图是未来可能的一种架构:
- 节点角色说明
节点
角色说明
Deployer
自动部署服务的本地代理
Repository
仓库用于存储服务应用发布包
Scheduler
调度中心基于访问压力自动增减服务提供者
Admin
统一管理控制台
Registry
服务注册与发现的注册中心
Monitor
统计服务的调用次数和调用时间的监控中心
配置标签
标签
用途
解释
<dubbo:service />
服务配置
用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心
引用配置
用于创建一个远程服务代理,一个引用可以指向多个注册中心
<dubbo:protocol />
协议配置
用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受
<dubbo:application />
应用配置
用于配置当前应用信息,不管该应用是提供者还是消费者
<dubbo:module />
模块配置
用于配置当前模块信息,可选
<dubbo:registry />
注册中心配置
用于配置连接注册中心相关信息
<dubbo:monitor />
监控中心配置
用于配置连接监控中心相关信息,可选
<dubbo:provider />
提供方配置
当ProtocolConfig和ServiceConfig某属性没有配置时,采用此缺省值,可选
<dubbo:consumer />
消费方配置
当ReferenceConfig某属性没有配置时,采用此缺省值,可选
<dubbo:method />
方法配置
用于ServiceConfig和ReferenceConfig指定方法级的配置信息
<dubbo:argument />
参数配置
用于指定方法参数配置
配置覆盖关系
- 方法级 > 接口级 > 全局配置
- 级别相同,则消费方优先
- 服务提供方配置,通过URL经由注册中心传递给消费方
- dubbo.properties通常用于共享公共配置,比如应用名,优先级低于XML配置
注解配置
服务提供方
import com.alibaba.dubbo.config.annotation.Service;
@Service(version="1.0.0")
public class FooServiceImpl implements FooService {
// ...
}
// XML配置
<!-- 公共信息,也可以用dubbo.properties配置 -->
<dubbo:application name="annotation-provider" />
<dubbo:registry address="127.0.0.1:4548" />
<!-- 扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类 -->
<dubbo:annotation package="com.foo.bar.service" />
服务消费方
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Component;
@Component
public class BarAction {
@Reference(version="1.0.0")
private FooService fooService;
}
// XML配置
<!-- 公共信息,也可以用dubbo.properties配置 -->
<dubbo:application name="annotation-consumer" />
<dubbo:registry address="127.0.0.1:4548" />
<!-- 扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类 -->
<dubbo:annotation package="com.foo.bar.action" />
也可以通过以下的XML配置完成annotation扫描,与前面的等价
<dubbo:annotation />
<context:component-scan base-package="com.foo.bar.service">
<context:include-filter type="annotation" expression="com.alibaba.dubbo.config.annotation.Service" />
</context:component-scan>
启动时检查
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=”true”,懒加载或通过API编程延迟引用服务时可check
-
关闭某个服务的启动时检查(没有提供者时报错)
<dubbo:reference interface="com.foo.BarService" check="false" />
-
关闭所有服务的启动时检查(没有提供者时报错)
<dubbo:consumer check="false" />
-
关闭注册中心启动时检查(注册订阅失败时报错)
<dubbo:registry check="false" />
集群容错
在集群调用失败时,Dubbo提供了多种容错方案,缺省为failover重试
Failover Cluster
失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。 可通过retries=”2” 来设置重试次数(不含第一次)
<dubbo:service retries="2" />
或
<dubbo:reference retries="2" />
或
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作
<dubbo:service cluster="failsafe" />
或
<dubbo:reference cluster="failsafe" />
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=”2” 来设置最大并行数
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
负载均衡
在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用
配置
<!-- 服务端服务级别 -->
<dubbo:service interface="..." loadbalance="roundrobin" />
<!-- 客户端服务级别 -->
<dubbo:reference interface="..." loadbalance="roundrobin" />
<!-- 服务端方法级别 -->
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="roundrobin" />
</dubbo:service>
<!-- 客户端方法级别 -->
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="roundrobin" />
</dubbo:reference>
Random LoadBalance
- 随机,按权重设置随机概率
- 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重
RoundRobin LoadBalance
- 轮询,按公约后的权重设置轮询比率
- 存在慢的提供者累计请求的问题,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上
LeastActive LoadBalance
- 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差
- 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大
ConsistentHash LoadBalance
-
一致性Hash,相同参数的请求总是发到同一提供者
-
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动
-
缺省只对第一个参数Hash,如果要修改,配置
<dubbo:parameter key="hash.arguments" value="0,1" />
-
缺省用 160 份虚拟节点,如果要修改,配置
<dubbo:parameter key="hash.nodes" value="320" />
线程模型
如果事件处理的逻辑能迅速完成,并且不会发起新的 IO 请求,比如只是在内存中记个标识,则直接在 IO 线程上处理更快,因为减少了线程池调度
但如果事件处理逻辑较慢,或者需要发起新的 IO 请求,比如需要查询数据库,则必须派发到线程池,否则 IO 线程阻塞,将导致不能接收其它请求
如果用 IO 线程处理事件,又在事件处理过程中发起新的 IO 请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁
因此,需要通过不同的派发策略和不同的线程池配置的组合来应对不同的场景
<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
Dispatcher
-
all
所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等
-
direct
所有消息都不派发到线程池,全部在 IO 线程上直接执行
-
message
只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在IO 线程上执行
-
execution
只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行
-
connection
在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池
ThreadPool
-
fixed
fixed 固定大小线程池,启动时建立线程,不关闭,一直持有(缺省)
-
cached
缓存线程池,空闲一分钟自动删除,需要时重建
-
limited
可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题
直连提供者
绕过注册中心,只测试指定服务提供者(只应在测试阶段使用)
<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" />
只订阅
为方便开发测试,经常会在线下共用一个所有服务可用的注册中心,让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,通过直连测试正在开发的服务
禁用注册配置
<dubbo:registry address="10.20.153.10:9090" register="false" />
或
<dubbo:registry address="10.20.153.10:9090?register=false" />
只注册
如果有两个镜像环境,两个注册中心,有一个服务只在其中一个注册中心有部署,另一个注册中心还没来得及部署,而两个注册中心的其它应用都需要依赖此服务。这个时候,可以让服务提供者方只注册服务到另一注册中心,而不从另一注册中心订阅服务
禁用订阅配置
<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" />
<dubbo:registry id="qdRegistry" address="10.20.141.150:9090" subscribe="false" />
或
<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" />
<dubbo:registry id="qdRegistry" address="10.20.141.150:9090?subscribe=false" />
静态服务
有时候希望人工管理服务提供者的上线和下线,此时需将注册中心标识为非动态管理模式
<dubbo:registry address="10.20.141.150:9090" dynamic="false" />
或
<dubbo:registry address="10.20.141.150:9090?dynamic=false" />
服务提供者初次注册时为禁用状态,需人工启用。断线时,将不会被自动删除,需人工禁用
多协议
Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议
不同服务在性能上适用不同协议进行传输,比如大数据用短连接协议,小数据大并发用长连接协议
多注册中心
Dubbo 支持同一服务向多注册中心同时注册,或者不同服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。另外,注册中心是支持自定义扩展的
多注册中心注册
<!-- 多注册中心配置 -->
<dubbo:registry id="hangzhouRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="qingdaoRegistry" address="10.20.141.151:9010" default="false" />
<!-- 向多个注册中心注册 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="hangzhouRegistry,qingdaoRegistry" />
多注册中心引用
<!-- 多注册中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 引用中文站服务 -->
<dubbo:reference id="chinaHelloService" interface="com.alibaba.hello.api.HelloServ ice" version="1.0.0" registry="chinaRegistry" /> <!-- 引用国际站站服务 -->
<dubbo:reference id="intlHelloService" interface="com.alibaba.hello.api.HelloServi ce" version="1.0.0" registry="intlRegistry" />
<!-- 多注册中心配置,竖号分隔表示同时连接多个不同注册中心,同一注册中心的多个集群地址用逗号分隔 -->
<dubbo:registry address="10.20.141.150:9090|10.20.154.177:9010" />
<!-- 引用服务 -->
<dubbo:reference id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" />
服务分组
当一个接口有多种实现时,可以用 group 区分
服务
<dubbo:service group="feedback" interface="com.xxx.IndexService" />
<dubbo:service group="member" interface="com.xxx.IndexService" />
引用
<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" />
<dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndewxService" />
<!-- 任意组(总是只调一个可用组的实现) -->
<dubbo:reference id="barService" interface="com.foo.BarService" group="*" />
多版本
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用
可以按照以下的步骤进行版本迁移:
-
在低压力时间段,先升级一半提供者为新版本
-
再将所有消费者升级为新版本
-
然后将剩下的一半提供者升级为新版本
<dubbo:service interface="com.foo.BarService" version="2.0.0" /> <dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" /> <!-- 不区分版本 --> <dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
分组聚合
消费方需从每种group中调用一次返回结果,合并结果返回,实现聚合菜单项
配置
搜索所有分组
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true" />
合并指定分组
<dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" />
指定方法合并结果,其它未指定的方法,将只调用一个Group
<dubbo:reference interface="com.xxx.MenuService" group="*">
<dubbo:method name="getMenuItems" merger="true" />
</dubbo:service>
某个方法不合并结果,其它都合并结果
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true">
<dubbo:method name="getMenuItems" merger="false" />
</dubbo:service>
指定合并策略,缺省根据返回值类型自动匹配,如果同一类型有两个合并器时,需指定合并器的名称
<dubbo:reference interface="com.xxx.MenuService" group="*">
<dubbo:method name="getMenuItems" merger="mymerge" />
</dubbo:service>
指定合并方法,将调用返回结果的指定方法进行合并,合并方法的参数类型必须是返回结果类型本身
<dubbo:reference interface="com.xxx.MenuService" group="*">
<dubbo:method name="getMenuItems" merger=".addAll" />
</dubbo:service>
回声测试
回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控
所有服务自动实现 EchoService 接口,只需将任意服务引用强制转型为 EchoService ,即可使用
<dubbo:reference id="memberService" interface="com.xxx.MemberService" />
代码
// 远程服务引用 MemberService
memberService = ctx.getBean("memberService");
EchoService echoService = (EchoService) memberService; // 强制转型为EchoService
// 回声测试可用性
String status = echoService.$echo("OK");
assert(status.equals("OK"));
上下文信息
上下文中存放的是当前调用过程中所需的环境信息,所有配置信息都将转换为URL的参数
服务消费方
// 远程调用
xxxService.xxx();
// 本端是否为消费端,这里会返回true
boolean isConsumerSide = RpcContext.getContext().isConsumerSide();
// 获取最后一次调用的提供方IP地址
String serverIP = RpcContext.getContext().getRemoteHost();
// 获取当前服务配置信息,所有配置信息都将转换为URL的参数
String application = RpcContext.getContext().getUrl().getParameter("application");
// 注意:每发起RPC调用,上下文状态会变化
yyyService.yyy();
服务提供方
public class XxxServiceImpl implements XxxService {
public void xxx() {
// 本端是否为提供端,这里会返回true
boolean isProviderSide = RpcContext.getContext().isProviderSide();
// 获取调用方IP地址
String clientIP = RpcContext.getContext().getRemoteHost();
// 获取当前服务配置信息,所有配置信息都将转换为URL的参数
String application = RpcContext.getContext().getUrl().getParameter("applicatio n");
// 注意:每发起RPC调用,上下文状态会变化
yyyService.yyy();
// 此时本端变成消费端,这里会返回false
boolean isProviderSide = RpcContext.getContext().isProviderSide();
}
}
异步调用
基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小
在 consumer.xml 中配置
<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
<dubbo:method name="findFoo" async="true" />
</dubbo:reference>
<dubbo:reference id="barService" interface="com.alibaba.bar.BarService">
<dubbo:method name="findBar" async="true" />
</dubbo:reference>
调用代码
// 此调用会立即返回null
fooService.findFoo(fooId);
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
Future<Foo> fooFuture = RpcContext.getContext().getFuture();
// 此调用会立即返回null
barService.findBar(barId);
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
Future<Bar> barFuture = RpcContext.getContext().getFuture();
// 此时findFoo和findBar的请求同时在执行,客户端不需要启动多线程来支持并行,而是借助NIO的非阻塞完成
// 如果foo已返回,直接拿到返回值,否则线程wait住,等待foo返回后,线程会被notify唤醒
Foo foo = fooFuture.get();
// 同理等待bar返回
Bar bar = barFuture.get();
// 如果foo需要5秒返回,bar需要6秒返回,实际只需等6秒,即可获取到foo和bar,进行接下来的处理
可以设置是否等待消息发出
-
sent=”true” 等待消息发出,消息发送失败将抛出异常
-
sent=”false” 不等待消息发出,将消息放入 IO 队列,即刻返回
<dubbo:method name="findFoo" async="true" sent="true" />
如果只是异步调用,完全忽略返回值,可以配置 return=”false”,以减少 Future 对象的创建和管理成本
<dubbo:method name="findFoo" async="true" return="false" />
本地存根
远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy
配置
<dubbo:service interface="com.foo.BarService" stub="true" />
或
<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />
提供Stub的实现
package com.foo;
public class BarServiceStub implements BarService {
private final BarService barService;
// 构造函数传入真正的远程代理对象
public (BarService barService) {
this.barService = barService;
}
public String sayHello(String name) {
// 此代码在客户端执行,你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
try {
return barService.sayHello(name);
} catch (Exception e) {
// 你可以容错,可以做任何AOP拦截事项
return "容错数据";
}
}
}
本地伪装
本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出 异常,而是通过 Mock 数据返回授权失败
配置
<dubbo:service interface="com.foo.BarService" mock="true" />
或
<dubbo:service interface="com.foo.BarService" mock="com.foo.BarServiceMock" />
在工程中提供Mock实现
package com.foo;
public class BarServiceMock implements BarService {
public String sayHello(String name) {
// 可以伪造容错数据,此方法只在出现RpcException时被执行
return "容错数据";
}
}
如果服务的消费方经常需要 try-catch 捕获异常,可以考虑改为 Mock(只有出现 RpcException 时才执行)实现,并在Mock实现中 return null,如果只是想简单的忽略异常,在 2.0.11 以上版本可用
<dubbo:service interface="com.foo.BarService" mock="return null" />
延迟暴露
如果服务需要预热时间,比如初始化缓存,等待相关资源就位等,可以使用 delay 进行延迟暴露
延迟5秒暴露服务
<dubbo:service delay="5000" />
延迟到 Spring 初始化完成后,再暴露服务
<dubbo:service delay="-1" />
并发控制
-
限制 com.foo.BarService 的每个方法,服务器端并发执行(或占用线程池线程数)不能超过 10 个
<dubbo:service interface="com.foo.BarService" executes="10" />
-
限制 com.foo.BarService 的 sayHello 方法,服务器端并发执行(或占用线程池线程数)不 能超过 10 个
<dubbo:service interface="com.foo.BarService"> <dubbo:method name="sayHello" executes="10" /> </dubbo:service>
-
限制 com.foo.BarService 的每个方法,每客户端并发执行(或占用连接的请求数)不能超过 10 个
<dubbo:service interface="com.foo.BarService" actives="10" /> 或 <dubbo:reference interface="com.foo.BarService" actives="10" />
-
限制 com.foo.BarService 的 sayHello 方法,每客户端并发执行(或占用连接的请求数)不 能超过 10 个
<dubbo:service interface="com.foo.BarService"> <dubbo:method name="sayHello" actives="10" /> </dubbo:service> 或 <dubbo:reference interface="com.foo.BarService"> <dubbo:method name="sayHello" actives="10" /> </dubbo:service>
连接控制
服务端连接控制
限制服务器端接受的连接不能超过 10 个
<dubbo:provider protocol="dubbo" accepts="10" />
或
<dubbo:protocol name="dubbo" accepts="10" />
客户端连接控制
限制客户端服务使用连接不能超过 10 个
<dubbo:reference interface="com.foo.BarService" connections="10" />
或
<dubbo:service interface="com.foo.BarService" connections="10" />
延迟连接
延迟连接用于减少长连接数。当有调用发起时,再创建长连接
<dubbo:protocol name="dubbo" lazy="true" />
优雅停机
kill PID 可以实现,kill -9 PID 不可以
原理
服务提供方
- 停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器
- 然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭
服务消费方
- 停止时,不再发起新的调用请求,所有新的调用在客户端即报错
- 然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭
设置停机超时时间,缺省超时时间是10秒
<dubbo:application ...>
<dubbo:parameter key="shutdown.timeout" value="60000" /> <!-- 单位毫秒 -->
</dubbo:application>