准备工作:

(1)启动zookeeper作为dubbo的注册中心

(2)新建一个maven的生产者web工程dubbo-provider-web和一个maven的消费者web工程dubbo-consumer-web

(3)在pom.xml文件里面引入如下依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.study</groupId>
<artifactId>dubbo-provider-web</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>dubbo-provider-web Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>4.3.10.RELEASE</spring.version>
</properties> <dependencies>
<!-- 添加dubbo依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加zk客户端依赖 -->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<!-- spring相关 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version> </dependency> -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!-- 日志相关依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency> </dependencies>
<build>
<finalName>dubbo-provider-web</finalName>
</build>
</project>

1. 注解配置

dubbo可以使用注解在生产者端暴露服务接口和在消费端引用接口,只需要在生产者和消费者的配置文件里面配置扫描包路径即可,而不用在xml里面配置需要暴露和引用的接口

扫描包路径的配置

<!-- 扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类 -->
<dubbo:annotation package="com.study.service" />

1.1 在生产者dubbo-provider-web和消费者dubbo-consumer-web新建一个dubbo注解测试的接口

package com.study.service;

/**
*
* @Description: dubbo注解测试的接口
* @author leeSmall
* @date 2018年10月23日
*
*/
public interface AnnotationDubboTest {
public String eat(String param);
}

1.2 在生产者dubbo-provider-web新建一个dubbo注解测试的接口的实现类

package com.study.service;

import com.alibaba.dubbo.config.annotation.Service;

/**
*
* @Description: dubbo注解测试的接口的实现类
* @author leeSmall
* @date 2018年10月23日
*
*/
@Service(timeout = 1000000, version = "1.2.3")
public class AnnotationDubboTestImpl implements AnnotationDubboTest { public String eat(String param) {
System.out.println("-----------AnnotationDubboTestImpl service test------------"
+ param);
return "-----------AnnotationDubboTestImpl service test------------";
} }

1.3 在消费端dubbo-consumer-web新建一个测试的control

/**
*
* @Description: dubbo消费端测试control
* @author leeSmall
* @date 2018年10月23日
*
*/
@Controller
@RequestMapping("/common")
public class CommonController implements ApplicationContextAware { private static Logger logger = Logger.getLogger(CommonController.class); @Reference(check = false, timeout = 100000, version = "1.2.3")
AnnotationDubboTest annotationdubbo; @RequestMapping("/annotationdubbo")
public @ResponseBody String annotationdubbo() {
annotationdubbo.eat("我是dubbo的注解测试control");
return "annotationdubbo";
} }

1.4 在tomcat8080和tomcat8081分别启动生产者和消费者,在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/annotationdubbo访问查看效果

生产者端:

浏览器:

2. 启动时检查

Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认 check="true" 。
可以通过 check="false" 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
另外,如果你的Spring容器是懒加载的,或者通过API编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到null引用,如果 check="false" ,总是会返回引用,当服务恢复时,能自动连上。

关闭某个服务的启动时检查 (没有提供者时报错):

<dubbo:reference interface="com.foo.BarService" check="false" />

关闭所有服务的启动时检查 (没有提供者时报错):

<dubbo:consumer check="false" />

3. 集群容错

3.1 Failover Cluster

失败自动切换,缺省值,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
配置如下:
生产者:
<dubbo:service retries="2" />
消费者:
<dubbo:reference retries="2" />
消费者具体到调用生产者的哪个方法:
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>

3.2 Failfast Cluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录
配置如下:
生产者:
<dubbo:service cluster="failfast" />
消费者:
<dubbo:reference cluster="failfast" />

3.3 Failsafe Cluster

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作
配置如下:
生产者:
<dubbo:service cluster="failsafe" />
消费者:
<dubbo:reference cluster="failsafe" />

3.4 Failback Cluster

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作
配置如下:
生产者:
<dubbo:service cluster="failback" />
消费者:
<dubbo:reference cluster="failback" />

3.5 Forking Cluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
配置如下:
生产者:
<dubbo:service cluster=“forking" />
消费者:
<dubbo:reference cluster=“forking" />

3.6 Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息
配置如下:
生产者:
<dubbo:service cluster="broadcast" />
消费者:
<dubbo:reference cluster="broadcast" />

4. 负载均衡

4.1 Random LoadBalance

随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

配置如下:

<dubbo:service interface="..." loadbalance="random" />
或:
<dubbo:reference interface="..." loadbalance="random" />
或:
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="random"/>
</dubbo:service>
或:
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="random"/>
</dubbo:reference>

4.2 RoundRobin LoadBalance

轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

配置如下:

<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>

4.3 ConsistentHash LoadBalance

一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
缺省只对第一个参数 Hash,如果要修改,请配置 
<dubbo:parameter key="hash.arguments"value="0,1" />
缺省用160份虚拟节点,如果要修改,
请配置 <dubbo:parameter key="hash.nodes" value="320" />
说明:
hash.arguments:当进行调用时候根据调用方法的哪几个参数生成key,并根据key来通过一致性hash算法来选择调用结点。例如调用方法invoke(String s1,String s2); 若hash.arguments为1(默认值),则仅取invoke的参数1(s1)来生成hashCode。
hash.nodes:为结点的副本数

