微服务核心理论 - 通信之RPC实践篇

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

1 说明

上一节我们我们详细学习了RPC的概念和原理,以及它能够提供的能力。也对目前业内主流的RPC的框架有了一定的了解。接下来以Dobbo为例子,来学习下怎么使用RPC框架来进行服务之间的通信。

2 Dubbo框架功能介绍

Apache Dubbo 是一款分布式微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。

2.1 服务发现

服务发现,即消费端自动发现服务地址列表的能力,是微服务框架需要具备的关键能力,借助于自动化的服务发现,微服务之间可以在无需感知对端部署位置与 IP 地址的情况下实现通信。

实现服务发现的方式有很多种,Dubbo 提供的是一种 Client-Based 的服务发现机制,通常还需要部署额外的第三方注册中心组件来协调服务发现过程,如常用的 Nacos、Consul、Zookeeper 等,Dubbo 自身也提供了对多种注册中心组件的对接,用户可以灵活选择。

Dubbo 基于消费端的自动服务发现能力,其基本工作原理如下图:

![202404072035323471.png][]

2.2 RPC通信

Dubbo3 提供了 Triple(Dubbo3)、Dubbo2 协议,这是 Dubbo 框架的原生协议。除此之外,Dubbo3 也对众多第三方协议进行了集成,并将它们纳入 Dubbo 的编程与服务治理体系, 包括 gRPC、Thrift、JsonRPC、Hessian2、REST 等。

2.3 服务流量管理

通过 Dubbo 定义的路由规则,实现对流量分布的控制,可以实现 A/B测试、金丝雀发布、蓝绿发布等能力。

2.4 配置管理

描述 Dubbo 支持的配置,Dubbo 的动态配置能力,包含几大类: 启动阶段配置项、服务治理规则、动态配置项。

2.5 部署架构(注册、配置、元数据中心)

作为一个微服务框架,Dubbo sdk 跟随着微服务组件被部署在分布式集群各个位置,为了在分布式环境下实现各个微服务组件间的协作, Dubbo 定义了一些中心化组件,这包括:

  • 注册中心。协调 Consumer 与 Provider 之间的地址注册与发现

  • 配置中心。

    • 存储 Dubbo 启动阶段的全局配置,保证配置的跨环境共享与全局一致性
    • 负责服务治理规则(路由规则、动态配置等)的存储与推送。
  • 元数据中心。

    • 接收 Provider 上报的服务接口元数据,为 Admin 等控制台提供运维能力(如服务测试、接口文档等)

    • 作为服务发现机制的补充,提供额外的接口/方法级别配置信息的同步能力,相当于注册中心的额外扩展。

      ![202404072035326502.png][]

2.6 高可扩展性

Dubbo 通过 SPI 机制提供了非常灵活的可扩展性

3 Dubbo实现简易的RPC通信

3.1 框架依赖

  • Protocol, Proxy, Service, Container, Registry, Monitor 代表层或模块,蓝色的表示与业务的交互,绿色表示 Dubbo 内部的交互。
  • 主模块 RPC Consumer,RPC Provider, Registry, Monitor 代表部署逻辑拓扑节点。
  • 蓝色虚线为init初始化时调用,红色虚线为async运行时异步调用,红色实线为sync运行时同步调用。
  • Remoting 过程整体都隐含在 Protocol 中。

3.2 核心角色说明

  • Provider 暴露服务的服务提供方
  • Consumer 调用远程服务的服务消费方(负载均衡)
  • Registry 服务注册与发现的注册中心(监控、心跳、踢出、重入)
  • Monitor 服务消费者和提供者在内存中累计调用次数和调用时间,主动定时每分钟发送一次统计数据到监控中心。
  • Service 服务:执行远程调用、数据序列化

3.3 在 SpringBoot 中实践

3.3.1 父项目中引入依赖

首先引入 Zookeeper 和 Dubbo的依赖,Zookeeper的作用是注册与发现,Dubbo的作用是引入RPC通信核心能力

            <!-- 包含了Zookeeper和Dubbo依赖 -->
            <dependency>
                <groupId>com.alibaba.boot</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>0.2.0</version>
            </dependency>

3.3.2 创建module

父项目下面创建三个Module,一个通用模块,一个服务提供者模块,一个服务消费者模块

  • RpcCommon

  • RpcProvider

  • RpcConsume

    ![202404072035332344.png][]

3.3.3 通用库 RpcCommon

提供了公用的实体和接口,比如这边包含了一个用户信息的实体类和用户信息接口,后面的服务提供者和消费这都可以引用:

    /**
     * @author brand
     * @Description: 用户信息实体
     * @Copyright: Copyright (c) 2022
     * @Company: Helenlyn, Inc. All Rights Reserved.
     * @date 2022/3/5 下午3:59
     * @Update Time:
     * @Updater:
     * @Update Comments:
     */
    @Getter
    @Setter
    public class UserInfo implements Serializable {
        private Integer userId;
        private String userName;
        private Integer age;
        private String sex;
    }
/**
 * @author brand
 * @Description: 用户信息接口
 * @Copyright: Copyright (c) 2022
 * @Company: Helenlyn, Inc. All Rights Reserved.
 * @date 2022/3/5 下午5:29
 * @Update Time:
 * @Updater:
 * @Update Comments:
 */
public interface UserInfoService {
    UserInfo getUserInfo (int userId) ;
    String getHello (int userId) ;
}

### 3.3.3 服务提供者 RpcProvider ###

