Dubbo基础知识+实战Demo

Posted 尚墨1111

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo基础知识+实战Demo相关的知识,希望对你有一定的参考价值。

1. RPC

1.1 是什么

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务 A、B 部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求 当然可以,但是可能会比较慢而且一些优化做的并不好。

最终解决的问题:**让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单。**是一种通过网络从远程计算机程序上请求服务

1.2 常用的 RPC 框架

  • Dubbo: Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。
  • gRPC :gRPC 是可以在任何环境中运行的现源高性能RPC框架。
  • Hessian: 采用的是二进制 RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。

1.3 实现原理:

// Client端 
// Student student = Call(ServerAddr, addAge, student)
1. 将这个调用映射为Call ID。
2.Call ID,student(params)序列化,以二进制形式打包
3.2中得到的数据包发送给ServerAddr,这需要使用网络传输层
4. 等待服务器返回结果
5. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新

// Server端
1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap
2. 等待客户端请求
3. 得到一个请求后,将其数据包反序列化,得到Call ID
4. 通过在callIdMap中查找,得到相应的函数指针
5. 将student(params)反序列化后,在本地调用addAge()函数,得到结果
6. 将student结果序列化后通过网络返回给Client
  • 服务端通过字符串解析出该字符串代表的接口的一切信息,需要用到反射技术

  • 客户端和服务端之间进行通信调用,需要用到socket技术

  • 服务端根据客户端不同请求返回不同的接口类型,此时客户端就要接收不同的接口类型,需要在客户端用到动态代理技术。

1.3.1 生成调用方法的请求:动态代理

基于动态代理生成代理对象,当调用代理对象的方法时,由代理进行相关信息(方法、参数等)的组装并发送到服务器进行远程调用,并由代理接收调用结果并返回

使用 JDK 动态代理类基本步骤:

1、编写需要被代理的类和接口

2、编写代理类,需要实现 InvocationHandler 接口,重写 invoke() 方法;

3、使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)动态创建代理类对象,通过代理类对象调用业务方法。

interface DemoInterface {
    String hello(String msg);
}

class DemoImpl implements DemoInterface {
    @Override
    public String hello(String msg) {
        System.out.println("msg = " + msg);
        return "hello";
    }
}

class DemoProxy implements InvocationHandler {

    private DemoInterface service;

    public DemoProxy(DemoInterface service) {
        this.service = service;
    }

    @Override
    public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("调用方法前...");
        Object returnValue = method.invoke(service, args);
        System.out.println("调用方法后...");
        return returnValue;
    }

}

public class Solution {
    public static void main(String[] args) {
        DemoProxy proxy = new DemoProxy(new DemoImpl());
        DemoInterface service = Proxy.newInstance(
            DemoInterface.class.getClassLoader(),
            new Class<?>[]{DemoInterface.class},
            proxy
        );
        System.out.println(service.hello("呀哈喽!"));
    }
}

1.3.2 传输使用序列化

RPC 是一种设计,就是为了解决不同服务之间的调用问题,完整的 RPC 实现一般会包含有 传输协议序列化协议 这两个。

而 HTTP 是一种传输协议,RPC 框架完全可以使用 HTTP 作为传输协议,也可以直接使用 TCP,使用不同的协议一般也是为了适应不同的场景。

使用 TCP 和使用 HTTP 各有优势:

传输效率

  • TCP,通常自定义上层协议,可以让请求报文体积更小
  • HTTP:如果是基于HTTP 1.1 的协议,请求中会包含很多无用的内容

性能消耗,主要在于序列化和反序列化的耗时

  • TCP,可以基于各种序列化框架进行,效率比较高
  • HTTP,大部分是通过 json 来实现的,字节大小和序列化耗时都要更消耗性能

跨平台

  • TCP:通常要求客户端和服务器为统一平台
  • HTTP:可以在各种异构系统上运行

总结
  RPC 的 TCP 方式主要用于公司内部的服务调用,性能消耗低,传输效率高。HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等。

2 Dubbo:远程过程调用

Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

Remoting: 网络通信框架,实现了 sync-over-async 和request-response 消息机制.

RPC: 一个远程过程调用的抽象,支持负载均衡容灾集群功能

