Dubbo&Zookepper

一、Dubbo的简介

二、案例一(与Spring结合)

1. 导入依赖(两个项目都要导入)

<!--Dubbo的依赖-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.5.3</version>
</dependency>
<!--单元测试的依赖-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!--Log4j日志的依赖-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!--Spring的依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.7.RELEASE</version>
</dependency>

2. 配置服务提供者项目(Provider)

2.1 在服务提供者端定义一个接口

package com.chenlianyuan.service;

public interface DemoService {

    String sayHello(String name);

}

2.2 在服务提供者端实现一个接口

package com.chenlianyuan.service.impl;

import com.chenlianyuan.service.DemoService;

public class DemoServiceImpl implements DemoService{

    public String sayHello(String name) {
        System.out.println("This is provider!");
        return "DemoService in Provicder:" + name;
    }

}

2.3 在服务提供者端配置Duboo(provider.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://code.alibabatech.com/schema/dubbo
                           http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="hello-provider-app"/>
    
    <!-- 用Dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />

    <!-- 声明需要暴露的服务接口 -->
    <!-- service -->
    <dubbo:service
            registry="N/A"
            interface="com.chenlianyuan.service.DemoService"
            ref="demoService" />

    <!-- 和本地bean一样实现服务 -->
    <bean id="demoService" class="com.chenlianyuan.service.impl.DemoServiceImpl"/>

</beans>

3. 配置服务调用者项目(Consumer)

3.1 在服务调用者端定义一个接口

//在服务调用者项目中的相同路径下要有service类,但不需要实现类
package com.chenlianyuan.service;

public interface DemoService {

    String sayHello(String name);

}

3.2 在服务调用者配置Duboo(consumer.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样-->
    <dubbo:application name="hello-consumer-app"/>
    
    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <!-- reference -->
    <dubbo:reference 
            id="demoService" 
            interface="com.chenlianyuan.service.DemoService" 
            url="dubbo://localhost:20880"/>
    
</beans>

4. 测试

4.1 配置服务提供者(Provider)的测试类

package com.chenlianyuan;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Provider {

    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(  "provider.xml");
        context.start();
        // 按任意键退出,用于保持程序运行状态
        System.in.read(); 
    }
}

4.2 配置服务调用者(Consumer)的测试类

package com.chenlianyuan;

import com.chenlianyuan.service.DemoService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Consumer {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
        context.start();
        // 获取远程服务代理
        DemoService demoService = (DemoService)context.getBean("demoService"); 
        //执行远程方法
        String hello = demoService.sayHello("world"); 
        //显示调用结果
        System.out.println(hello); 
    }

}

4.3 项目结构图

Dubbo&Zookepper

4.4 运行结果

//服务调用者(Consumer)的控制台
DemoService in Provicder:world
//服务提供者(Provider)的控制台
This is provider!

三、案例二(与Spring Boot结合)

1. 导入依赖(两个项目都要导入)

<!--新建Spring Boot项目会自动加入此依赖,是Spring Boot的核心依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<!--新建Spring Boot项目会自动加入此依赖,用于测试相关-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!--用于Dubbo与Spring Boot整合的依赖-->
 <dependency>
    <groupId>com.alibaba.spring.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>

2. 配置服务提供者项目(Provider)

2.1 在服务提供者端定义一个接口

package com.chenlianyuan.service;

public interface DemoService {

    String sayHello(String name);

}

2.2 在服务提供者端实现一个接口

package com.chenlianyuan.service.impl;
//这个@Service注解是由Dubbo提供的
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;

import com.chenlianyuan.service.DemoService;

@Service(interfaceClass = com.chenlianyuan.service.DemoService.class)
@Component
public class DemoServiceImpl implements DemoService{

    public String sayHello(String name) {
        System.out.println("This is provider!");
        return "DemoService in Provicder:" + name;
    }

}

2.3 在服务提供者端配置Dubbo(application.yml)

spring:
  application:
   name: dubbo-provider
  dubbo:
   server: true
   registry: N/A

3. 配置服务调用者项目(Consumer)

3.1 在服务调用者端定义一个接口

//在服务调用者项目中的相同路径下要有service类,但不需要实现类
package com.chenlianyuan.service;

public interface DemoService {

    String sayHello(String name);

}

3.2 在服务调用者端定义一个使用了Dubbo的类

