文章目录

  • 一、Apache Dubbo概述
  • 1. Dubbo简介
  • 2. Dubbo架构
  • 二、 Dubbo快速入门
  • 1. 点对点直连
  • 【1】服务提供者-service项目
  • 【2】服务消费者-web项目
  • 2. zookeeper注册中心
  • 【1】服务提供者
  • 【2】服务消费者
  • 三、 Dubbo管理控制台
  • 1. jar包部署管理平台
  • 2. war包部署管理平台
  • 四、Dubbo开发SpringBoot
  • 1. Dubbo文档查询
  • 2. 架构模型
  • 3. 代码实现
  • 五、 Dubbo配置及注册中心
  • 1. 配置
  • 2. 注册中心
  • 【1】zookeeper
  • 【2】redis
  • 【3】 Multicast
  • 【4】 Nacos
  • 【5】 Simple
  • 3. dubbo协议(了解)
  • 【1】协议比较
  • 【2】Dubbo协议介绍
  • 六、Dubbo高级
  • 1. Dubbo的负载均衡
  • 2. Dubbo超时
  • 3. 服务降级
  • 4. RpcContext对象
  • 总结


一、Apache Dubbo概述

1. Dubbo简介

Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。

什么是RPC?

RPC全称为remote procedure call,即远程过程调用。比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。

注:RPC并不是一个具体的技术,而是指整个网络远程调用过程。

RPC是一个泛化的概念,严格来说一切远程过程调用手段都属于RPC范畴。各种开发语言都有自己的RPC框架。Java中的RPC框架比较多,广泛使用的有RMI、Hessian、Dubbo等。

Dubbo官网地址:http://dubbo.apache.org

Dubbo提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

2. Dubbo架构

Dubbo架构图(Dubbo官方提供)如下:

虚线都是异步访问,实线都是同步访问

蓝色虚线:在启动时完成的功能 红色虚线(实线):都是程序运行过程中执行的功能


dubbo远程接口调用使用的协议 dubbo远程调用实现_apache

节点角色说明:

节点

角色名称

Provider

暴露服务的服务提供方

Consumer

调用远程服务的服务消费方

Registry

服务注册与发现的注册中心

Monitor

统计服务的调用次数和调用时间的监控中心

Container

服务运行容器

调用关系说明:

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

二、 Dubbo快速入门

1. 点对点直连

【1】服务提供者-service项目
1.创建maven项目,导入jar包坐标
2.编写Service层代码
3.编写测试类,用于发布服务

1.创建maven项目,导入jar包坐标

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.7.5</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.2.0</version>
</dependency>

2.编写Service层代码

package com.ahcfl.service;

public interface HelloService {
    String sayHello(String name);
}

========================================
    
package com.ahcfl.service.impl;

import com.ahcfl.service.HelloService;

public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        return "hello "+name;
    }
}

3.编写测试类,用于发布服务

package com.ahcfl.app;

import com.ahcfl.service.HelloService;
import com.ahcfl.service.impl.HelloServiceImpl;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;

import java.io.IOException;

public class ProviderApp {
    /**
     * 将服务发布出去
     */
    public static void main(String[] args) throws IOException {
        //1.创建提供服务的类对象
        HelloService helloService = new HelloServiceImpl();
        //2.创建服务提供方应用配置
        ApplicationConfig providerApp = new ApplicationConfig();
        // 设置应用名称
        providerApp.setName("Provider_hello");
        //3.注册中心配置
        // 设置无注册中心
        RegistryConfig registryConfig = new RegistryConfig(RegistryConfig.NO_AVAILABLE);
        //4.访问配置
        ProtocolConfig protocol = new ProtocolConfig();
        // 访问服务提供者时,遵循的协议
        protocol.setName("dubbo");
        // 访问服务提供者时,使用的端口
        protocol.setPort(20880);
        // 访问服务提供者时,线程数量
        protocol.setThreads(200);
        //5.服务提供者暴露服务配置
        ServiceConfig<HelloService> service = new ServiceConfig<>();
        // 设置应用配置
        service.setApplication(providerApp);
        // 设置注册中心
        service.setRegistry(registryConfig);
        // 设置访问配置
        service.setProtocol(protocol);
        // 设置服务接口
        service.setInterface(HelloService.class);
        // 设置应用实例
        service.setRef(helloService);
        // 设置服务提供者版本
        service.setVersion("1.0");
        //6.暴露服务
        service.export();

        System.in.read();
    }
}
【2】服务消费者-web项目
1.创建maven项目,导入jar包坐标
2.编写服务接口
3.编写测试类,用于消费dubbo发布的服务

