【微服务-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 网关的相关内容。

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

相关推荐
ai小鬼头5 小时前
Ollama+OpenWeb最新版0.42+0.3.35一键安装教程,轻松搞定AI模型部署
后端·架构·github
萧曵 丶6 小时前
Rust 所有权系统:深入浅出指南
开发语言·后端·rust
老任与码6 小时前
Spring AI Alibaba(1)——基本使用
java·人工智能·后端·springaialibaba
华子w9089258597 小时前
基于 SpringBoot+VueJS 的农产品研究报告管理系统设计与实现
vue.js·spring boot·后端
星辰离彬7 小时前
Java 与 MySQL 性能优化:Java应用中MySQL慢SQL诊断与优化实战
java·后端·sql·mysql·性能优化
GetcharZp8 小时前
彻底告别数据焦虑!这款开源神器 RustDesk,让你自建一个比向日葵、ToDesk 更安全的远程桌面
后端·rust
jack_yin9 小时前
Telegram DeepSeek Bot 管理平台 发布啦!
后端
小码编匠9 小时前
C# 上位机开发怎么学?给自动化工程师的建议
后端·c#·.net
库森学长9 小时前
面试官:发生OOM后,JVM还能运行吗?
jvm·后端·面试
转转技术团队9 小时前
二奢仓店的静默打印代理实现
java·后端