Registry: 服务目录框架用于服务的注册和服务事件发布和订阅

2.1 Dubbo框架结构

调用关系说明

  • Container负责启动,加载,运行服务提供者。

  • Provider在启动时,向注册中心注册自己提供的服务。

  • Consumer在启动时,向注册中心订阅自己所需的服务。

  • Register返回Provider地址列表给Consumer,如果有变更,注册中心将基于长连接推送变更数据给消费者。

  • Consumer从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

要启用服务发现,需要为 Dubbo 增加注册中心配置:在启动服务提供者项目之前要先启动zookeeper,因为我们需要把服务提供方注册到zookeeper中,然后才能被服务消费方发现并调用。

2.3 项目构建的相关

① 有关项目构建的相关内容

  • archetype 的作用是帮你生成某一种项目的通用文件布局,比如常用的maven-archetype-webapp
  • GroupID 对应Java包结构,在项目中看到的是main目录里java的目录结构。
  • ArtifactID 就是idea中工程的名字
  • version 对应项目的版本号,SNAPSHOT表示快照,即不稳定的意思
<groupId>com.test.gmall</groupId>        表示项目源码中java目录下的包结构
<artifactId>gmall-interface</artifactId> 表示项目的名称
<version>1.0-SNAPSHOT</version>          版本号

② 服务化最佳实践

  • 分包:建议将服务接口、服务模型、服务异常等均放在 API 包中,因为服务模型和异常也是 API 的一部分,这样做也符合分包原则:重用发布等价原则(REP),共同重用原则(CRP)。

  • 服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸。

  • 每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题

2.4 环境搭建

  • 参考上节,下载并部署zookeeper
  • 下载dubbo admin ,作为监视器的可视化界面
    • 启动 zookeeper 的 zkServicer.cmd 服务
    • 进入dubbo下的 dubbo-admin-distribution/target 文件夹,启动cmd
    • 输入 java -jar dubbo-admin-0.3.0-SNAPSHOT.jar
    • 访问 http://localhost:8080

详情参考:dubbo admin

2.5 Demo 搭建

2.5.1 公共接口项目 gmall-interface

将公共的服务接口放在同一个工程gmall-interface下,将这个项目进行打包成jar包,并加入到本地maven仓库中

IDEA在本项目中 打开Terminal中输入 mvn package,将项目打包

将打包的jar包导入本地Maven仓库中,里面的Id分别对应pom.xml中的Id

在要导入的项目中执行,不是在打包的项目中执行。参考:如何在IDEA Maven项目中导入本地jar包的步骤

mvn install:install-file -DgroupId=com.test.gmall -DartifactId=gmall-interface -Dversion=1.0.0 -Dpackaging=jar -Dfile=D:\\MyApp\\JavaProject\\gmall-interface\\target\\gmall-interface-1.0.0.jar
public class UserAddress {
    private Integer id;
    private String userAddress;
    private String userId;
    private String consignee;//收货人
    private String phoneNum;
    private String isDefault;
}

//
public interface OrderService {
    void initOrder(String userId);
}
//服务提供方,获取用户地址
public interface UserService {
    List<UserAddress> getUserAddressList(String userId);
}

2.5.2 服务方:gmall-Provider

服务的提供方是UserService,提供用户的·收货地址

public class UserServiceImpl implements UserService {
    public List<UserAddress> getUserAddressList(String s) {
        UserAddress userAddress1 = new UserAddress(1,"广东省","001111","Dim","12345678"
                                                   ,"null");
        UserAddress userAddress2 = new UserAddress(2,"广西省","001111","Tom","12345612"
                                                   ,"null");
        return Arrays.asList(userAddress1,userAddress2);
    }
}

1、将服务提供者注册到注册中心(暴露服务)

导入dubbo依赖,操作zookeeper的·客户端(curator)

<dependencies>        
    <dependency>            
        <groupId>com.test.gmall</groupId>            
        <artifactId>gmall-interface</artifactId>            
        <version>1.0.0</version>        
    </dependency>        <!--将服务注册到注册中心,需要导入dubbo依赖-->
    <dependency>            
        <groupId>com.alibaba</groupId>            
        <artifactId>dubbo</artifactId>            
        <version>2.6.2</version>        
    </dependency>        <!-- 导入操作注册中心zookeeper的客户端 --> 
    <dependency>            
        <groupId>org.apache.curator</groupId>            
        <artifactId>curator-framework</artifactId>            
        <version>2.12.0</version>        
    </dependency>    