1.创建maven项目,导入jar包坐标

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.7.5</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.2.0</version>
</dependency>

2.编写服务接口

package com.ahcfl.service;

public interface HelloService {
    String sayHello(String name);
}

3.编写测试类,用于消费dubbo发布的服务

package com.ahcflahcfl.app;

import com.ahcfl.service.HelloService;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;

import java.lang.reflect.Proxy;

public class ConsumerApp {
    /**
     * 服务消费者方
     */
    public static void main(String[] args) {
        //1.当前应用配置
        ApplicationConfig application = new ApplicationConfig();
        // 设置应用的名称
        application.setName("Consumer_hello");

        //2.连接注册中心配置
        RegistryConfig registry = new RegistryConfig(RegistryConfig.NO_AVAILABLE);


        //3.引用远程服务
        ReferenceConfig<HelloService> reference = new ReferenceConfig<HelloService>();
        // 设置应用配置
        reference.setApplication(application);
        // 设置注册中心配置
        reference.setRegistry(registry);
        // 设置服务提供者的访问路径
        String url = "dubbo://10.254.29.50:20880/com.ahcfl.service.HelloService";
        reference.setUrl(url);
        // 设置服务接口的字节码文件
        reference.setInterface(HelloService.class);
        // 设置版本号
        reference.setVersion("1.0");

        //4.远程过程调用
        // 调用get方法会生成接口的代理类对象,在代理类对象中的方法内,完成远程过程调用
        HelloService helloService = reference.get();
        String str = helloService.sayHello("tom");
        System.out.println("远程过程调用返回值: "+str);
    }
}

2. zookeeper注册中心

注: 先启动Zookeeper服务

【1】服务提供者
package com.ahcfl.app;

import com.ahcfl.service.HelloService;
import com.ahcfl.service.impl.HelloServiceImpl;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;

import java.io.IOException;

public class ProviderApp {
    /**
     * 服务提供者应用
     * 作用: 用于发布服务,让外界调用
     */
    public static void main(String[] args) throws IOException {
        //TODO:1.创建提供服务的类实例
        HelloService helloService = new HelloServiceImpl();
        //TODO:2.当前应用配置: 创建应用配置信息
        ApplicationConfig applicationConfig = new ApplicationConfig();
        // 配置应用名称
        applicationConfig.setName("Provider_hello");
        //TODO:3.注册中心配置: 配置注册中心的路径
        // 配置当前没有可以使用的注册中心(点对点直连-web项目直接访问service项目)
        //RegistryConfig registryConfig = new RegistryConfig(RegistryConfig.NO_AVAILABLE);
        // 配置zookeeper注册中心地址
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://127.0.0.1:2181");
        //TODO:4.设置访问提供者的协议与端口
        ProtocolConfig protocolConfig = new ProtocolConfig();
        // 设置访问时的协议
        protocolConfig.setName("dubbo");
        // 设置访问时的端口 dubbo协议默认访问端口为20880
        protocolConfig.setPort(20880);
        // 设置访问当前服务的最大线程数
        protocolConfig.setThreads(200);
        //TODO:5.发布服务 (发布服务时,需要设置泛型,用于描述当前发布的是哪个接口对应的服务)
        ServiceConfig<HelloService> serviceConfig = new ServiceConfig<>();
        // 设置当前应用的配置信息
        serviceConfig.setApplication(applicationConfig);
        // 设置注册中心配置
        serviceConfig.setRegistry(registryConfig);
        // 设置访问时的协议与端口配置
        serviceConfig.setProtocol(protocolConfig);
        // 设置服务接口
        serviceConfig.setInterface(HelloService.class);
        // 设置提供服务的实现类对象
        serviceConfig.setRef(helloService);
        // 设置当前服务的版本号
        serviceConfig.setVersion("1.0.0");
        // 发布服务
        serviceConfig.export();

        System.in.read();
    }
}
【2】服务消费者
package com.ahcfl.app;

