dubbo系列二:dubbo經常使用功能總結

準備工做:html

(1)啓動zookeeper做爲dubbo的註冊中心java

(2)新建一個maven的生產者web工程dubbo-provider-web和一個maven的消費者web工程dubbo-consumer-webnode

(3)在pom.xml文件裏面引入以下依賴git

<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裏面配置須要暴露和引用的接口程序員

掃描包路徑的配置github

<!-- 掃描註解包路徑,多個包用逗號分隔,不填pacakge表示掃描當前ApplicationContext中全部的類 -->
    <dubbo:annotation package="com.study.service" />

1.1 在生產者dubbo-provider-web和消費者dubbo-consumer-web新建一個dubbo註解測試的接口web

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新建一個測試的controlspring

/**
 * 
 * @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訪問查看效果apache

生產者端:

瀏覽器:

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

相關文章
相關標籤/搜索