package com.chenlianyuan;

import com.alibaba.dubbo.config.annotation.Reference;
import com.chenlianyuan.service.DemoService;
import org.springframework.stereotype.Component;

@Component("consumer")
public class Consumer {

    @Reference(url = "dubbo://localhost:20880")
    DemoService demoService;

    public String rpc(String  str){
        return demoService.sayHello(str);
    }

}

3.3 在服务调用者配置Duboo(application.yml)

spring:
  application:
   name: dubbo-consumer
  dubbo:
   registry: N/A

4. 测试

4.1 配置服务提供者(Provider)的测试类

package com.chenlianyuan.dubbo;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration//开启Dubbo
public class DubboProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboProviderApplication.class, args);
    }

}

4.2 配置服务调用者(Consumer)的测试类

package com.chenlianyuan;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableDubboConfiguration//开启Dubbo
public class DubboConsumerApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(DubboConsumerApplication.class, args);
        Consumer consumer = (Consumer) run.getBean("consumer");
        String rpc = consumer.rpc("hello");
        System.out.println(rpc);
    }

}

4.3 项目结构图

Dubbo&Zookepper

4.4 运行结果

//服务调用者(Consumer)的控制台
This is provider!
//服务提供者(Provider)的控制台
DemoService in Provicder:hello,cskaoyan

四、Dubbo的使用方法之直连提供者(用于测试环境)

1. 介绍

  • 案例一和案例二使用的微服务的用法,实际上是一种最简单的点对点调用方式。这种方式通常用于测试环境。
  • 在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表,A接口配置点对点,不影响B接口从注册中心获取列表。
  • 为了避免复杂化线上环境,不要在线上使用这个功能,只应在测试阶段使用。

Dubbo&Zookepper

2. 弊端

  • 不利于分布式的扩展

五、Dubbo的使用方法之使用ZooKeeper进行服务注册及服务发现(用于商业运行环境)

1. 架构图

Dubbo&Zookepper

Dubbo&Zookepper

2. ZooKeeper的简介

ZooKeeper是一个中间件,负责为分布式系统提供协调服务,即服务注册和服务发现。

3. ZooKeeper的安装(Windows下)

3.1 下载ZooKeeper

http://zookeeper.apache.org/

3.2 复制zoo_sample.cfg命名为zoo.cfg并修改配置

# 不用修改
tickTime=2000
# 不用修改
initLimit=10
# 不用修改
syncLimit=5
# 修改为本地磁盘上的路径
dataDir=D:\\ZooKeeper\\data
# 修改为本地磁盘上的路径
dataLogDir=D:\\ZooKeeper\\log
# 不用修改
clientPort=2181

六、案例三(与Spring和Zookepper结合)

1. 导入依赖(两个项目都要导入)

<!--Dubbo的依赖-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.5.3</version>
</dependency>
<!--单元测试的依赖-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!--Log4j日志的依赖-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!--Spring的依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.7.RELEASE</version>
</dependency>
<!--ZooKeeper的依赖-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
    <type>pom</type>
</dependency>
<!--ZooKeeper的依赖-->
<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.9</version>
</dependency>

2. 配置服务提供者项目(Provider)

2.1 在服务提供者端定义一个接口

package com.chenlianyuan.service;

public interface DemoService {

    String sayHello(String name);

}

2.2 在服务提供者端实现一个接口

package com.chenlianyuan.service.impl;

import com.chenlianyuan.service.DemoService;

public class DemoServiceImpl implements DemoService{

    public String sayHello(String name) {
        System.out.println("This is provider!");
        return "DemoService in Provicder:" + name;
    }

}

