【微服务-Dubbo】Dubbo最佳实践

上一篇我们介绍了微服务架构中的 OpenFeign 技术,在微服务架构中,除了 OpenFeign 技术可以实现服务间的通信外,还有一个重量级选手-Dubbo。这里,我们来看看阿里巴巴自家的 RPC 框架 Dubbo 是如何与 SpringCloudAlibaba 协同工作的。

一、什么是 Dubbo?

Dubbo 是阿里巴巴开源的高性能、轻量级的开源框架,目前被 Apache 收录。

Dubbo 是典型的 RPC 框架的代表,通过客户端/服务端结构实现跨进程应用的高效通信框架。它主要提供了六大核心能力:

  • 面向接口代理的高性能 RPC 调用;
  • 智能容错和负载均衡;
  • 服务自动注册和发现;
  • 高度可扩展能力;
  • 运行期流量调度;
  • 可视化的服务治理与运维。

Dubbo主要特性

具体介绍大家可以在 Dubbo 官网查看,这里就不做过多的搬运。

接下来我们通过实例来看一下 Dubbo 与 Nacos 如何协同作业实现服务间调用。

二、案例背景

在某电商平台的订单业务中,为了保证商品不超卖,我们需要在下单时查询商品库存,如有库存则创建订单,继续支付流程,如果库存为 0,则提示用户库存不足,无法下单。这里我们来定义订单服务(order-service)和仓储服务(warehouse-service)。总体流程如下:

在上述业务中,订单服务是依赖仓储服务的,那仓储服务就是服务提供者订单服务就是服务消费者,梳理清思路后,我们来使用代码还原这个场景。

1、开发仓储服务(Provider)

(1)创建工程、引入依赖

利用 Spring Initializr 向导创建 warehouse-service 工程,确保 pom.xml 引入以下依赖。

xml 复制代码
<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

    <groupId>com.alibaba.cloud</groupId>

    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

</dependency>

<dependency>

    <groupId>org.apache.dubbo</groupId>

    <artifactId>dubbo-spring-boot-starter</artifactId>

    <version>2.7.8</version>

</dependency>

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

与 OpenFeign 不同的是,我们在这里需要引入 dubbo-spring-boot-starter 与 spring-boot-starter-actuator。其中 dubbo-spring-boot-starter 是 Dubbo 与 Spring Boot 整合最关键的组件,为 Spring Boot 提供了 Dubbo 的默认支持。而 spring-boot-starter-actuator 则为微服务提供了监控指标接口,便于监控系统从应用收集各项运行指标。

(2)配置 Dubbo 和 Nacos

在创建好的工程中,打开 application.yml 文件,配置 Nacos 和 Dubbo 通信地址:

yaml 复制代码
server:

  port: 8000 #服务端口

spring:

  application:

    name: warehouse-service #微服务id

  cloud:

    nacos: #nacos注册地址

      discovery:

        server-addr: 106.14.221.171:8848

        username: nacos

        password: nacos

dubbo: #dubbo与nacos的通信配置

  application:

    name: warehouse-dubbo #provider在Nacos中的应用id

  registry: #Provider与Nacos通信地址,与spring.cloud.nacos地址一致

    address: nacos://106.14.221.171:8848

  protocol:

    name: dubbo #通信协议名

    port: 20880 #配置通信端口,默认为20880

  scan:

    base-packages: com.example.warehouseservice.dubbo

这里要注意一下,为什么要配置 Dubbo 的通信端口为 20880 呢?因为 Dubbo 需要依托容器(Container)对外暴露服务,而容器配置和微服务配置是分开的,所以需要额外占用一个网络端口 20880 提供服务。

另外,dubbo.scan.base-packages 代表在 Dubbo 容器启动时自动扫描 com.example.warehouseservice.dubbo 包下的接口与实现类,并将这些接口信息在 Nacos 进行登记,因此 Dubbo 对外暴露的接口必须放在该包下。

(3)创建接口与实现类

kotlin 复制代码
//开发 Stock 库存商品对象
package com.example.warehouseservice.dto;

import java.io.Serializable;

//库存商品对象

public class Stock implements Serializable {

    private Long skuId; //商品品类编号

    private String title; //商品与品类名称

    private Integer quantity; //库存数量

    private String unit; //单位

    private String description; //描述信息

    //带参构造函数

    public Stock(Long skuId, String title, Integer quantity, String unit) {

        this.skuId = skuId;

        this.title = title;

        this.quantity = quantity;

        this.unit = unit;

    }

    //getter 与 setter...

}
kotlin 复制代码
package com.example.warehouseservice.dubbo;

import com.example.warehouseservice.dto.Stock;

//Provider接口

public interface WarehouseService {

    //查询库存

    public Stock getStock(Long skuId);

}
java 复制代码
package com.example.warehouseservice.dubbo.impl;

import com.example.warehouseservice.dto.Stock;

import com.example.warehouseservice.dubbo.WarehouseService;

import org.apache.dubbo.config.annotation.DubboService;

import org.springframework.stereotype.Service;

import java.util.HashMap;

import java.util.Map;

@DubboService

public class WarehouseServiceImpl implements WarehouseService {