import com.ahcfl.service.HelloService;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;

import java.io.IOException;

public class ConsumerApp {
    /**
     * 服务消费者: 用于远程调用service项目中的代码
     */
    public static void main(String[] args) throws IOException {
        //TODO:1.创建消费者应用配置
        ApplicationConfig applicationConfig = new ApplicationConfig();
        // 设置应用名称
        applicationConfig.setName("Consumer_hello");
        //TODO:2.连接注册中心配置: 配置注册中心地址
        // 没有注册中心
        //RegistryConfig registryConfig = new RegistryConfig(RegistryConfig.NO_AVAILABLE);
        // 设置zookeeper注册中心地址
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://127.0.0.1:2181");
        //TODO:3.引用远程服务 (在本应用中创建接口的实现类,在实现类中完成远程调用)
        ReferenceConfig<HelloService> referenceConfig = new ReferenceConfig<>();
        // 设置应用名称
        referenceConfig.setApplication(applicationConfig);
        // 设置注册中心
        referenceConfig.setRegistry(registryConfig);
        // TODO:设置消费者路径(每一个人生成的消费者虚拟路径都不一样,注意修改)
        // String url = "dubbo://10.254.34.38:20880/com.ahcfl.service.HelloService";
        // referenceConfig.setUrl(url);
        // 设置服务接口
        referenceConfig.setInterface(HelloService.class);
        // 设置版本
        referenceConfig.setVersion("1.0.0");

        // 远程调用提供者提供的方法
        HelloService helloService = referenceConfig.get();
        String result = helloService.sayHello("Tom");
        System.out.println("远程过程调用的结果: "+result);
        System.in.read();
    }
}

优化:服务接口抽取

思考: 在入门案例中service层的接口分别在两个项目中都存在,这样合理吗? 该如何优化

不合理.
将冗余的接口抽离成一个独立的java项目,哪个项目需要使用此接口,直接依赖当前项目即可

三、 Dubbo管理控制台

我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。

之前都是通过zookeeper客户端命令来查看。除了命令外,我们也可以通过部署一个管理中心来实现。

管理中心是Dubbo编写好的, 直接拿过来运行使用即可。Dubbo提供的管理平台有两种一种war包,一种jar包。

1. jar包部署管理平台

dubbo-admin-2.6.0.jar 下载

直接将dubbo-admin.jar命令窗口运行即可

访问: http://localhost:8081
    
注意1-端口:
    为了防止内置tomcat端口与我们常用的端口冲突,我已将端口设置成了8101
    也可通过外部指定配置进行端口的设置
        java -jar jar包名 --server.port=端口号
注意2-zookeeperk地址:
		如果你的zookeeper没有存放在本机,需要编辑jar包中的配置,设置你的zookeeper地址与端口
内置账号:
  账户1: root
  密码: root
  账户2: guest
  密码: guest

2. war包部署管理平台

(1)dubbo-admin-2.6.0.war 下载到tomcat的webapps目录下

(2)启动tomcat,此war文件会自动解压

(3)访问tomcat服务 http://localhost:8080/dubbo-admin-2.6.0/

注意: 
如果你的zookeeper不是部署在本机, 那么需要在项目解压后WEB-INF下的dubbo.properties文件中的dubbo.registry.address对应的值需要设置成你使用的Zookeeper的ip地址和端口号
dubbo.registry.address=zookeeper://192.168.190.137:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest

访问http://localhost:8080/dubbo-admin-2.6.0/,输入用户名(root)和密码(root)

当启动服务提供者工程和服务消费者工程,可以在查看到对应的信息

四、Dubbo开发SpringBoot

1. Dubbo文档查询

Dubbo作为一个RPC框架,其最核心的功能就是要实现跨网络的远程过程调用。

本章节就是要创建两个应用(app/项目),一个作为服务的提供方,一个作为服务的消费方。

通过Dubbo来实现服务消费方远程调用服务提供方的方法。

版本开发要求说明: 开发中所有的版本统一,避免环境问题导致的代码无法运行

  • jdk1.8 环境变量配置要OK
  • springboot 2.4.0
  • dubbo 2.7.5
  • zookeeper 3.5.8
  • maven 环境搭建OK 连接好阿里云远程仓库
