当前位置: 首页 > news >正文

Spring Cloud集成Dubbo实现RPC调用

RPC是什么

RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。

说通俗一点,就是RPC可以让我们调用远程方法像本地方法一样简单,客户端并不会感知到网络通信等等细节

RPC框架的通信与具体的协议无关,RPC可基于HTTP或者TCP协议,现在市面上比较成熟的Java生态中的RPC实现一般是这两种:

  1. 基于HTTP的OpenFeign

Netflix Feign 是 Netflix 公司发布的一种实现负载均衡和服务调用的开源组件。Spring Cloud 将其与 Netflix 中的其他开源服务组件(例如 Eureka、Ribbon 以及 Hystrix 等)一起整合进 Spring Cloud Netflix 模块中,整合后全称为 Spring Cloud Netflix Feign。

Feign 是一种声明式服务调用组件,它在 RestTemplate 的基础上做了进一步的封装。通过 Feign,我们只需要声明一个接口并通过注解进行简单的配置(类似于 Dao 接口上面的 Mapper 注解一样)即可实现对 HTTP 接口的绑定。

但是Feign已经停更了

OpenFeign 全称 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用与负载均衡组件,它的出现就是为了替代进入停更维护状态的 Feign

OpenFeign 是 Spring Cloud 对 Feign 的二次封装,它具有 Feign 的所有功能,并在 Feign 的基础上增加了对 Spring MVC 注解的支持,例如 @RequestMapping、@GetMapping 和 @PostMapping 等

  1. 基于TCP自定义协议的Dubbo

我们今天的主角就是它,接下来我们来重点介绍它吧

Dubbo简介

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

Dubbo 比起OpenFegin 有自身的优势:

  1. 支持多种传输协议
  2. 支持更多的负载均衡算法
  3. 支持多种容错策略
  4. 开箱即用
  5. 高度可扩展
  6. 面向超大规模微服务集群设计

在分布式系统中,尤其是随着微服务架构的发展,应用的部署、发布、扩缩容变得极为频繁,作为 RPC 消费方,如何动态的发现服务提供方地址成为 RPC 通信的前置条件。Dubbo 提供了自动的地址发现机制,用于应对分布式场景下机器实例动态迁移的问题。如下图所示,通过引入注册中心来协调提供方与消费方的地址,提供者启动之后向注册中心注册自身地址,消费方通过拉取或订阅注册中心特定节点,动态的感知提供方地址列表的变化。

Dubbo3已经开始拥抱HTTP2,解决了Dubbo之前的互通性问题,以下内容源于官网:

Triple 是 Dubbo3 提出的基于 HTTP2 的开放协议,旨在解决 Dubbo2 私有协议带来的互通性问题。相比于原有 Dubbo2 协议,Triple 有以下优势:

  1. 原生和 gRPC 协议互通。打通 gRPC 生态,降低从 gRPC 至 Dubbo 的迁移成本。
  2. 增强多语言生态。避免因 CPP/C#/RUST 等语言的 Dubbo SDK 能力不足导致业务难以选型适配的问题。
  3. 网关友好。网关无需参与序列化,方便用户从传统的 HTTP 转泛化 Dubbo 调用网关升级至开源或云厂商的 Ingress 方案。
  4. 完善的异步和流式支持。带来从底层协议到上层业务的性能提升,易于构建全链路异步以及严格保证消息顺序的流式服务。

目前 Java 和 Go 的 Dubbo SDK 已全面支持 Triple 协议。 在阿里巴巴,Triple 协议广泛用于跨环境、跨语言、跨生态互通,已有数十万容器生产级使用。

快速开始

接下来我们来对之前的项目进行改造

准备服务提供者

  1. 我们先在父pom里面添加依赖,管理版本
        <dubbo.version>2.7.8</dubbo.version>

            <!--dubbo-->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>${dubbo.version}</version>
            </dependency>
复制代码
  1. 在服务提供者user-service 里面添加依赖
        <!--降低版本,详见dubbo issues#7274,否则启动类型转换异常-->
        <dependency>
            <groupId>com.alibaba.spring</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>1.0.10</version>
        </dependency>
         <!--dubbo的依赖-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
         <!--这里遇到一个bug,调用dubbo接口会报java.lang.NoClassDefFoundError-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.2.4.Final</version>
        </dependency>
复制代码
  1. 准备Dubbo服务

先创建实体类

package cuit.epoch.pymjl.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/27 0:35
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    @TableId(type = IdType.AUTO)
    private Long id;
    private String username;
    private String password;
    private String email;
    private String phone;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

复制代码

再创建接口

package cuit.epoch.pymjl.service;

import com.baomidou.mybatisplus.extension.service.IService;
import cuit.epoch.pymjl.entity.User;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/27 0:56
 **/
public interface UserService extends IService<User> {
    /**
     * 注册
     */
    void register();

    /**
     * 得到用户
     *
     * @param id id
     * @return {@code User}
     */
    User get(Long id);
}

复制代码

注意,dubbo用到的实体类以及接口需要写在通用模块common-utils 里面方便消费者调用,而真正的接口实现类是放在服务提供者的包里面的