<dubbo:service interface="..." loadbalance="consistenthash" />
或:
<dubbo:reference interface="..." loadbalance="consistenthash" />
或:
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="consistenthash"/>
</dubbo:service>
或:
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="consistenthash"/>
</dubbo:reference>

4.4 LeastActive LoadBalance

最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

<dubbo:service interface="..." loadbalance="leastactive" />
或:
<dubbo:reference interface="..." loadbalance="leastactive" />
或:
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="leastactive"/>
</dubbo:service>
或:
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="leastactive"/>
</dubbo:reference>

5. 服务分组

当一个接口有多个实现时,可以用group区分要调用的服务。

5.1 服务分组配置方式实现:

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

package com.study.test.service;

public interface DubboTestService {
public String eat(String param);
}

在生产者dubbo-provider-web新建两个DubboTestService接口的实现类

实现类1:

package com.study.test.service;

public class DubboTestServiceImpl implements DubboTestService {

    public String eat(String param) {

        System.out.println("-----------dubbo service test DubboTestServiceImpl ------------" + param);
return "-----------dubbo service test DubboTestServiceImpl ------------";
} }

实现类2:

package com.study.test.service;

public class DubboTestService1Impl implements DubboTestService {

    public String eat(String param) {

        System.out.println("-----------dubbo service test DubboTestService1Impl------------" + param);
return "-----------dubbo service test DubboTestService1Impl------------";
} }

在生产者dubbo-provider-web的applicationProvider.xml配置分组

    <!-- 服务分组 -->
<bean id="dubboTestServiceImpl1" class="com.study.test.service.DubboTestServiceImpl"/>
<bean id="dubboTestServiceImpl2" class="com.study.test.service.DubboTestService1Impl"/>
<dubbo:service interface="com.study.test.service.DubboTestService" ref="dubboTestServiceImpl1" group="dubboTestServiceImpl1"/>
<dubbo:service interface="com.study.test.service.DubboTestService" ref="dubboTestServiceImpl2" group="dubboTestServiceImpl2"/>

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的分组

    <!--服务分组  -->
<dubbo:reference id="dubboTestServiceImpl1" interface="com.study.test.service.DubboTestService" check="false" retries="4" cluster="failover" group="dubboTestServiceImpl1"/>
<dubbo:reference id="dubboTestServiceImpl2" interface="com.study.test.service.DubboTestService" check="false" retries="4" cluster="failover" group="dubboTestServiceImpl2"/>

在消费者dubbo-consumer-web的CommonController.java里面创建服务分组测试代码

//服务分组示例begin
@Autowired
@Qualifier("dubboTestServiceImpl1")
DubboTestService dubboService1; @Autowired
@Qualifier("dubboTestServiceImpl2")
DubboTestService dubboService2; @RequestMapping("/dubboTest")
public @ResponseBody String dubboTest() {
dubboService1.eat("服务分组示例!");
dubboService2.eat("服务分组示例!");
return "dubboTest";
}
//服务分组示例end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/dubboTest访问查看效果

生产者:

浏览器:

5.2 服务分组注解方式实现:

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

package com.study.service;

public interface UserService {

    public String login(String param);

}

在生产者dubbo-provider-web新建两个UserService接口的实现类

实现类1:

package com.study.service;

import org.apache.log4j.Logger;

import com.alibaba.dubbo.config.annotation.Service;

@Service(version = "1.0.2", group = "user2")
public class UserService2Impl implements UserService { private static Logger logger = Logger.getLogger(UserService2Impl.class); public String login(String param) {
logger.info("UserService2Impl.login begin!22222");
return "用户已经登录成功!·~~~~~~~~~~~~~~~~~~";
} }

实现类2:

package com.study.service;

import org.apache.log4j.Logger;

import com.alibaba.dubbo.config.annotation.Service;

@Service(version = "1.0.2", group = "user1")
public class UserServiceImpl implements UserService { private static Logger logger = Logger.getLogger(UserServiceImpl.class); public String login(String param) {
logger.info("UserServiceImpl.login begin!");
return "用户已经登录成功!·~~~~~~~~~~~~~~~~~~";
} }

在消费者dubbo-consumer-web的CommonController.java里面创建服务分组测试代码

//服务分组注解实现示例begin
@Reference(version = "1.0.2", check = false, group = "user1")
UserService usrService; @Reference(version = "1.0.2", check = false, group = "user2")
UserService usr2Service; @RequestMapping("/grouplogin1")
public @ResponseBody String login() {
logger.info(usrService.login("服务分组注解实现"));
logger.info(usr2Service.login("服务分组注解实现"));
return "成功";
}
//服务分组注解实现示例end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/grouplogin1访问查看效果

生产者:

消费者:

6. 多版本

当一个接口实现出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
1)在低压力时间段,先升级一半提供者为新版本
2)再将所有消费者升级为新版本
3)然后将剩下的一半提供者升级为新版本
老版本服务提供者配置:
<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="*" />

7. 参数验证

在生产者dubbo-provider-web和消费者dubbo-consumer-web的pom.xml里面分别引入如下依赖:

        <dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个参数验证接口和一个参数验证实体

参数验证接口:

