之前项目中用过dubbo做分布式,最近想把这个框架的搭建及一些基本功能的使用记录下来。
注册中心用zookeeper
架构
Provider 暴露服务的服务提供⽅
Consumer 调⽤远程服务的服务消费⽅
Registry 服务注册与发现的注册中⼼
Monitor 统计服务的调⽤次调和调⽤时间的监控中⼼
Container 服务运⾏容器
Dubbo 架构具有以下⼏个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性
创建一个dubbo-parent的maven project, packaging类型设置为pom,
在dubbo-parent下创建3个maven module,分别是dubbo-consumer、dubbo-provider,和dubbo-api packaging类型设置为jar
dubbo-parent的pom依赖及插件如下
org.springframework.boot
spring-boot-starter-parent
1.5.2.RELEASE
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
1.0.0
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- Spring Boot Dubbo 依赖 -->
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
<version>${dubbo-spring-boot}</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
</dependencies>
<modules>
<module>dubbo-api</module>
<module>dubbo-provider</module>
<module>dubbo-consumer</module>
dubbo-api项目创建一个接口
public interface DubboService {
public void sayHello(String str);
}
dubbo-provider项目
@Service(version = “1.0.0”)
public class DubboServiceImpl implements DubboService{
private static final Logger logger = LoggerFactory.getLogger(DubboServiceImpl.class);
@Override
public void sayHello(String str) {
logger.info("打印信息:" + str);
}
}
pom中要依赖dubbo-api项目
<dependency>
<groupId>com.zookeeper</groupId>
<artifactId>dubbo-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
<version>${dubbo-spring-boot}</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
配置文件
server:
port: 8092
spring:
dubbo:
application:
name: dubbo-provider
registry:
protocol: zookeeper
address: 127.0.0.1:2181, 127.0.0.1:2182
protocol:
name: dubbo
port: 20880
scan: org.dubbo.provider.service.impl
dubbo-consumer项目 pom配置
<dependency>
<groupId>com.zookeeper</groupId>
<artifactId>dubbo-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
代码调用接口
@RestController
public class DubboController {
@Reference(version = "1.0.0")
private DubboService dubboService;
@GetMapping("/sayHello")
public String sayHello() {
dubboService.sayHello("consumer 请求dubbo服务");
return "请求成功";
}
}
配置文件
server:
port: 8800
spring:
dubbo:
application:
name: dubbo-consumer
registry:
protocol: zookeeper
address: 127.0.0.1:2181, 127.0.0.1:2182
scan: org.dubbo.consumer.controller
启动zookeeper, 在启动dubbo-provider, 最后启动dubbo-consumer项目,打开浏览器,输入地址localhost:8800/sayHello
控制栏输出: 打印信息:consumer 请求dubbo服务
通过api调用服务
创建项目dubbo-generic-consumer
public class BaseInvoker2 {
private ReferenceConfig<GenericService> config = null;
public BaseInvoker2() {
config = new ReferenceConfig<>();
ApplicationConfig configApplication = new ApplicationConfig();
configApplication.setName("dubbo-generic-consumer");
configApplication.setVersion("1.0.0");
config.setApplication(configApplication);
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("127.0.0.1:2181, 127.0.0.1:2182");
config.setRegistry(registryConfig);
config.setProtocol("dubbo");
}
public void setInterface(String interfaceName) {
config.setInterface(interfaceName);
}
public void setVersion(String version) {
config.setVersion(version);
}
public Object invokeMethod(String method, String[] parameterTypes, Object[] values) {
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
GenericService service = cache.get(config);
return service.$invoke(method, parameterTypes, values);
}
public void setGeneric(boolean flag) {
config.setGeneric(flag);
}
public void setGroup(String group) {
config.setGroup(group);
}
}
@RestController
public class GenericConsumerController {
private static final Logger logger = LoggerFactory.getLogger(GenericConsumerController.class);
@GetMapping("/infos")
public String getGeneric() {
BaseInvoker2 baseInvoker2 = new BaseInvoker2();
baseInvoker2.setInterface("org.dubbo.api02.DubboService");
baseInvoker2.setVersion("1.0.0");
baseInvoker2.setGeneric(true);
String[] parameterTypes = { "java.lang.String", "java.lang.String", "java.lang.Integer" };
Object[] values = { "王五", "男", 35 };
Object result = baseInvoker2.invokeMethod("getInfo", parameterTypes, values);
logger.info(result.toString());
return null;
}
@GetMapping("/diff")
public String getDifferences() {
BaseInvoker2 baseInvoker2 = new BaseInvoker2();
baseInvoker2.setInterface("org.dubbo.api02.GroupService");
baseInvoker2.setVersion("1.0.0");
baseInvoker2.setGeneric(true);
baseInvoker2.setGroup("a1");
String[] parameterTypes = {};
Object[] values = {};
Object result = baseInvoker2.invokeMethod("groupInfo", parameterTypes, values);
logger.info(result.toString());
return (String) result;
}
@GetMapping("/diff2")
public String getDifference() {
BaseInvoker2 baseInvoker2 = new BaseInvoker2();
baseInvoker2.setInterface("org.dubbo.api02.GroupService");
baseInvoker2.setVersion("1.0.0");
baseInvoker2.setGeneric(true);
baseInvoker2.setGroup("a2");
String[] parameterTypes = {};
Object[] values = {};
Object result = baseInvoker2.invokeMethod("groupInfo", parameterTypes, values);
logger.info(result.toString());
return (String) result;
}
}
启动时检查
关闭某个服务的启动时检查 (没有提供者时报错):
<dubbo:reference interface=“com.foo.BarService” check=“false” />
关闭所有服务的启动时检查 (没有提供者时报错):
<dubbo:consumer check=“false” />
关闭注册中⼼启动时检查 (注册订阅失败时报错):
<dubbo:registry check=“false” />
集群容错
重试次数配置如下:
<dubbo:service retries=“2” />
或
<dubbo:reference retries=“2” />
或
dubbo:reference
<dubbo:method name=“findFoo” retries=“2” />
</dubbo:reference>
按照以下示例在服务提供⽅和消费⽅配置集群模式
<dubbo:service cluster=“failsafe” />
或
<dubbo:reference cluster=“failsafe” />
负载均衡
Dubbo 提供了多种均衡策略,缺省为 random 随机调⽤
Random LoadBalance
随机,按权重设置随机概率。
RoundRobin LoadBalance
轮循,按公约后的权重设置轮循⽐率
LeastActive LoadBalance
最少活跃调⽤数,相同活跃数的随机,活跃数指调⽤前后计数差。
ConsistentHash LoadBalance
⼀致性 Hash,相同参数的请求总是发到同⼀提供者
服务端服务级别
<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>
线程模型
<dubbo:protocol name=“dubbo” dispatcher=“all” threadpool=“fixed” threads=“100” />
Dispatcher
all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,⼼跳等。
direct 所有消息都不派发到线程池,全部在 IO 线程上直接执⾏。
message 只有请求响应消息派发到线程池,其它连接断开事件,⼼跳等消息,直接在 IO 线程上执⾏。
execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,⼼跳等消息,直接在 IO 线程上执 ⾏。 connection 在 IO 线程上,将连接断开事件放⼊队列,有序逐个执⾏,其它消息派发到线程池。
ThreadPool
fixed 固定⼤⼩线程池,启动时建⽴线程,不关闭,⼀直持有。(缺省)
cached 缓存线程池,空闲⼀分钟⾃动删除,需要时重建。
limited 可伸缩线程池,但池中的线程数只会增⻓不会收缩。只增⻓不收缩的⽬的是为了避免收缩时突然来了⼤ 流量引起的性能问题。
服务分组
当⼀个接⼝有多种实现时,可以⽤ 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=“1.0.0” />
新版本服务提供者配置:
<dubbo:service interface=“com.foo.BarService” version=“2.0.0” />
⽼版本服务消费者配置:
<dubbo:reference id=“barService” interface=“com.foo.BarService” version=“1.0.0” />
新版本服务消费者配置:
<dubbo:reference id=“barService” interface=“com.foo.BarService” version=“2.0.0” />
如果不需要区分版本,可以按照以下的⽅式配置 :
<dubbo:reference id=“barService” interface=“com.foo.BarService” version="*" />
分组聚合
有多种实现,⽤group区分,现在消费⽅需从每种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>
参数验证
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
结果缓存
lru 基于最近最少使⽤原则删除多余缓存,保持最热的数据被缓存。
threadlocal 当前线程缓存,⽐如⼀个⻚⾯渲染,⽤到很多 portal,每个 portal 都要去查⽤户信息,通过线程缓 存,可以减少这种多余访问。
jcache 与 JSR107 集成,可以桥接各种缓存实现
<dubbo:reference interface=“com.foo.BarService” cache=“lru” />
或:
<dubbo:reference interface=“com.foo.BarService”>
<dubbo:method name=“findBar” cache=“lru” />
</dubbo:reference>
泛化调⽤
通过 GenericService 调⽤所有服务实现,参数及返回值中的所有 POJO 均⽤ Map 表示,通 常⽤于框架集成
<dubbo:reference id=“barService” interface=“com.foo.BarService” generic=“true” />
api调用例子:
ReferenceConfig reference = new ReferenceConfig(); // 弱类型接⼝名 reference.setInterface(“com.xxx.XxxService”);
reference.setVersion(“1.0.0”); // 声明为泛化接⼝ reference.setGeneric(true);
// ⽤com.alibaba.dubbo.rpc.service.GenericService可以替代所有接⼝引⽤
GenericService genericService = reference.get();
// 基本类型以及Date,List,Map等不需要转换,直接调⽤
Object result = genericService.
i
n
v
o
k
e
(
"
s
a
y
H
e
l
l
o
"
,
n
e
w
S
t
r
i
n
g
[
]
"
j
a
v
a
.
l
a
n
g
.
S
t
r
i
n
g
"
,
n
e
w
O
b
j
e
c
t
[
]
"
w
o
r
l
d
"
)
;
/
/
⽤
M
a
p
表
示
P
O
J
O
参
数
,
如
果
返
回
值
为
P
O
J
O
也
将
⾃
动
转
成
M
a
p
M
a
p
<
S
t
r
i
n
g
,
O
b
j
e
c
t
>
p
e
r
s
o
n
=
n
e
w
H
a
s
h
M
a
p
<
S
t
r
i
n
g
,
O
b
j
e
c
t
>
(
)
;
p
e
r
s
o
n
.
p
u
t
(
"
n
a
m
e
"
,
"
x
x
x
"
)
;
p
e
r
s
o
n
.
p
u
t
(
"
p
a
s
s
w
o
r
d
"
,
"
y
y
y
"
)
;
/
/
如
果
返
回
P
O
J
O
将
⾃
动
转
成
M
a
p
O
b
j
e
c
t
r
e
s
u
l
t
=
g
e
n
e
r
i
c
S
e
r
v
i
c
e
.
invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"}); // ⽤Map表示POJO参数,如果返回值为POJO也将⾃动转成Map Map<String, Object> person = new HashMap<String, Object>(); person.put("name", "xxx"); person.put("password", "yyy"); // 如果返回POJO将⾃动转成Map Object result = genericService.
invoke("sayHello",newString[]"java.lang.String",newObject[]"world");//⽤Map表示POJO参数,如果返回值为POJO也将⾃动转成MapMap<String,Object>person=newHashMap<String,Object>();person.put("name","xxx");person.put("password","yyy");//如果返回POJO将⾃动转成MapObjectresult=genericService.invoke(“findPerson”, new String[] {“com.xxx.Person”}, new Object[]{person});
回声测试
回声测试⽤于检测服务是否可⽤,回声测试按照正常请求流程执⾏,能够测试整个调⽤是否通畅,可⽤于监控。
上下⽂信息
RpcContext 是⼀个 ThreadLocal 的临时状态记录器,当接收到 RPC 请求,或发起 RPC 请求时,RpcContext 的状态 都会变化
异步调⽤
在 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 fooFuture = RpcContext.getContext().getFuture();
// 此调⽤会⽴即返回null barService.findBar(barId); // 拿到调⽤的Future引⽤,当结果返回后,会被通知和设置到此Future Future barFuture = RpcContext.getContext().getFuture();
// 此时findFoo和findBar的请求同时在执⾏,客户端不需要启动多线程来⽀持并⾏,⽽是借助NIO的⾮阻塞完成
// 如果foo已返回,直接拿到返回值,否则线程wait住,等待foo返回后,线程会被notify唤醒
Foo foo = fooFuture.get(); // 同理等待bar返回 Bar
bar = barFuture.get();
sent=“true” 等待消息发出,消息发送失败将抛出异常。 sent=“false” 不等待消息发出,将消息放⼊ IO 队列,即刻返回。
<dubbo:method name=“findFoo” async=“true” sent=“true” />
如果你只是想异步,完全忽略返回值,可以配置 return=“false” ,以减少 Future 对象的创建和管理成本:
<dubbo:method name=“findFoo” async=“true” return=“false” />
参数回调
服务接⼝示例
public interface CallbackService {
void addListener(String key, CallbackListener listener);
}
public interface CallbackListener {
void changed(String msg);
}
服务提供者接⼝实现示例
public class CallbackServiceImpl implements CallbackService {
private final Map<String, CallbackListener> listeners = new ConcurrentHashMap<String, CallbackListener>();
public CallbackServiceImpl() { Thread t = new Thread(new Runnable() { public void run() { while(true) { try { for(Map.Entry<String, CallbackListener> entry : listeners.entrySet()){ try { entry.getValue().changed(getChanged(entry.getKey())); } catch (Throwable t) { listeners.remove(entry.getKey()); } }
Thread.sleep(5000); // 定时触发变更通知
} catch (Throwable t) { // 防御容错
t.printStackTrace(); } } } });
t.setDaemon(true); t.start(); }
public void addListener(String key, CallbackListener listener) {
listeners.put(key, listener);
listener.changed(getChanged(key)); // 发送变更通知
}
private String getChanged(String key) {
return "Changed: " + new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(new Date());
} }
服务提供者配置示例
<dubbo:service interface=“com.callback.CallbackService” ref=“callbackService” connections=“1” callbacks=“1000”> <dubbo:method name=“addListener”>
<dubbo:argument index=“1” callback=“true” />
</dubbo:method> </dubbo:service>
服务消费者配置示例
<dubbo:reference id=“callbackService” interface=“com.callback.CallbackService” />
服务消费者调⽤示例
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(“classpath:consumer.xml”); context.start();
CallbackService callbackService = (CallbackService) context.getBean(“callbackService”);
callbackService.addListener(“http://10.20.160.198/wiki/display/dubbo/foo.bar”, new CallbackListener(){
public void changed(String msg) { System.out.println(“callback1:” + msg); } });
事件通知
在调⽤之前、调⽤之后、出现异常时,会触发 oninvoke 、 onreturn 、 onthrow 三个事件,可以配置当事件发⽣时, 通知哪个类的哪个⽅法
服务提供者与消费者共享服务接⼝
interface IDemoService { public Person get(int id); }
服务提供者实现
class NormalDemoService implements IDemoService {
public Person get(int id) { return new Person(id, “charles`son”, 4); } }
服务提供者配置
<dubbo:application name=“rpc-callback-demo” />
<dubbo:registry address=“http://10.20.160.198/wiki/display/dubbo/10.20.153.186” />
<dubbo:service interface=“com.alibaba.dubbo.callback.implicit.IDemoService” ref=“demoService” version=“1.0.0” g roup=“cn”/>
服务消费者 Callback 接⼝
interface Notify {
public void onreturn(Person msg, Integer id); public void onthrow(Throwable ex, Integer id); }
服务消费者 Callback 实现
class NotifyImpl implements Notify {
public Map<Integer, Person> ret = new HashMap<Integer, Person>();
public Map<Integer, Throwable> errors = new HashMap<Integer, Throwable>();
public void onreturn(Person msg, Integer id) {
System.out.println(“onreturn:” + msg);
ret.put(id, msg); }
public void onthrow(Throwable ex, Integer id) {
errors.put(id, ex); } }
服务消费者 Callback 配置
<dubbo:reference id=“demoService” interface=“com.alibaba.dubbo.callback.implicit.IDemoService” version=“1.0.0” group=“cn” >
<dubbo:method name=“get” async=“true” onreturn = “demoCallback.onreturn” onthrow=“demoCallback.onthrow” />
</dubbo:reference>
callback 与 async 功能正交分解, async=true 表示结果是否⻢上返回, onreturn 表示是否需要回调。 两者叠加存在以下⼏种组合情况 :
异步回调模式: async=true onreturn=“xxx” 同步回调模式: async=false onreturn=“xxx” 异步⽆回调 : async=true 同步⽆回调 : async=false
本地存根
<dubbo:service interface=“com.foo.BarService” stub=“true” />
或
<dubbo:service interface=“com.foo.BarService” stub=“com.foo.BarServiceStub” />
本地伪装
本地伪装 通常⽤于服务降级,⽐如某验权服务,当服务提供⽅全部挂掉后,客户端不抛出异常,⽽是通过 Mock 数据 返回授权失败。
<dubbo:service interface=“com.foo.BarService” mock=“true” />
或
<dubbo:service interface=“com.foo.BarService” mock=“com.foo.BarServiceMock” />
如果服务的消费⽅经常需要 try-catch 捕获异常,请考虑改为 Mock 实现,并在 Mock 实现中 return null
<dubbo:service interface=“com.foo.BarService” mock=“return null” />
延迟暴露
你的服务需要预热时间,⽐如初始化缓存,等待相关资源就位等,可以使⽤ delay 进⾏延迟暴露
<dubbo:service delay=“5000” />
并发控制
限制 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>
如果 dubbo:service 和 dubbo:reference 都配了actives, dubbo:reference 优先
Load Balance 均衡
配置服务的客户端的 loadbalance 属性为 leastactive ,此 Loadbalance 会调⽤并发数最⼩的 Provider(Consumer 端并发数)。
<dubbo:reference interface=“com.foo.BarService” loadbalance=“leastactive” />
或
<dubbo:service interface=“com.foo.BarService” loadbalance=“leastactive” />
连接控制
服务端连接控制
限制服务器端接受的连接不能超过 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:service 和 dubbo:reference 都配了 connections, dubbo:reference 优先
延迟连接
延迟连接⽤于减少⻓连接数。当有调⽤发起时,再创建⻓连接。
<dubbo:protocol name=“dubbo” lazy=“true” />
粘滞连接
粘滞连接⽤于有状态服务,尽可能让客户端总是向同⼀提供者发起调⽤,除⾮该提供者挂了,再连另⼀台。
粘滞连接将⾃动开启延迟连接,以减少⻓连接数。
<dubbo:protocol name=“dubbo” sticky=“true” />
令牌验证
通过令牌验证在注册中⼼控制权限,以决定要不要下发令牌给消费者,可以防⽌消费者绕过注册中⼼访问提供者
可以全局设置开启令牌验证:
<dubbo:provider interface=“com.foo.BarService” token=“true” />
或
<dubbo:provider interface=“com.foo.BarService” token=“123456” />
也可在服务级别设置:
<dubbo:service interface=“com.foo.BarService” token=“true” />
或
<dubbo:service interface=“com.foo.BarService” token=“123456” />
还可在协议级别设置:
<dubbo:protocol name=“dubbo” token=“true” />
或
<dubbo:protocol name=“dubbo” token=“123456” />
路由规则
路由规则 决定⼀次 dubbo 服务调⽤的⽬标服务器,分为条件路由规则和脚本路由规则,并且⽀持可扩展
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtensio n(); Registry registry = registryFactory.getRegistry(URL.valueOf(“zookeeper://10.20.153.10:2181”)); registry.register(URL.valueOf(“condition://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule=” + U RL.encode(“host = 10.20.153.10 => host = 10.20.153.11”) + "));
其中: condition:// 表示路由规则的类型,⽀持条件路由规则和脚本路由规则,可扩展,必填。 0.0.0.0 表示对所有 IP 地址⽣效,如果只想对某个 IP 的⽣效,请填⼊具体 IP,必填。 com.foo.BarService 表示只对指定服务⽣效,必填。 category=routers 表示该数据为动态配置类型,必填。 dynamic=false 表示该数据为持久数据,当注册⽅退出时,数据依然保存在注册中⼼,必填。 enabled=true 覆盖规则是否⽣效,可不填,缺省⽣效。 force=false 当路由结果为空时,是否强制执⾏,如果不强制执⾏,路由结果为空的路由规则将⾃动失效,可不 填,缺省为 flase 。 runtime=false 是否在每次调⽤时执⾏路由规则,否则只在提供者地址列表变更时预先执⾏并缓存结果,调⽤时直 接从缓存中获取路由结果。如果⽤了参数路由,必须设为 true ,需要注意设置会影响调⽤的性能,可不填,缺省 为 flase 。 priority=1 路由规则的优先级,⽤于排序,优先级越⼤越靠前执⾏,可不填,缺省为 0 。 rule=URL.encode(“host = 10.20.153.10 => host = 10.20.153.11”) 表示路由规则的内容,必填
基于条件表达式的路由规则,如: host = 10.20.153.10 => host = 10.20.153.11
规则:
=> 之前的为消费者匹配条件,所有参数和消费者的 URL 进⾏对⽐,当消费者满⾜匹配条件时,对该消费者执⾏ 后⾯的过滤规则。 => 之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进⾏对⽐,消费者最终只拿到过滤后的地址列 表。 如果匹配条件为空,表示对所有消费⽅应⽤,如: => host != 10.20.153.11 如果过滤条件为空,表示禁⽌访问,如: host = 10.20.153.10 =>
表达式:
参数⽀持: 服务调⽤信息,如:method, argument 等,暂不⽀持参数路由 URL 本身的字段,如:protocol, host, port 等 以及 URL 上的所有参数,如:application, organization 等
条件⽀持: 等号 = 表示"匹配",如: host = 10.20.153.10 不等号 != 表示"不匹配",如: host != 10.20.153.10
值⽀持: 以逗号 , 分隔多个值,如: host != 10.20.153.10,10.20.153.11 以星号 * 结尾,表示通配,如: host != 10.20.* 以美元符 $ 开头,表示引⽤消费者参数,如: host = $host
脚本路由规则
脚本路由规则 ⽀持 JDK 脚本引擎的所有脚本,⽐如:javascript, jruby, groovy 等,通过 type=javascript 参数设置 脚本类型,缺省为 javascript。
“script://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule=” + URL.encode(“function route(invoker s) { … } (invokers)”)
基于脚本引擎的路由规则,如:
function route(invokers) { var result = new java.util.ArrayList(invokers.size()); for (i = 0; i < invokers.size(); i ++) { if (“10.20.153.10”.equals(invokers.get(i).getUrl().getHost())) { result.add(invokers.get(i)); } } return result; } (invokers); // 表示⽴即执⾏⽅法
配置规则
向注册中⼼写⼊动态配置覆盖规则 。该功能通常由监控中⼼或治理中⼼的⻚⾯完成。
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtensio n(); Registry registry = registryFactory.getRegistry(URL.valueOf(“zookeeper://10.20.153.10:2181”)); registry.register(URL.valueOf(“override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&appli cation=foo&timeout=1000”));
其中: override:// 表示数据采⽤覆盖⽅式,⽀持 override 和 absent ,可扩展,必填。 0.0.0.0 表示对所有 IP 地址⽣效,如果只想覆盖某个 IP 的数据,请填⼊具体 IP,必填。 com.foo.BarService 表示只对指定服务⽣效,必填。 category=configurators 表示该数据为动态配置类型,必填。 dynamic=false 表示该数据为持久数据,当注册⽅退出时,数据依然保存在注册中⼼,必填。 enabled=true 覆盖规则是否⽣效,可不填,缺省⽣效。 application=foo 表示只对指定应⽤⽣效,可不填,表示对所有应⽤⽣效。 timeout=1000 表示将满⾜以上条件的 timeout 参数的值覆盖为 1000。如果想覆盖其它参数,直接加在 override 的 URL 参数上。
示例: 1. 禁⽤提供者:(通常⽤于临时踢除某台提供者机器,相似的,禁⽌消费者访问请使⽤路由规则)
override://10.20.153.10/com.foo.BarService?category=configurators&dynamic=false&disbaled=true
2. 调整权重:(通常⽤于容量评估,缺省权重为 100)
override://10.20.153.10/com.foo.BarService?category=configurators&dynamic=false&weight=200
3. 调整负载均衡策略:(缺省负载均衡策略为 random)
override://10.20.153.10/com.foo.BarService?category=configurators&dynamic=false&loadbalance=leastactive
4. 服务降级:(通常⽤于临时屏蔽某个出错的⾮关键服务)
override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:retu rn+null
服务降级
可以通过服务降级功能 临时屏蔽某个出错的⾮关键服务,并定义降级后的返回策略。
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtensio n(); Registry registry = registryFactory.getRegistry(URL.valueOf(“zookeeper://10.20.153.10:2181”)); registry.register(URL.valueOf(“override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&appli cation=foo&mock=force:return+null”));
其中: mock=force:return+null 表示消费⽅对该服务的⽅法调⽤都直接返回 null 值,不发起远程调⽤。⽤来屏蔽不重要服 务不可⽤时对调⽤⽅的影响。 还可以改为 mock=fail:return+null 表示消费⽅对该服务的⽅法调⽤在失败后,再返回 null 值,不抛异常。⽤来容 忍不重要服务不稳定时对调⽤⽅的影响。
主机绑定
<dubbo:protocol host=“205.182.23.201”>
端⼝配置
dubbo 20880
rmi 1099
http 80
hessian 80
webservice 80
memcached 11211
redis 6379
⽇志适配
<dubbo:application logger=“log4j” />
或
dubbo.application.logger=log4j
API 汇总如下:
配置 API
com.alibaba.dubbo.config.ServiceConfig
com.alibaba.dubbo.config.ReferenceConfig
com.alibaba.dubbo.config.ProtocolConfig
com.alibaba.dubbo.config.RegistryConfig
com.alibaba.dubbo.config.MonitorConfig
com.alibaba.dubbo.config.ApplicationConfig
com.alibaba.dubbo.config.ModuleConfig
com.alibaba.dubbo.config.ProviderConfig
com.alibaba.dubbo.config.ConsumerConfig
com.alibaba.dubbo.config.MethodConfig
com.alibaba.dubbo.config.ArgumentConfig
注解 API
com.alibaba.dubbo.config.annotation.Service
com.alibaba.dubbo.config.annotation.Reference
模型 API
com.alibaba.dubbo.common.URL
com.alibaba.dubbo.rpc.RpcException
上下⽂ API
com.alibaba.dubbo.rpc.RpcContext
服务API
com.alibaba.dubbo.rpc.service.GenericService
com.alibaba.dubbo.rpc.service.GenericException
com.alibaba.dubbo.rpc.service.EchoService
功能使用
1)在泛化引用dubbo时,因为referencrConfig是一个很重的实例,所以需要使用到缓存
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
GenericService service = cache.get(config);//config是ReferenceConfig实例
2)dubbo-admin管理平台搭建
通过dubbo-admin可以对消费者和提供者进行管理.可自行到官网下载:https://github.com/apache/incubator-dubbo-ops下载。
下载下来后,进入incubator-dubbo-ops文件夹,在此处打开命令行窗口;
执行 mvn package -Dmaven.test.skip=true 命令,打包该项目;
在生成的target文件夹中找到dubbo-admin的jar包,在命令行执行该项目就好了;
根据打印的日志找到 current host:yourip 和 Tomcat started on port(s): yourport
浏览器访问yourip:yourport;
用户名和密码都输入root或都输guest,即可登录成功
3、dubbo网关主要是解决统一鉴权、限流、防攻击、系统拆分、api横向扩展、高可用、负载均横、服务自动扩缩一系列问题,所以需要一个网关系统来作为外部服务调用的统一入口。