Dubbo在线文档地址: 此地址有可能发生变动,具体地址应通过官网查找 
	http://dubbo.apache.org/zh/docs/v2.7/user/configuration/
注解配置Dubbo相关API:
	http://dubbo.apache.org/zh/docs/v2.7/user/configuration/annotation/

2. 架构模型

经典开发模式:


dubbo远程接口调用使用的协议 dubbo远程调用实现_RPC_02

采用Dubbo 框架SOA面向服务开发流程:


dubbo远程接口调用使用的协议 dubbo远程调用实现_apache_03

dubbo远程接口调用使用的协议 dubbo远程调用实现_apache_04

3. 代码实现

步骤:
1.创建三个模块  编写依赖
    dubbo_api:     存放接口规范  普通的maven项目
    dubbo_service: 服务提供方    springboot构建的web项目
    dubbo_web:     服务消费方    springboot构建的web项目
    dubbo_service模块依赖dubbo_api
    dubbo_web模块依赖dubbo_api
2.编写dubbo_api
    创建接口规范
3.编写dubbo_service
    导入jar包依赖
    编写服务代码[完成接口实现]
    编写配置文件,完成注册中心配置
	编写引导类,启动服务器,完成服务注册
4.编写dubbo_web
    导入jar包
    编写Controller代码
    编写配置文件,完成注册中心配置
    编写引导类,启动服务器,完成服务注册

1、创建三个模块 并设置依赖关系

<!--dubbo_service模块依赖dubbo_api-->
<dependency>
    <groupId>com.ahcfl</groupId>
    <artifactId>dubbo_api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<!--dubbo_web模块依赖dubbo_api-->
<dependency>
    <groupId>com.ahcfl</groupId>
    <artifactId>dubbo_api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

2、编写dubbo_api 创建接口规范

package com.ahcfl.service;

public interface UserService {
    String sayHello(String name);
    String findAll();
}

3、编写dubbo_service 服务提供方

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.5</version>
</dependency>

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.7.5</version>
</dependency>

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.5.8</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.10</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.2.0</version>
</dependency>

编写服务代码【实现接口】

package com.ahcfl.service.impl;

import com.ahcfl.service.UserService;
import org.apache.dubbo.config.annotation.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public String sayHello(String name) {
        System.out.println("=======sayHello服务被调用========");
        return "hello "+name;
    }

    @Override
    public String findAll() {
        return "查询到了所有的数据啊...";
    }
}

编写配置文件,完成注册中心配置

# dubbo-provider.properties
# 设置应用(项目-app)的名称
dubbo.application.name=user-provider-service
# Zookeeper地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 消费者在消费此服务时需要遵循的协议
# web层在调用service层的方法时,需要遵循的协议
dubbo.protocol.name=dubbo
# 消费者在消费此服务时需要使用的端口
# web层在调用service层的方法时,需要使用的端口 (dubbo协议默认使用20880端口)
dubbo.protocol.port=20880

# 设置tomcat端口
server.port=8081

编写引导类,启动服务器,完成服务注册

package com.ahcfl;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.ahcfl.service.impl")
public class DubboServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboServiceApplication.class, args);
        System.out.println("========服务方启动了,服务发布成功========");
    }
}

4、dubbo_web: 服务消费方

导入jar包

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.5</version>
</dependency>

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.7.5</version>
</dependency>

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.5.8</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.10</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.2.0</version>
</dependency>

编写Controller代码

package com.ahcfl.web;

import com.ahcfl.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Reference
    private UserService service;

    @RequestMapping("/hello")
    public String hello(String name){
        return service.sayHello(name);
    }

    @RequestMapping("/findAll")
    public String findAll(){
        return service.findAll();
    }
}

编写配置文件,完成注册中心配置

# dubbo-consumer.properties
dubbo.application.name=dubbo-consumer-web
dubbo.registry.address=zookeeper://127.0.0.1:2181

server.port=8085

编写引导类,启动服务器,完成服务注册

package com.ahcfl;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.ahcfl.web")
public class DubboWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboWebApplication.class, args);
        System.out.println("--------消费者启动了---------");
    }

}