package com.study.service;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size; /**
*
* @Description: 参数验证接口
* 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class)
* @author leeSamll
* @date 2018年10月23日
*
*/
public interface ValidationService { void save(ValidationParameter parameter); void update(ValidationParameter parameter); void delete(
@Min(1) long id,
@NotNull @Size(min = 2, max = 16) @Pattern(regexp = "^[a-zA-Z]+$") String operator); // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选
@interface Save {
} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Update.class),可选
@interface Update {
} }

参数验证实体:

package com.study.service;

import java.io.Serializable;
import java.util.Date; import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size; /**
*
* @Description: 参数验证实体
* @author leeSamll
* @date 2018年10月23日
*
*/
public class ValidationParameter implements Serializable { private static final long serialVersionUID = 7158911668568000392L; // 不允许为空
@NotNull
// 长度或大小范围
@Size(min = 2, max = 20)
private String name; @NotNull(groups = ValidationService.Save.class)
// 保存时不允许为空,更新时允许为空 ,表示不更新该字段
@Pattern(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$")
private String email; // 最小值
@Min(18)
// 最大值
@Max(100)
private int age; // 必须为一个过去的时间
@Past
private Date loginDate; // 必须为一个未来的时间
@Future
private Date expiryDate; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public Date getLoginDate() {
return loginDate;
} public void setLoginDate(Date loginDate) {
this.loginDate = loginDate;
} public Date getExpiryDate() {
return expiryDate;
} public void setExpiryDate(Date expiryDate) {
this.expiryDate = expiryDate;
} }

在生产者dubbo-provider-web新建ValidationService接口的实现类

package com.study.service;

/**
*
* @Description: 参数验证接口实现类
* @author leeSamll
* @date 2018年10月23日
*
*/
public class ValidationServiceImpl implements ValidationService { public void save(ValidationParameter parameter) {
System.out.println("save");
} public void update(ValidationParameter parameter) {
} public void delete(long id, String operator) {
System.out.println("delete");
} }

在生产者dubbo-provider-web的applicationProvider.xml配置参数验证接口:

    <!--参数验证begin  -->
<bean id="validationService" class="com.study.service.ValidationServiceImpl"/>
<dubbo:service interface="com.study.service.ValidationService" ref="validationService"
validation="true"/>
<!--参数验证end -->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的参数验证接口

    <!--参数验证begin  -->
<dubbo:reference id="validationService" interface="com.study.service.ValidationService"
validation="true"/>
<!--参数验证end -->

在消费者dubbo-consumer-web的CommonController.java里面创建参数验证测试代码

    //参数验证begin
@Autowired
ValidationService validationService; @RequestMapping("/validation")
public @ResponseBody String validation() {
// Save OK
ValidationParameter parameter = new ValidationParameter();
parameter.setName("leeSmall");
parameter.setEmail("leeSmall@qq.com");
parameter.setAge(50);
parameter.setLoginDate(new Date(System.currentTimeMillis() - 1000000));
parameter.setExpiryDate(new Date(System.currentTimeMillis() + 1000000));
validationService.save(parameter);
System.out.println("Validation Save OK"); // Save Error
try {
parameter = new ValidationParameter();
validationService.save(parameter);
System.err.println("Validation Save ERROR");
}
catch (RpcException e) {
ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
System.out.println(violations);
} // Delete OK
validationService.delete(2, "abc");
System.out.println("Validation Delete OK"); // Delete Error
try {
validationService.delete(0, "abc");
System.err.println("Validation Delete ERROR");
}
catch (RpcException e) {
ConstraintViolationException ve = (ConstraintViolationException)e.getCause();
Set<ConstraintViolation<?>> violations = ve.getConstraintViolations();
System.out.println(violations);
}
return "OK";
}
//参数验证end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/validation访问查看效果

生产者:

消费者:

8. 结果缓存

缓存类型
lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。

LRU的缺省cache.size为1000,执行1001次,会把最开始请求的缓存结果清除掉

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

package com.study.service;

/**
*
* @Description: 结果缓存接口
* @author leeSmall
* @date 2018年10月24日
*
*/
public interface CacheService { String findCache(String id); }

在生产者dubbo-provider-web新建CacheService接口的实现类

package com.study.service;

import java.util.concurrent.atomic.AtomicInteger;

/**
*
* @Description: 结果缓存接口实现类
* @author leeSmall
* @date 2018年10月24日
*
*/
public class CacheServiceImpl implements CacheService { private final AtomicInteger i = new AtomicInteger(); public String findCache(String id) {
System.out.println("request: " + id + ", response: "
+ i.getAndIncrement());
return "request: " + id + ", response: " + i.getAndIncrement();
}
}

在生产者dubbo-provider-web的applicationProvider.xml配置结果缓存接口

    <!--结果缓存begin  -->
<bean id="cacheService" class="com.study.service.CacheServiceImpl"/>
<dubbo:service interface="com.study.service.CacheService" ref="cacheService"/>
<!--结果缓存end -->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的缓存接口

    <!--结果缓存begin  -->
<dubbo:reference id="cacheService" interface="com.study.service.CacheService" cache="lru"/>
<!--结果缓存end -->

在消费者dubbo-consumer-web的CommonController.java里面创建缓存接口测试代码