2.3 在服务提供者端配置Duboo(provider.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="hello-provider-app"/>
    
    <!-- 使用ZooKeeper作为注册中心 -->
    <dubbo:registry address="zookeeper://localhost:2181"/>
    
    <!-- 用Dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />

    <!-- 声明需要暴露的服务接口 -->
    <!-- 去掉registry="N/A" -->
    <dubbo:service       
            interface="com.chenlianyuan.service.DemoService"
            ref="demoService" />

    <!-- 和本地bean一样实现服务 -->
    <bean id="demoService" class="com.chenlianyuan.service.impl.DemoServiceImpl"/>

</beans>

3. 配置服务调用者项目(Consumer)

3.1 在服务调用者端定义一个接口

//在服务调用者项目中的相同路径下要有service类,但不需要实现类
package com.chenlianyuan.service;

public interface DemoService {

    String sayHello(String name);

}

3.2 在服务调用者配置Duboo(consumer.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样-->
    <dubbo:application name="hello-consumer-app"/>
    
    <!-- 使用ZooKeeper作为注册中心 -->
    <dubbo:registry address="zookeeper://localhost:2181"/>
    
    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <!-- 去掉url="dubbo://localhost:20880" -->
    <dubbo:reference 
            id="demoService" 
            interface="com.chenlianyuan.service.DemoService"/>
                
</beans>

4. 测试

4.1 配置服务提供者(Provider)的测试类

package com.chenlianyuan;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Provider {

    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(  "provider.xml");
        context.start();
        // 按任意键退出,用于保持程序运行状态
        System.in.read(); 
    }
}

4.2 配置服务调用者(Consumer)的测试类

package com.chenlianyuan;

import com.chenlianyuan.service.DemoService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Consumer {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
        context.start();
        // 获取远程服务代理
        DemoService demoService = (DemoService)context.getBean("demoService"); 
        //执行远程方法
        String hello = demoService.sayHello("world"); 
        //显示调用结果
        System.out.println(hello); 
    }

}

4.3 项目结构图

Dubbo&Zookepper

4.4 运行结果

//服务调用者(Consumer)的控制台
DemoService in Provicder:world
//服务提供者(Provider)的控制台
This is provider!

七、案例四(与Spring Boot和Zookepper结合)

1. 导入依赖(两个项目都要导入)

<!--新建Spring Boot项目会自动加入此依赖,是Spring Boot的核心依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<!--新建Spring Boot项目会自动加入此依赖,用于测试相关-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!--用于Dubbo与Spring Boot整合的依赖-->
 <dependency>
    <groupId>com.alibaba.spring.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
<!--导入zkclient依赖会自动引入ZooKeeper的相关依赖-->
<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.10</version>
</dependency>

2. 配置服务提供者项目(Provider)

2.1 在服务提供者端定义一个接口

package com.chenlianyuan.service;

public interface DemoService {

    String sayHello(String name);

}

2.2 在服务提供者端实现一个接口

package com.chenlianyuan.service.impl;
//这个@Service注解是由Dubbo提供的
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;

import com.chenlianyuan.service.DemoService;

@Service(interfaceClass = com.chenlianyuan.service.DemoService.class)
@Component
public class DemoServiceImpl implements DemoService{

    public String sayHello(String name) {
        System.out.println("This is provider!");
        return "DemoService in Provicder:" + name;
    }

}

2.3 在服务提供者端配置Dubbo(application.yml)

spring:
  application:
    name: dubbo-provider
  dubbo:
    server: true
    registry: zookeeper://localhost:2181

3. 配置服务调用者项目(Consumer)

3.1 在服务调用者端定义一个接口

//在服务调用者项目中的相同路径下要有service类,但不需要实现类
package com.chenlianyuan.service;

public interface DemoService {

    String sayHello(String name);

}

3.2 在服务调用者端定义一个使用了Dubbo的类

package com.chenlianyuan;

import com.alibaba.dubbo.config.annotation.Reference;
import com.chenlianyuan.service.DemoService;
import org.springframework.stereotype.Component;

@Component("consumer")
public class Consumer {

    //和Spring+ZooKeeper不一样
    //@Reference(url = "dubbo://localhost:20880")
    @Reference
    DemoService demoService;

    public String rpc(String  str){
        return demoService.sayHello(str);
    }

}

3.3 在服务调用者配置Duboo(application.yml)

spring:
  application:
    name: dubbo-consumer
  dubbo:
    registry: zookeeper://localhost:2181

4. 测试

4.1 配置服务提供者(Provider)的测试类

package com.chenlianyuan.dubbo;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration//开启Dubbo
public class DubboProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboProviderApplication.class, args);
    }

}

4.2 配置服务调用者(Consumer)的测试类

package com.chenlianyuan;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableDubboConfiguration//开启Dubbo
public class DubboConsumerApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(DubboConsumerApplication.class, args);
        Consumer consumer = (Consumer) run.getBean("consumer");
        String rpc = consumer.rpc("hello,cskaoyan");
        System.out.println(rpc);
    }

}