</dependencies>

2、配置服务方,用 Spring 配置声明暴露服务

其实我们不需要注册中心也可以让消费者直接连接服务者(注册中心相当于中介),此时需要制定消费者和服务者之间的通信协议,也就是<dubbo:protocol name="dubbo" port="20880"/>这句话

<?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="gmall-Provider"></dubbo:application><!--    指定注册中心的地址-->    
    <dubbo:registry address="zookeepe://127.0.0.1:2181" protocol="zookeeper" timeout="20000" /><!--使用dubbo协议,将服务暴露在20880端口-->    
    <dubbo:protocol name="dubbo" port="20880"/><!--    指定需要暴露的服务-->    
    <dubbo:service interface="com.test.gmall.service.UserService" ref="userService"/>    
    <bean name="userService" class="com.test.gmall.service.Impl.UserServiceImpl"></bean>
</beans>

3、测试是否已经将服务注册到了zookeeper中

开启zookeeper服务,注意!

zkService.cmd

zkcli.cmd

运行dubbo.admin,打开的cmd不要关闭

新建测试类,运行,此时如果在dubbo.admin中能够看到服务信息,即表示注册服务成功

public class Provider {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Provider.xml");
        context.start();//启动服务
        System.in.read();//阻塞
    }
}

2.5.3 消费者: gmall- Consumer

gmall-Consumer工程中创建OrderService的实现类,里面远程调用到了UserService.getUserList方法

/**
 * @author:liuliping * @date:2021/7/7 11:28
 *///注意此时使用的是dubbo的service注解
@Servicepublic
class OrderServiceImpl implements OrderService {
    @Autowired
    UserService userService;

    public void initOrder(String userId) {
        List<UserAddress> userList = userService.getUserAddressList(userId);
        for (UserAddress user : userList) {
            System.out.println(user.getUserAddress() + user.getUserId());
        }
    }
}

配置消费者从注册中心订阅服务

1、导入依赖

<dependencies>
    <dependency>
        <groupId>com.test.gmall</groupId>
        <artifactId>gmall-interface</artifactId>
        <version>1.0.0</version>
    </dependency>        <!--将服务注册到注册中心,需要导入dubbo依赖-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>dubbo</artifactId>
        <version>2.6.2</version>
    </dependency>        <!-- 导入操作注册中心zookeeper的客户端 -->
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>2.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.25</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

2、配置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"
       xmlns:context="http://www.springframework.org/schema/context"
       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                           http://www.springframework.org/schema/context                  http://www.springframework.org/schema/context/spring-context-4.2.xsd">    
    <!--为了使注解生效,所以需要开启包扫描-->
    <context:component-scan base-package="com.test.gmall.service.Impl"></context:component-scan>
    <!--设置应用名称-->
    <dubbo:application name="gmall-Consumer"></dubbo:application>
    <!-- 指定注册中心地址   -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <!--生成远程服务UserService的代理,可以和本地一样调用service-->
    <!--userService在容器里面,所以可以直接用spring注解注入-->
    <dubbo:reference interface="com.test.gmall.service.UserService" id="userService"></dubbo:reference>
    <bean name="orderService" class="com.test.gmall.service.Impl.OrderServiceImpl"></bean>

3、创建测试类

public class Consumer {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Consumer.xml");
        OrderService orderService = context.getBean(OrderService.class);        //实现输出结果,说明远程调用实现
        orderService.initOrder("1");
        System.out.println("调用完成.....");
        System.in.read();
    }
}

以上是关于Dubbo基础知识+实战Demo的主要内容,如果未能解决你的问题,请参考以下文章

dubbo实战---No provider available for the service

dubbo实战---No provider available for the service

十三:Sentinel整合RestTemplate&openFegin&Dubbo实战

[vscode]--HTML代码片段(基础版,reactvuejquery)

Dubbo + ZK + SpringBoot 分布式实战

Dubbo实战基础学习篇