五、 Dubbo配置及注册中心

1. 配置

# 包扫描
@EnableDubbo(scanBasePackages = "包路径")
	服务提供者: 扫描包发现@Service注解,并将对应的类创建实例存放到IOC容器,并发布到Zookeeper中
    服务消费者: 扫描@Reference注解,完成代理类的注入
@Service
    创建类对象存放到IOC容器中并发布到Zookeeper中
@Reference
    注入Dubbo生成的代理类对象
   
# 启动时检查
在注入服务的代理类时不检查
@Reference(check = false)

我们可以在配置文件中进行全局配置
dubbo.consumer.check=false    ----- properties
dubbo:
  consumer:
    check: false
    
上面这个配置需要配置在=服务消费者一方=,如果不配置, 默认check值为true。
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,
能及早发现问题。可以通过将check值改为false来关闭检查。
建议在开发阶段将check值设置为false,在生产环境下改为true。

2. 注册中心

【1】zookeeper

dubbo的官方文档中推荐使用zookeeper作为注册中心.


dubbo远程接口调用使用的协议 dubbo远程调用实现_服务提供者_05

流程说明

  • 服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
  • 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
  • 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。
dubbo:
  registry:
    address: zookeeper://127.0.0.1:2181
【2】redis

dubbo远程接口调用使用的协议 dubbo远程调用实现_apache_06

使用 Redis 的 Key/Map 结构存储数据结构:

  • 主 Key 为服务名和类型
  • Map 中的 Key 为 URL 地址
  • Map 中的 Value 为过期时间,用于判断脏数据,脏数据由监控中心删除

使用,需要在maven中添加依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.2</version>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.3.0</version>
</dependency>

配置文件中使用:

dubbo:
  registry:
    address: redis://127.0.0.1:6379

dubbo远程接口调用使用的协议 dubbo远程调用实现_dubbo远程接口调用使用的协议_07

【3】 Multicast

Multicast 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。

dubbo远程接口调用使用的协议 dubbo远程调用实现_服务提供者_08

  1. 提供方启动时广播自己的地址
  2. 消费方启动时广播订阅请求
  3. 提供方收到订阅请求时,单播自己的地址给订阅者,如果设置了 unicast=false,则广播给订阅者
  4. 消费方收到提供方地址时,连接该地址进行 RPC 调用。

组播受网络结构限制,只适合小规模应用或开发阶段使用。组播地址段: 224.0.0.0 - 239.255.255.255

【4】 Nacos

Nacos 是 Dubbo 生态系统中重要的注册中心实现,其中 dubbo-registry-nacos 则是 Dubbo 融合 Nacos 注册中心的实现。

spring cloud aliaba会用到nacos

【5】 Simple

Simple 注册中心本身就是一个普通的 Dubbo 服务,可以减少第三方依赖,使整体通讯方式一致。

服务暴露:

dubbo:
  protocol:
    port: 9090

引用配置:

dubbo:
  registry:
    address: 127.0.0.1:9090

3. dubbo协议(了解)

【1】协议比较

dubbo框架中支持调用的协议有很多,如:dubbo、hessian、rmi、http等,推荐使用 Dubbo 协议,各协议性能对比:

dubbo远程接口调用使用的协议 dubbo远程调用实现_dubbo_09

【2】Dubbo协议介绍

Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。

反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。

dubbo远程接口调用使用的协议 dubbo远程调用实现_apache_10

  • Transporter: mina, netty, grizzy
  • Serialization: dubbo, hessian2, java, json
  • Dispatcher: all, direct, message, execution, connection
  • ThreadPool: fixed, cached

特性

缺省协议,使用基于 mina 1.1.7 和 hessian 3.2.1 的 tbremoting 交互。

  • 连接个数:单连接
  • 连接方式:长连接
  • 传输协议:TCP
  • 传输方式:NIO 异步传输
  • 序列化:Hessian 二进制序列化
  • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。
  • 适用场景:常规远程服务方法调用

约束

  • 参数及返回值需实现 Serializable 接口
  • 参数及返回值不能自定义实现 List, Map, Number, Date, Calendar 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。
  • Hessian 序列化,只传成员属性值和值的类型,不传方法或静态变量,兼容情况