    public Stock getStock(Long skuId){

        Map result = new HashMap();

        Stock stock = null;

        if(skuId == 1101l){

            //模拟有库存商品

            stock = new Stock(1101l, "Apple iPhone 15 128GB 紫色", 32, "台");

            stock.setDescription("Apple 15 紫色版对应商品描述");

        }else if(skuId == 1102l){

            //模拟无库存商品

            stock = new Stock(1102l, "Apple iPhone 15 256GB 白色", 0, "台");

            stock.setDescription("Apple 15 白色版对应商品描述");

        }else{

            //演示案例,暂不考虑无对应skuId的情况

        }

        return stock;

    }

}

这里的实现逻辑比较简单,与前一篇文章实现思路基本一致,这里要注意实现类上需要额外增加@DubboService 注解,@DubboService 是 Provider 注解,说明该类所有方法都是服务提供者,加入该注解会自动将类与方法的信息在 Nacos 中注册为 Provider。

(4)启动微服务,验证 Nacos 注册信息

启动微服务后,可以在 Nacos 服务列表中查看注册信息。

此时在服务列表中出现了两个服务注册,warehouse-service 是仓储微服务的注册信息,providers 开头的是 Dubbo 在 Nacos 的注册 Provider 信息。

2、创建订单服务(Consumer )

(1)创建工程并引入依赖

xml 复制代码
<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

    <groupId>com.alibaba.cloud</groupId>

    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

</dependency>

<dependency>

    <groupId>org.apache.dubbo</groupId>

    <artifactId>dubbo-spring-boot-starter</artifactId>

    <version>2.7.8</version>

</dependency>

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

(2)配置 Dubbo 和 Nacos

打开 application.yml 文件,配置 Nacos 与 Dubbo 通信选项,因为 order-service 是消费者,因此不需要专门配置端口与 base-packages 选项。

less 复制代码
spring:

  application:

    name: order-service

  cloud:

    nacos:

      discovery:

        server-addr: 106.14.221.171:8848

        username: nacos

        password: nacos

server:

  port: 9000

dubbo:

  application:

    name: order-service-dubbo

  registry:

    address: nacos://106.14.221.171:8848

(3)开发实现类和接口

这里我们可以把服务提供者的 Stock 类和 WarehouseService 接口复制到本工程中。这里主要是演示方便,在工作中,正常做法是把通用的类和接口打包成公用 jar 放在公共仓库中供各服务调用。

(4)业务逻辑调用实现

kotlin 复制代码
@RestController

public class OrderController {

    @DubboReference

    private WarehouseService warehouseService;

    /**

     * 创建订单业务逻辑

     * @param skuId 商品类别编号

     * @param salesQuantity 销售数量

     * @return

     */

    @GetMapping("/create_order")

    public Map createOrder(Long skuId , Long salesQuantity){

        Map result = new LinkedHashMap();

        //查询商品库存,像调用本地方法一样完成业务逻辑。

        Stock stock = warehouseService.getStock(skuId);

        System.out.println(stock);

        if(salesQuantity <= stock.getQuantity()){

            //创建订单相关代码,此处省略

            //CODE=SUCCESS代表订单创建成功

            result.put("code" , "SUCCESS");

            result.put("skuId", skuId);

            result.put("message", "订单创建成功");

        }else{

            //code=NOT_ENOUGN_STOCK代表库存不足

            result.put("code", "NOT_ENOUGH_STOCK");

            result.put("skuId", skuId);

            result.put("message", "商品库存数量不足");

        }

        return result;

    }

}

业务逻辑和之前是一样的,创建订单前查询库存,如果充足,就返回订单创建成功,如果不足,就返回库存不足。这里需要注意@DubboReference 注解。该注解用在 Consumer 端,说明 WarehouseService 是 Dubbo Consumer 接口,Spring 会自动生成 WarehouseService 接口的代理实现类,并隐藏远程通信细节。

(5)启动微服务,验证 Nacos 注册信息

启动 order-service 微服务,并在注册中心中查看注册信息。

(6)调用验证

当消费者服务注册成功后,我们可以在浏览器中调用接口,来看返回情况:

库存充足情况下

vbnet 复制代码
http://106.14.221.171:9000/create_order?skuId=1101&salesQuantity=10

{

code: "SUCCESS",

skuId: 1101,

message: "订单创建成功"

}

库存不足情况下:

vbnet 复制代码
http://106.14.221.171:9000/create_order?skuId=1102&salesQuantity=1

{

code: "NOT_ENOUGH_STOCK",

skuId: 1102,

message: "商品库存数量不足"

}

至此,下 Dubbo 与 Nacos 如何协同作业实现服务间调用的介绍就结束了。下一篇我们再一起来看微服务 API 网关的相关内容。

欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发

相关推荐
追逐时光者2 小时前
推荐 12 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
Jagger_2 小时前
敏捷开发流程-精简版
前端·后端
苏打水com3 小时前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
间彧4 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧4 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧4 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧4 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧4 小时前
Spring Cloud Gateway详解与应用实战
后端
EnCi Zheng5 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端
烙印6015 小时前
Spring容器的心脏:深度解析refresh()方法(上)
java·后端·spring