    //结果缓存begin
@Autowired
CacheService cacheService; @RequestMapping("/cache")
public @ResponseBody String cache() {
// 测试缓存生效,多次调用返回同样的结果。(服务器端自增长返回值)
String fix = null;
for (int i = 0; i < 5; i++) {
String result = cacheService.findCache("0");
if (fix == null || fix.equals(result)) {
System.out.println("OK: " + result);
}
else {
System.err.println("ERROR: " + result);
}
fix = result;
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
e.printStackTrace();
}
} // LRU的缺省cache.size为1000,执行1001次,会把第一次请求的缓存结果清除掉
for (int n = 0; n < 1001; n++) {
String pre = null;
for (int i = 0; i < 10; i++) {
String result = cacheService.findCache(String.valueOf(n));
if (pre != null && !pre.equals(result)) {
System.err.println("ERROR: " + result);
}
pre = result;
}
} // 测试LRU有移除最开始的一个缓存项
String result = cacheService.findCache("0");
if (fix != null && !fix.equals(result)) {
System.out.println("OK: " + result);
}
else {
System.err.println("ERROR: " + result);
}
return "OK";
}
//结果缓存end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/cache访问查看效果:

测试缓存生效,多次调用返回同样的结果。(服务器端自增长返回值):

生产者:

消费者:

LRU的缺省cache.size为1000,执行1001次,会把第一次请求的缓存结果清除掉:

生产者:

测试LRU有移除最开始的一个缓存项:

生产者:

消费者:

9. 使用泛化调用

泛化接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用 Map表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过GenericService 调用所有服务实现。

泛化调用就是服务消费者端因为某种原因并没有该服务接口,这个原因有很多,比如是跨语言的,一个PHP工程师想调用某个java接口,他并不能按照你约定,去写一个个的接口,Dubbo并不是跨语言的RPC框架,但并不是不能解决这个问题,这个PHP程序员搭建了一个简单的java web项目,引入了dubbo的jar包,使用dubbo的泛化调用,然后利用web返回json,这样也能完成跨语言的调用。泛化调用的好处之一就是这个了。
好了,简而言之,泛化调用,最直接的表现就是服务消费者不需要有任何接口的实现,就能完成服务的调用。

这个功能一般不用,因为代码的可读性很差

在生产者dubbo-provider-web新建一个接口:

package com.study.service;

/**
*
* @Description: 泛化调用接口
* @author leeSmall
* @date 2018年10月23日
*
*/
public interface DemoService {
String eat(Long id);
}

在生产者dubbo-provider-web新建两个DemoService接口的实现类

package com.study.service;

/**
*
* @Description: 泛化调用接口实现类
* @author leeSmall
* @date 2018年10月23日
*
*/
public class DemoServiceImpl implements DemoService {
public String eat(Long id) {
System.out.println("泛化调用");
return "泛化调用" + id;
}
}

在生产者dubbo-provider-web的applicationProvider.xml配置泛化调用接口

    <!--泛化调用begin-->
<dubbo:service interface="com.study.service.DemoService" ref="demoService" protocol="dubbo"/>
<bean id="demoService" class="com.study.service.DemoServiceImpl"/>
<!--泛化调用end-->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的泛化调用接口

    <!--泛化调用begin -->
<dubbo:reference id="demoService" interface="com.study.service.DemoService" generic="true"/>
<!--泛化调用end -->

在消费者dubbo-consumer-web的CommonController.java里面创建泛化调用测试代码

    //泛化调用begin
@RequestMapping("/fanhua")
public @ResponseBody String fanhuayinyong() {
GenericService demoService = (GenericService)applicationContext.getBean("demoService");
Object result = demoService.$invoke("eat", new String[] { "java.lang.Long" }, new Object[]{ 1L });
System.out.println(result);
return "OK";
}
//泛化调用end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/fanhua访问查看效果

生产者:

消费者:

10. 实现泛化调用

泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。

在生产者dubbo-provider-web新建dubbo提供的泛化接口的实现类:

package com.study.service;

import com.alibaba.dubbo.rpc.service.GenericException;
import com.alibaba.dubbo.rpc.service.GenericService; /**
*
* @Description: dubbo提供的泛化接口的实现类
* @author leeSmall
* @date 2018年10月24日
*
*/
public class MyGenericService implements GenericService { public Object $invoke(String method, String[] parameterTypes, Object[] args)
throws GenericException {
System.out.println("泛化调用实现!");
return "泛化调用实现";
} }

在生产者dubbo-provider-web的applicationProvider.xml配置泛化接口的实现类

    <!--实现泛化调用begin  -->
<bean id="genericService" class="com.study.service.MyGenericService" />
<dubbo:service interface="com.alibaba.dubbo.rpc.service.GenericService" ref="genericService" />
<!--实现泛化调用end -->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的泛化接口的实现

    <!--实现泛化调用begin  -->
<dubbo:reference id="genericService" interface="com.alibaba.dubbo.rpc.service.GenericService"/>
<!--实现泛化调用end -->

在消费者dubbo-consumer-web的CommonController.java里面创建泛化接口的实现测试代码

    //实现泛化调用begin
@RequestMapping("/fanhuashixian")
public @ResponseBody String fanhuashixian() {
GenericService barService = (GenericService)applicationContext.getBean("genericService");
Object result = barService.$invoke("login",
new String[] {"java.lang.String"},
new Object[] {"World"});
System.out.println(result);
return "OK";
}
//实现泛化调用end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/fanhuashixian访问查看效果