数据通讯

情况

结果

A->B

类A多一种 属性(或者说类B少一种 属性)

不抛异常,A多的那 个属性的值,B没有, 其他正常

A->B

枚举A多一种 枚举(或者说B少一种 枚举),A使用多 出来的枚举进行传输

抛异常

A->B

枚举A多一种 枚举(或者说B少一种 枚举),A不使用 多出来的枚举进行传输

不抛异常,B正常接 收数据

A->B

A和B的属性 名相同,但类型不相同

抛异常

A->B

serialId 不相同

正常传输

接口增加方法,对客户端无影响,如果该方法不是客户端需要的,客户端不需要重新部署。输入参数和结果集中增加属性,对客户端无影响,如果客户端并不需要新属性,不用重新部署。

输入参数和结果集属性名变化,对客户端序列化无影响,但是如果客户端不重新部署,不管输入还是输出,属性名变化的属性值是获取不到的。

总结:服务器端和客户端对领域对象并不需要完全一致,而是按照最大匹配原则。

配置协议

默认配置:

dubbo:
  protocol:
    name: dubbo

六、Dubbo高级

1. Dubbo的负载均衡

搭建服务提供者集群: service

yml配置

# dubbo-provider.properties
# 设置应用(项目-app)的名称
dubbo:
  application:
    name: user-provider-service
# Zookeeper地址
  registry:
    address: zookeeper://127.0.0.1:2181
# 消费者在消费此服务时需要遵循的协议
# web层在调用service层的方法时,需要遵循的协议
  protocol:
    name: dubbo
# 消费者在消费此服务时需要使用的端口
# web层在调用service层的方法时,需要使用的端口 (dubbo协议默认使用20880端口)
    port: -1

# 设置tomcat端口
server:
  # 获取动态的port值
  port: ${port}

配置三个服务端启动 一个客户端启动 端口号不能相同

dubbo远程接口调用使用的协议 dubbo远程调用实现_apache_11

实现负载均衡

只需要在服务端配置一个属性即可:
/**
 * loadbalance: 配置集群的模式(负载均衡)
 *      random: 随机
 *      roundrobin: 轮循
 *      leastactive: 最活跃的
 */
//@Service(loadbalance="roundrobin")
// 配置响应超时时间
@Service(timeout = 3000)
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        ...............
        ...............
    }

2. Dubbo超时

# 超时概念
服务消费者引用服务提供者的服务时可能由于网络原因导致长时间未返回响应,
此时大量的线程将会阻塞,引起性能下降等问题。
可以通过引入服务超时来解决该问题,一般配合retries(重试次数)使用。单位毫秒,默认值1000毫秒。
在服务的消费方,配置 retries 重试,默认值为 2 

# Dubbo超时的原理
dubbo默认采用了netty做为网络组件,它属于一种NIO的模式。
消费端发起远程请求后,线程不会阻塞等待服务端的返回,而是马上得到一个ResponseFuture,
消费端通过不断的轮询机制判断结果是否有返回。
因为是通过轮询,轮询有个需要特别注要的就是避免死循环,所以为了解决这个问题就引入了超时机制,
只在一定时间范围内做轮询,如果超时时间就返回超时异常

# 超时解决的是什么问题
当前端大量请求并发出现时,很有可能将业务线程池中的线程消费完,因为默认缺省的线程池是固定大小,
对调用的服务设置超时时间,是为了避免因为某种原因导致线程被长时间占用,最终出现线程池用完返回拒绝服务的异常。

超时代码演示

# 应用名称
dubbo:
  application:
    name: web-consumer
# 注册中心地址
  registry:
    address: zookeeper://127.0.0.1:2181
# 超时时间
  consumer:
# 全局配置服务的超时时间
    timeout: 3000
# 启动时不检查远程服务是否存在(默认值为true)
    check: false
# 配置重试次数
    retries: 1  // 默认重试3次 一次2秒

# tomcat端口
server:
  port: ${port}
package com.ahcfl.service.impl;

import com.ahcfl.service.HelloService;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;
// TODO:将此实现类发布成服务,使用Dubbo的注解

/**
 * loadbalance: 配置集群的模式(负载均衡)
 *      random: 随机
 *      roundrobin: 轮循
 *      leastactive: 最活跃的
 */