yml文件中的配置信息如下,可以看到我配置的zookeeper地址是127.0.0.1:2181,这是我本地部署到Zookeeper服务,大家可以对应修改下。  
scan属性指的是暴露服务的位置,对应位置下的类,只要声明 Service 注解,就会被扫描并暴露出去。
# Dubbo Provider 配置
dubbo:
  application:
    name: rpc-provider # 发布的dubbo服务名称
  registry:
    address: 127.0.0.1:2181  # 使用Zookeeper注册中心提供的服务地址,这边可以配置多个,逗号隔开
    protocol: zookeeper
  protocol:
    name: dubbo
    port: 20882  # 用dubbo协议在20882端口暴露服务
  scan: # 使用注解方式暴露接口,扫描的位置
    base-packages: rpcprovider.modules.service

然后编写需要暴露出去的接口的实现,这边需要注意类上引入的注解为com.alibaba.dubbo.config.annotation的Service,  
而不是springframework包中的service,这样Service服务才能被注册到dubbo中:
package rpcprovider.modules.service;

import com.alibaba.dubbo.config.annotation.Service;
import rpccommon.dto.UserInfo;
import rpccommon.service.UserInfoService;

/**
 * @author brand
 * @Description:
 * @Copyright: Copyright (c) 2022
 * @Company: Helenlyn, Inc. All Rights Reserved.
 * @date 2022/3/5 下午5:43
 * @Update Time:
 * @Updater:
 * @Update Comments:
 */
@Service
public class UserInfoServiceImpl implements UserInfoService {
    @Override
    public UserInfo getUserInfo(int userId) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(userId);
        userInfo.setAge(18);
        userInfo.setSex("男");
        userInfo.setUserName("Brand");
        return userInfo;
    }

    @Override
    public String getHello(int userId) {
        return " Hello, " + userId;
    }
}

启动起来看看Zookeeper的效果,是不是被注册到Dubbo中,这边可以用看到,Provider中已经有注册节点了。
[zk: 127.0.0.1:2181(CONNECTED) 68] ls /
[dubbo, zookeeper]
[zk: 127.0.0.1:2181(CONNECTED) 69] ls /dubbo
[rpccommon.service.UserInfoService]
[zk: 127.0.0.1:2181(CONNECTED) 70] ls /dubbo/rpccommon.service.UserInfoService
[configurators, consumers, providers, routers]
[zk: 127.0.0.1:2181(CONNECTED) 71] ls /dubbo/rpccommon.service.UserInfoService/providers
[dubbo%3A%2F%2F172.21.213.159%3A20882%2Frpccommon.service.UserInfoService%3Fanyhost%3Dtrue%26application%3Drpc-provider%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Drpccommon.service.UserInfoService%26methods%3DgetHello%2CgetUserInfo%26pid%3D60138%26side%3Dprovider%26timestamp%3D1646535565910]
[zk: 127.0.0.1:2181(CONNECTED) 72]

### 3.3.4 消费者 RpcConsume ###

yml文件中的配置信息如下:
# Dubbo Consumer 配置文件
dubbo:
  application:
    name: rpc-consumer
  registry:
    address: 127.0.0.1:2181 # 使用Zookeeper注册中心提供的服务地址来获取服务,这边可以多个逗号隔开
    protocol: zookeeper

消费者使用注解方式进行RPC调用,这边注意,通过@Reference注解, dubbo会在扫描的时候自动帮我们代理接口,然后通过rpc调用远程服务。如下:
package rpcconsume.modules.service;

import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
import rpccommon.dto.UserInfo;
import rpccommon.service.UserInfoService;

/**
 * @author brand
 * @Description: 消费者使用注解方式进行RPC调用
 * @Copyright: Copyright (c) 2022
 * @Company: Helenlyn, Inc. All Rights Reserved.
 * @date 2022/3/5 下午6:49
 * @Update Time:
 * @Updater:
 * @Update Comments:
 */
@Service
public class UserInfoConsumer implements UserInfoService {
    @Reference
    private UserInfoService userInfoService ;

    @Override
    public UserInfo getUserInfo(int userId) {
        return userInfoService.getUserInfo(userId);
    }

    @Override
    public String getHello(int userId) {
        return userInfoService.getHello(userId);
    }
}

写一个Controller,通过外部调用,来测试效果
package rpcconsume.modules.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import rpccommon.dto.UserInfo;
import rpcconsume.modules.service.UserInfoConsumer;

/**
 * @author brand
 * @Description: 测试消费者效果
 * @Copyright: Copyright (c) 2022
 * @Company: Helenlyn, Inc. All Rights Reserved.
 * @date 2022/3/5 下午6:48
 * @Update Time:
 * @Updater:
 * @Update Comments:
 */
@RestController
@Slf4j
@RequestMapping("/v1.0/consumer")
public class ConsumerController {
    @Autowired
    private UserInfoConsumer userInfoConsumer ;

    /**
     * 获取用户信息
     * @return
     */
    @RequestMapping(value = "/userinfo/{user_id}", method = RequestMethod.GET)
    public UserInfo getUserInfo(@PathVariable("user_id") int userId) {
        return userInfoConsumer.getUserInfo(userId);
    }


    /**
     * 获取问候信息
     * @return
     */
    @RequestMapping(value = "/hello/{user_id}", method = RequestMethod.GET)
    public String getHello(@PathVariable("user_id") int userId) {
        return userInfoConsumer.getHello(userId);
    }
}

查一下Zookeeper 上的消费者信息:
[zk: 127.0.0.1:2181(CONNECTED) 73] ls /dubbo/rpccommon.service.UserInfoService/consumers
[consumer%3A%2F%2F172.21.213.159%2Frpccommon.service.UserInfoService%3Fapplication%3Drpc-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.6.2%26interface%3Drpccommon.service.UserInfoService%26methods%3DgetHello%2CgetUserInfo%26pid%3D60884%26side%3Dconsumer%26timestamp%3D1646536367371]

  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值