生产者:

消费者:

11. 回声测试

回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。
所有服务自动实现EchoService接口,只需将任意服务引用强制转型为EchoService,即可使用。

直接使用第8个功能点(8.结果缓存)的接口进行回声测试的实现

在消费者dubbo-consumer-web的CommonController.java里面创建回声测试的测试代码:

    //回声测试begin
@RequestMapping("/huisheng")
public @ResponseBody String huisheng() {
CacheService barService = (CacheService)applicationContext.getBean("cacheService");
EchoService echoService = (EchoService)barService; // 强制转型为EchoService
// 回声测试可用性
String status = (String)echoService.$echo("OK");
if(status.equals("OK")) {
System.out.println("缓存接口服务可用");
}
assert (status.equals("OK"));
return "OK";
}
//回声测试end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/huisheng访问查看效果

消费者:

12. 异步调用

基于NIO的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口:

package com.study.service;

/**
*
* @Description: 异步调用接口
* @author leeSmall
* @date 2018年10月24日
*
*/
public interface AsyncService { String sayHello(String name); }

在生产者dubbo-provider-web新建AsyncService接口的实现类

package com.study.service;

/**
*
* @Description: 异步调用接口实现类
* @author leeSmall
* @date 2018年10月24日
*
*/
public class AsyncServiceImpl implements AsyncService { public String sayHello(String name) {
for (int i = 0; i < 5; i++) {
System.out.println("async provider received: " + name);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
return "hello, " + name;
} }

在生产者dubbo-provider-web的applicationProvider.xml配置异步调用接口

    <!--异步调用begin  -->
<bean id="asyncServiceImpl" class="com.study.service.AsyncServiceImpl"/>
<dubbo:service interface="com.study.service.AsyncService" ref="asyncServiceImpl"/>
<!--异步调用end -->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的异步调用接口

    <!--异步调用begin  -->
<dubbo:reference id="asyncServiceImpl" interface="com.study.service.AsyncService" timeout="1200000">
<dubbo:method name="sayHello" async="true"/>
</dubbo:reference>
<!--异步调用end -->

在消费者dubbo-consumer-web的CommonController.java里面创建异步调用测试代码

    //异步调用begin
@Autowired
AsyncService asyncService; @RequestMapping("/async")
public @ResponseBody String async() {
String result = asyncService.sayHello("我是异步调用客户端");
System.out.println(result);
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
Future<String> fooFuture = RpcContext.getContext().getFuture();
// 如果result已返回,直接拿到返回值,否则线程wait住,等待result返回后,线程会被notify唤醒
try {
//调用get方法会阻塞在这里直到拿到结果
result = fooFuture.get();
System.out.println(result);
}
catch (InterruptedException e) {
e.printStackTrace();
}
catch (ExecutionException e) {
e.printStackTrace();
}
return "OK";
}
//异步调用end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/async访问查看效果:

生产者:

消费者:

13. 参数回调

参数回调方式与调用本地callback或listener相同,只需要在Spring的配置文件中声明哪个参数是callback 类型即可。Dubbo将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑。适用于服务端完成某个动作以后通知客户端

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个参数回调监听接口和参数回调接口

参数回调监听接口:

package com.study.callback;

/**
*
* @Description: 参数回调监听接口
* @author leeSmall
* @date 2018年10月25日
*
*/
public interface CallbackListener { void changed(String msg); }

参数回调接口:

package com.study.callback;

/**
*
* @Description: 参数回调接口
* @author leeSmall
* @date 2018年10月25日
*
*/
public interface CallbackService { void addListener(String key, CallbackListener listener); }

在生产者dubbo-provider-web新建CallbackService接口的实现类

package com.study.callback;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
*
* @Description: 参数回调接口实现类
* @author leeSmall
* @date 2018年10月25日
*
*/
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-provider-web的applicationProvider.xml配置参数回调接口

    <!--参数回调begin  -->
<bean id="callbackService" class="com.study.callback.CallbackServiceImpl"/>
<dubbo:service interface="com.study.callback.CallbackService" ref="callbackService"
connections="1" callbacks="1000">
<dubbo:method name="addListener">
<dubbo:argument index="1" callback="true"/>
<!--也可以通过指定类型的方式-->
<!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />-->
</dubbo:method>
</dubbo:service>
<!--参数回调end -->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的参数回调接口

    <!--参数回调begin  -->
<dubbo:reference id="callbackService" interface="com.study.callback.CallbackService"/>
<!--参数回调end -->

在消费者dubbo-consumer-web的CommonController.java里面创建参数回调接口测试代码