实际的开发中,Dubbo服务一般分为client和core两个模块,client存放dubbo所用到的实体类以及接口,core存放接口的实现类(真正的业务处理),随后服务提供者需要将client打包让消费者引入,向其暴露接口

注意:所有传输的类都需要序列化(实现序列化接口)

然后编写对应的实现类

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/27 0:50
 **/
@DubboService
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);

    @Override
    public void register() {
        User user = new User();
        user.setUsername("pymjl@" + ATOMIC_INTEGER.incrementAndGet());
        user.setPassword("123456");
        user.setPhone("1231234214124");
        user.setEmail("pymjl@" + ATOMIC_INTEGER.incrementAndGet() + ".com");
        baseMapper.insert(user);
    }

    @Override
    public User get(Long id) {
        return baseMapper.selectById(id);
    }
}
复制代码

一定记得要给主启动类加上@EnableDubbo 注解

最后,编写application.yml

dubbo:
  application:
    name: ${spring.application.name}
    logger: slf4j
  protocol:
    name: dubbo
    port: -1
  registry:
    address: nacos://192.168.199.128:8848
  config-center:
    address: nacos://192.168.199.128:8848
  metadata-report:
    address: nacos://192.168.199.128:8848
  consumer:
    validation: true
    timeout: 3000
    check: false
  scan:
    base-packages: cuit.epoch.pymjl.dubbo
复制代码

准备消费者

一样的,先在消费者的pom里面导入上面的依赖,然后在主启动类上加上@EnableDubbo 注解

  1. 编写application.yml
dubbo:
  application:
    name: ${spring.application.name}
    logger: slf4j
  protocol:
    name: dubbo
    port: -1
  registry:
    address: nacos://192.168.199.128:8848
  config-center:
    address: nacos://192.168.199.128:8848
  metadata-report:
    address: nacos://192.168.199.128:8848
  consumer:
    validation: true
    timeout: 3000
    check: false
  scan:
    base-packages: cuit.epoch.pymjl.controller
复制代码
  1. 编写controller
package cuit.epoch.pymjl.controller;

import cuit.epoch.pymjl.entity.User;
import cuit.epoch.pymjl.result.CommonResult;
import cuit.epoch.pymjl.result.ResultUtils;
import cuit.epoch.pymjl.service.UserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/26 12:17
 **/
@RestController
@RequestMapping("/consumer")
public class TestController {
    private static final String SERVICE_URL = "http://UserService";

    @Resource
    RestTemplate restTemplate;

    @DubboReference(check = false)
    UserService userService;

    @GetMapping("/test")
    public CommonResult consumerTest() {
        return restTemplate.getForObject(SERVICE_URL + "/user/test", CommonResult.class);
    }

    @GetMapping("/register")
    public CommonResult<String> register() {
        userService.register();
        return ResultUtils.success();
    }

    @GetMapping("/get/{id}")
    public CommonResult<User> get(@PathVariable("id") Long id) {
        return ResultUtils.success(userService.get(id));
    }
}

复制代码

开始测试

  1. 启动服务提供者

  1. 启动消费者

  1. 观察Nacos 控制台

  1. 访问测试

观察后台日志输出

至此,Spring Cloud集成Nacos、Dubbo就演示完毕了

项目源码:gitee github

相关文章:

  • 怎么开发自己的NFT平台
  • Android Context
  • 架构师的 36 项修炼第10讲:架构实战案例分析
  • 力扣每日一题-第63天-867. 转置矩阵
  • java基于ssm+vue的考研信息查询系统 elementui
  • 北大肖臻老师《区块链技术与应用》系列课程学习笔记[29]总结
  • C++设计模式之工厂模式(创建型模式)
  • 姿态分析开源工具箱MMPose使用示例:2d手势估计
  • MySQL分页查询
  • 第22篇 基础 (二十二)手把手教学 Qt 操作 ini 文件(详解)
  • Linux CentOS 8(用户管理)
  • 算法---找出数组中的所有孤独数字(Kotlin)
  • 【Git命令】git commit --amend
  • 高级js 面向对象 和面向过程 三种函数
  • Linux常用命令:htop(交互式进程查看器)【后台运行及查看状态命令】【top命令的升级版】
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • bootstrap创建登录注册页面
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • JavaScript函数式编程(一)
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • python 学习笔记 - Queue Pipes,进程间通讯
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 计算机在识别图像时“看到”了什么?
  • 记一次和乔布斯合作最难忘的经历
  • 将回调地狱按在地上摩擦的Promise
  • 每天一个设计模式之命令模式
  • 删除表内多余的重复数据
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 突破自己的技术思维
  • 小程序 setData 学问多
  • 小程序开发中的那些坑
  • 用Python写一份独特的元宵节祝福
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 积累各种好的链接
  • (¥1011)-(一千零一拾一元整)输出
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (173)FPGA约束:单周期时序分析或默认时序分析
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)ssm码农论坛 毕业设计 231126
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (十三)Maven插件解析运行机制
  • (转)菜鸟学数据库(三)——存储过程
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • * CIL library *(* CIL module *) : error LNK2005: _DllMain@12 already defined in mfcs120u.lib(dllmodu
  • . Flume面试题
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • [ C++ ] STL---string类的模拟实现