4.3 项目结构图

Dubbo&Zookepper

4.4 运行结果

//服务调用者(Consumer)的控制台
This is provider!
//服务提供者(Provider)的控制台
DemoService in Provicder:hello,cskaoyan

八、Dubbo负载均衡

1. 概念

  • LoadBalance中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载“均摊”到不同的机器上:

    • 避免集群中部分服务器压力过大。
    • 而在服务器比较空闲的时,通过负载均衡,可以让每台服务器获取到适合自己处理能力的负载。
  • y在为高负载服务器分流的同时,还可以避免资源浪费,一举两得。

2. 负载均衡策略

Dubbo&Zookepper

2.1 Random

  • Random,即随机加权。
  • RandomLoadBalance是加权随机算法的具体实现。
  • 它的算法思想很简单,假设我们有一组服务器 servers = [A, B, C],他们对应的权重为weights = [5, 3, 2],权重总和为10。现在把这些权重值平铺在一维坐标值上,[0, 5) 区间属于服务器 A,[5, 8) 区间属于服务器 B,[8, 10) 区间属于服务器 C。接下来通过随机数生成器生成一个范围在 [0, 10) 之间的随机数,然后计算这个随机数会落到哪个区间上。权重越大的机器,在坐标轴上对应的区间范围就越大,因此随机数生成器生成的数字就会有更大的概率落到此区间内。

2.2 RoundRobin

  • 我们有三台服务器 A、B、C。我们将第一个请求分配给服务器 A,第二个请求分配给服务器 B,第三个请求分配给服务器 C,第四个请求再次分配给服务器 A。这个过程就叫做轮询。
  • 轮询是一种无状态负载均衡算法,实现简单,适用于每台服务器性能相近的场景下。
  • 通常我们可以给轮询的机器设置不同的权重,经过加权后,每台服务器能够得到的请求数比例,接近或等于他们的权重比,比如服务器 A、B、C 权重比为 5:2:1。那么在8次请求中,服务器 A 将收到其中的5次请求,服务器 B 会收到其中的2次请求,服务器 C 则收到其中的1次请求。

2.3 LeastActive

  • LeastActiveLoadBalance 翻译过来是最小活跃数负载均衡。
  • 活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求。此时应优先将请求分配给该服务提供者。
  • 在具体实现中,每个服务提供者对应一个活跃数 active。初始情况下,所有服务提供者活跃数均为0。每收到一个请求,活跃数加1,完成请求后则将活跃数减1。在服务运行一段时间后,性能好的服务提供者处理请求的速度更快,因此活跃数下降的也越快,此时这样的服务提供者能够优先获取到新的服务请求、这就是最小活跃数负载均衡算法的基本思想。

2.4 ConsistentHash

  • ConsistentHash具有一致性,相同参数的请求总是发到同一提供者。
  • 当某一台服务提供者挂了时,原本发往该服务提供者的请求会基于虚拟节点平摊到其它服务提供者,不会引起剧烈变动。

3. 配置

3.1 Random

此为默认配置

3.2 RoundRobin

3.2.1 Consumer端配置

<!--Consumer端service级别-->
<!--该service的所有method都使用roundrobin负载均衡-->
<dubbo:reference interface="..." loadbalance="roundrobin"/>
<!--Consumer端method级别-->
<!--只有该service的某些method使用roundrobin负载均衡-->
<dubbo:reference interface="...">
    <dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:reference>

3.2.2 Provider端配置

<!--Provider端service级别-->
<!--该service的所有method都使用roundrobin负载均衡-->
<dubbo:service interface="..." loadbalance="roundrobin"/>
<!--Provider端method级别-->
<!--只有该service的某些method使用roundrobin负载均衡-->
<dubbo:serivce interface="...">
    <dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:service>

3.3 LeastActive

3.4 ConsistentHash

3.5 配置的优先级

  • 和Dubbo其他的配置类似,多个配置是有覆盖关系的:

    • 方法级优先,接口级次之,全局配置再次之。
    • 如果级别一样,则消费方优先,提供方次之。
  • 上面4种配置的优先级是: (由高到低)

    • 客户端方法级别配置。
    • 客户端接口级别配置。
    • 服务端方法级别配置。
    • 服务端接口级别配置。