    //参数回调begin
@Autowired
CallbackService callbackService; @RequestMapping("/callback")
public @ResponseBody String callback() {
callbackService.addListener("foo.bar", new CallbackListener() {
public void changed(String msg) {
System.out.println("callback1:" + msg);
}
});
return "OK";
}
//参数回调end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/callback访问查看效果:

生产者:

消费者:

14. 事件通知

在调用之前、调用之后、出现异常时,会触发 oninvoke、onreturn、onthrow 三个事件,可以配置当事件发生时,通知哪个类的哪个方法 。

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

package com.study.event;

/**
*
* @Description: 事件通知接口参与接口
* @author leeSmall
* @date 2018年10月25日
*
*/
public interface Common {
public String eat(String param);
}

在生产者dubbo-provider-web新建Common接口的实现类

package com.study.event;

/**
*
* @Description: 事件通知接口参与接口实现类
* @author leeSmall
* @date 2018年10月25日
*
*/
public class CommonImpl implements Common { public String eat(String param) {
System.out.println("CommonImpl eat");
return "CommonImpl eat";
} }

在生产者dubbo-provider-web的applicationProvider.xml配置事件通知接口参与接口

    <!-- 事件通知接口参与接口begin -->
<bean id="commonImpl" class="com.study.event.CommonImpl"/>
<dubbo:service interface="com.study.event.Common" ref="commonImpl"/>
<!-- 事件通知接口参与接口end -->

在消费者dubbo-consumer-webl创建事件通知接口

package com.study.event;

/**
*
* @Description: 事件通知接口
* @author leeSmall
* @date 2018年10月25日
*
*/
public interface Notify { //调用之前
public void oninvoke(String name); //调用之后
public void onreturn(String msg); // 出现异常
public void onthrow(Throwable ex);
}

在消费者dubbo-consumer-web创建事件通知接口实现类

package com.study.event;

/**
*
* @Description: 事件通知接口实现类
* @author leeSmall
* @date 2018年10月25日
*
*/
public class NotifyImpl implements Notify { //调用之前
public void oninvoke(String msg) {
System.out.println("======oninvoke======, param: " + msg);
} //调用之后
public void onreturn(String msg) {
System.out.println("======onreturn======, param: " + msg);
} // 出现异常
public void onthrow(Throwable ex) {
System.out.println("======onthrow======, param: " + ex);
} }

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用事件通知接口参与接口和事件通知接口

    <!--事件通知begin  -->
<bean id ="demoCallback" class = "com.study.event.NotifyImpl" />
<dubbo:reference id="commonImpl" interface="com.study.event.Common" >
<dubbo:method name="eat" async="false" oninvoke="demoCallback.oninvoke" onreturn = "demoCallback.onreturn" onthrow=
"demoCallback.onthrow" />
</dubbo:reference>
<!--事件通知end -->

在消费者dubbo-consumer-web的CommonController.java里面创建事件通知接口测试代码

    //事件通知begin
@Autowired
Common common; @RequestMapping("/event")
public @ResponseBody String event() {
String result = common.eat("jdksk");
System.out.println(result);
return "OK";
}
//事件通知end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/cache访问查看效果:

生产者:

消费者:

15. 本地存根

远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在API中带上Stub,客户端生成Proxy实例,会把Proxy通过构造函数传给Stub ,然后把Stub暴露给用户,Stub可以决定要不要去调Proxy。

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

package com.study.stub;

/**
*
* @Description: 本地存根接口
* @author leeSmall
* @date 2018年10月25日
*
*/
public interface StubService {
String stub(String param);
}

在生产者dubbo-provider-web新建StubService接口的实现类

package com.study.stub;

/**
*
* @Description: 本地存根接口实现类
* @author leeSmall
* @date 2018年10月25日
*
*/
public class StubServiceImpl implements StubService { public String stub(String param) {
System.out.println("provider StubServiceImpl stub");
return "provider StubServiceImpl stub";
} }

在生产者dubbo-provider-web的applicationProvider.xml配置本地存根接口

    <!--本地存根begin  -->
<bean id="stubServiceImpl" class="com.study.stub.StubServiceImpl"/>
<dubbo:service interface="com.study.stub.StubService" ref="stubServiceImpl"/>
<!--本地存根end -->

在消费者dubbo-consumer-web创建本地存根Proxy实例

package com.study.stub;

/**
*
* @Description: 本地存根Proxy实例
* @author leeSmall
* @date 2018年10月25日
*
*/
public class LocalStubServiceProxy implements StubService { private StubService stubService; public LocalStubServiceProxy(StubService stubService) {
this.stubService = stubService;
} public String stub(String arg0) {
System.out.println("我是localstub,我就相当于一个过滤器,在代理调用远程方法的时候,我先做一下拦截工作"); try {
return stubService.stub("xxx");
}
catch (Exception e) {
System.out.println("远程调用出现异常了,我是本地stub,我要对远程服务进行伪装,达到服务降级的目的");
return "远程调用出现异常了,我是本地stub,我要对远程服务进行伪装,达到服务降级的目的";
}
} }

在消费者dubbo-consumer-web的applicationConsumer.xml配置本地存根接口和stub对应的本地存根Proxy实例

    <!--本地存根begin  -->
<dubbo:reference id="stubServiceImpl" interface="com.study.stub.StubService" stub="com.study.stub.LocalStubServiceProxy"/>
<!--本地存根end -->

在消费者dubbo-consumer-web的CommonController.java里面创建本地存根接口测试代码