//@Service(loadbalance="roundrobin")
// 配置当前服务的响应超时时间
@Service(timeout = 2000)  
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        int port = 0;
        try {
            // 获取当前服务所使用的端口好
            port = RpcContext.getContext().getLocalPort();
            System.out.println("sayHello方法执行了..."+port);
            Thread.sleep(7000);
        } catch (InterruptedException e) {
        }
        return "Hello "+name+" ! "+port;
    }
}

3. 服务降级

注: 服务降级在服务的消费方进行

mock方式完成服务降级
@Reference(check = false,timeout = 2000,mock = "com.ahcfl.service.UserServiceMock")

示例代码:

package com.ahcfl.web;

import com.ahcfl.service.HelloService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    // 根据接口创建代理类,在代理类中远程过程调用目标对象
    // check属性: 注入该对象时无需检测提供者是否存在
    //@Reference(check = false)
    // mock: 设置服务降级时调用的相关方法
    // 访问提供者的方法超过2s后,进行服务降级
    @Reference(timeout = 2000,mock = "com.ahcfl.mock.HelloServiceMock")
    private HelloService service;

    @RequestMapping("/sayHello/{name}")
    public String sayHello(@PathVariable("name")String name){
        // 远程过程调用service
        String result = service.sayHello(name);
        System.out.println("远程调用结果为: " + result);
        return result;
    }
}
package com.ahcfl.mock;

import com.ahcfl.service.HelloService;

/**
 * Mock类:
 *      服务降级时调用的相关方法
 * 要求:
 *      编写一个类,实现服务接口,重写方法,一个方法对应一个降级行为
 */
public class HelloServiceMock implements HelloService {
    @Override
    public String sayHello(String name) {
        return "访问资源的人数太多,请稍后访问....";
    }
}

4. RpcContext对象

RpcContext : Rpc架构的上下文对象,用于在消费者与服务者之间进行资源共享。隐式传参

// 向RpcContext中设置属性值
RpcContext.getContext().setAttachment("name","lisi");

// 从RpcContext中获取属性值
RpcContext.getContext().getAttachment("name");
@RequestMapping("/sayHello/{name}")
public String sayHello(@PathVariable("name") String name){
    System.out.println("web项目执行了 : "+name);
    // RpcContext: 远程过程调用上下文对象
    // 向RpcContext对象中存放数据信息 (注:在远程过程调用前进行设置)
    RpcContext.getContext().setAttachment("name","lisi");
    return helloService.sayHello(name);
}

总结

软件架构演进过程:
		软件的结构
        单体架构: all in one
          所有的功能都存放在一个项目中
          缺点:
							技术栈受限
              扩展维护不方便
              如果对整个项目进行性能扩展只能通过搭建集群(成本高)
        垂直架构:
					根据业务划分,将一个大的项目拆分成几个小项目
          优点:
							可以使用多种不同的技术构建项目
          缺点:
							多个小项目中存在冗余的模块
              扩展维护不方便
              如果对整个项目进行性能扩展只能通过搭建集群(成本高)    
        SOA架构
          将冗余的模块抽离成独立的服务
          缺点:
							各系统之间业务不同,很难确认功能或模块是重复的。
					    抽取服务的粒度大。
							系统和服务之间耦合度高。
        微服务架构
            抽取服务的粒度更小
            一个大型项目中服务太多,对整个技术团队都是一个很大挑战.
RPC:远程过程调用,指的是调用的过程
  	Apache Dubbo是一款rpc框架,实现了远程过程调用,Dubbo在调用目标服务器上的服务时,默认遵循Dubbo协议。
		Dubbo架构图
    
Dubbo入门:
		编写点对点的服务调用
        借助Zookeeper做注册中心,完成远程过程调用调用
        公共接口抽取
Dubbo的管理控制平台:
		通过Dubbo提供的管理平台项目我们可以在浏览器上查看到有哪些服务提供方与服务消费方
Dubbo整合SpringBoot: ★
		注解API:
			@Service
			@Reference
			@EnableDubbo(scanBasePackages="包")
Dubbo实现服务端的负载均衡:默认随机策略
Dubbo的超时
Dubbo的降级