    //本地存根begin
@Autowired
StubService stub; @RequestMapping("/stub")
public @ResponseBody String stub() {
String result = stub.stub("eee");
System.out.println(result);
return "OK";
}
//本地存根end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/stub访问查看效果:

生产者:

消费者:

16. 本地伪装

本地伪装 通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过Mock数据返回授权失败。
在spring配置文件中按以下方式配置:
<dubbo:service interface="com.foo.BarService" mock="com.foo.BarServiceMock" />

1)Mock是Stub的一个子集,便于服务提供方在客户端执行容错逻辑,因经常需要在出现RpcException(比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户名密码错误)时不需要容错,如果用Stub,可能就需要捕获并依赖RpcException类,而用Mock就可以不依赖RpcException,因为它的约定就是只有出现RpcException时才执行。
2)在interface旁放一个Mock实现,它实现BarService接口,并有一个无参构造函数

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

package com.study.mock;

/**
*
* @Description: 本地伪装接口
* @author leeSmall
* @date 2018年10月25日
*
*/
public interface MockService {
String mock(String param);
}

在生产者dubbo-provider-web新建MockService接口的实现类

package com.study.mock;

/**
*
* @Description: 本地伪装接口实现类
* @author leeSmall
* @date 2018年10月25日
*
*/
public class MockServiceImpl implements MockService { public String mock(String param) {
System.out.println("provider MockServiceImpl mock");
return "provider MockServiceImpl mock";
} }

在生产者dubbo-provider-web的applicationProvider.xml配置本地伪装接口

    <!--本地伪装begin  -->
<bean id="mockServiceImpl" class="com.study.mock.MockServiceImpl"/>
<dubbo:service interface="com.study.mock.MockService" ref="mockServiceImpl"/>
<!--本地伪装end -->

在消费者dubbo-consumer-web创建本地伪装代理实例

package com.study.mock;

/**
*
* @Description: 本地伪装代理实例
* @author leeSmall
* @date 2018年10月25日
*
*/
public class LocalMockProxyService implements MockService { public String mock(String arg0) {
System.out.println("local mock 做一下容错处理,这个就是服务降级");
return "local mock 做一下容错处理,这个就是服务降级";
}
}

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的本地伪装接口和本地伪装代理实例

    <!--本地伪装begin  -->
<dubbo:reference id="mockServiceImpl" interface="com.study.mock.MockService" mock="com.study.mock.LocalMockProxyService"/>
<!--本地伪装end -->

在消费者dubbo-consumer-web的CommonController.java里面创建本地伪装接口测试代码

    //本地伪装begin
@Autowired
MockService mock; @RequestMapping("/mock")
public @ResponseBody String mock() {
String result = mock.mock("vvvv");
System.out.println(result);
return "OK";
}
//本地伪装end

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/mock访问查看效果:

生产者:

消费者:

停掉生产者,再次在浏览器访问查看效果:

消费者:

17. 延迟暴露

延迟到Spring初始化完成后,再暴露服务,规避spring的加载死锁问题
生产者单个服务接口配置:

<dubbo:service delay="-1" />

生产者全局服务接口配置:

<dubbo:provider deplay=”-1” />

源码实现方式:ApplicationListener<ContextRefreshEvent> 监控ContextRefreshEvent事件

18. 并发控制

服务端配置:
限制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>优先

19. 连接控制

服务端连接控制
限制服务器端接受的连接不能超过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" />

注意:该配置只对使用长连接的dubbo协议生效。

粘滞连接
粘滞连接用于有状态服务,尽可能让客户端总是向同一提供者发起调用,除非该提供者挂了,再连另一台。
粘滞连接将自动开启延迟连接,以减少长连接数。

<dubbo:protocol name="dubbo" sticky="true" />

20. 令牌验证

通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者

可以在生产者(客户端不需要配置token,只需要从注册中心获取服务时获取即可)全局设置开启令牌验证:

<!--随机token令牌,使用UUID生成-->
<dubbo:provider interface="com.foo.BarService" token="true" />
<!--固定token令牌,相当于密码-->
<dubbo:provider interface="com.foo.BarService" token="123456" />

21. 路由规则

路由规则:决定一次dubbo服务调用的目标服务器,分为条件路由规则和脚本路由规则,并且支持可扩展。
向注册中心写入路由规则的操作通常由监控中心或治理中心的页面完成
示例:
registry.register(URL.valueOf("condition://0.0.0.0/com.foo.BarService?category=routers
&dynamic=false&rule=" + URL.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

白名单:
host != 10.20.153.10,10.20.153.11 =>
黑名单:
host = 10.20.153.10,10.20.153.11 =>

读写分离:
method = find*,list*,get*,is* => host = 172.22.3.94,172.22.3.95,172.22.3.96
method != find*,list*,get*,is* => host = 172.22.3.97,172.22.3.98
前后台分离:
application = bops => host = 172.22.3.91,172.22.3.92,172.22.3.93
application != bops => host = 172.22.3.94,172.22.3.95,172.22.3.96

22. 服务降级

可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
向注册中心写入动态配置覆盖规则:
mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

示例代码获取地址

 参考文章:

dubbo官方文档:http://dubbo.apache.org/zh-cn/docs/user/quick-start.html

Dubbo基本特性之泛化调用:https://www.cnblogs.com/flyingeagle/p/8908317.html

dubbo系列二:dubbo常用功能总结的更多相关文章

  1. dubbo系列二、dubbo+zookeeper+dubboadmin分布式服务框架搭建(windows平台)

    一.zookeeper配置中心安装 1.下载安装包,zookeeper-3.4.6.tar.gz 2.解压安装包,修改配置文件 参考zookeeper-3.4.6/conf/zoo_sample.cf ...

  2. dubbo系列二、dubbo请求流程记录

    目录 1.dubbo请求处理流程 1.1. consumer端处理流程 1.2.provider端处理流程 1.3.dubbo请求分析记录-图 泳道图 xmind图 2.dubbo请求核心说明 1.d ...

  3. Dubbo学习(二) Dubbo 集群容错模式-负载均衡模式

    Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...

  4. C#构造方法(函数) C#方法重载 C#字段和属性 MUI实现上拉加载和下拉刷新 SVN常用功能介绍(二) SVN常用功能介绍(一) ASP.NET常用内置对象之——Server sql server——子查询 C#接口 字符串的本质 AJAX原生JavaScript写法

    C#构造方法(函数)   一.概括 1.通常创建一个对象的方法如图: 通过  Student tom = new Student(); 创建tom对象,这种创建实例的形式被称为构造方法. 简述:用来初 ...

  5. dubbo学习 二 dubbo源码大致查阅

    源码的解析在官网都已经写的非常详细,可以参考:http://dubbo.io/Developer+Guide-zh.htm   服务提供者暴露一个服务的详细过程 首先ServiceConfig类拿到对 ...

  6. Harbor 学习分享系列4 - Harbor常用功能实验

    前言 本文为Harbor技术分享系列的第4部分也是初级部分的完结篇,下个阶段作者将会进阶分享,更多详细的内容将会将会在文中介绍. 云盘链接 链接:https://pan.baidu.com/s/1PT ...

  7. Docker系列二: docker常用命令总结

    https://docs.docker.com/reference/  官方命令总结地址 容器生命周期管理 1.docker run 创建一个新的容器并运行一个命令 docker run [optio ...

  8. dubbo系列四、dubbo服务暴露过程源码解析

    一.代码准备 1.示例代码 参考dubbo系列二.dubbo+zookeeper+dubboadmin分布式服务框架搭建(windows平台) 2.简单了解下spring自定义标签 https://w ...

  9. Dubbo源码学习总结系列二 dubbo-rpc远程调用模块

    dubbo本质是一个RPC框架,我们首先讨论这个骨干中的骨干,dubbo-rpc模块. 主要讨论一下几部分内容: 一.此模块在dubbo整体框架中的作用: 二.此模块需要完成的需求功能点及接口定义: ...

随机推荐

  1. 使用纯CSS制作展开合并立方体特效

    显示效果 源码 <html> <head> <meta http-equiv="Content-Type" content="text/ht ...

  2. Python3从零开始爬取今日头条的新闻【四、模拟点击切换tab标签获取内容】

    Python3从零开始爬取今日头条的新闻[一.开发环境搭建] Python3从零开始爬取今日头条的新闻[二.首页热点新闻抓取] Python3从零开始爬取今日头条的新闻[三.滚动到底自动加载] Pyt ...

  3. C语言基础一(敲打键盘、寻找资料)

    事前声明一点:小编的所有材料都是基础,没有什么大的不同,您若觉得不错的话,可以互相探讨下,毕竟本人也是小雏鸟. 大家在学习C语言.C++类似的高端语言时候,往往都是为了学而学,殊不知为什么而学,或许更 ...

  4. [P3957][NOIP2017]跳房子 (DP+二分/队列?)

    看到GREED_VI大佬在打这题 我这个蒟蒻偷偷看一眼洛谷上目前普及难度里最难的一题 题目还是能看懂的,不想道路游戏那题,我完全不知道题目是什么意思…… GREED_VI大佬第一次用的是二分的思想,于 ...

  5. Chrome中Vim插件cVim

    参考资料:http://blog.csdn.net/hk2291976/article/details/51280816 常用命令: k,w:上移; j,s:下移:h:向左:l:向右:u:上半页d:下 ...

  6. Vue(十三)自定义指令

    自定义指令 分类:全局指令.局部指令 1. 自定义全局指令 使用全局方法Vue.directive(指令ID,定义对象) 2. 自定义局部指令   <!DOCTYPE html> < ...

  7. delphi 设置多屏幕

    //poScreenCenter时,窗体会显示到主显示器的中央 MainForm.Position := poScreenCenter; function TGAEAMainForm.GetWorkA ...

  8. 咏南APP(手机)开发框架

    咏南APP(手机)开发框架 有意者可向咏南索取DEMO. 基于DELPHI官方的FIREMONKEY类库构建,不使用任何三方控件. 原生手机框架,支持各种手机硬件操作. 主界面 聊天 照相并分享 短信 ...

  9. Reactor反应器模式 (epoll)

    1. 背景 最近在看redis源码,主体流程看完了. 在网上看到了reactor模式,看了一下,其实我们经常使用这种模式. 2. 什么是reactor模式 反应器设计模式(Reactor patter ...

  10. 20180821 Python学习笔记:如何获取当前程序路径

    20180821 Python学习笔记:如何获取当前程序路径 启动的脚本的路径为:D:\WORK\gitbase\ShenzhenHouseInfoCrawler\main.py 当前脚本的路径为:D ...