如何将Dubbo从Zookeeper平滑地迁移到Nacos?

在这篇文章中 zookeeper命令入门 我们学习了zookeeper的相关命令。如果大家不知道zookeeper的命令,推荐去阅读一下这篇文章,对后面的理解有帮助。

在dubbo官方,推荐使用Zookeeper作为注册中心。然后我们就使用Springboot加Dubbo进行了实战练习 如何使用Dubbo进行优雅的开发? 如果大家不知道怎么Springboot集成Dubbo,推荐大家先去看了这篇文章。

但是现在随着nacos也能集成Dubbo,使用zookeeper的越来越少了,很多公司也在把注册中心往nacos方面迁移。所以我们这次来讲如何使用Nacos集成Dubbo应用。

使用 Nacos 集成 Dubbo的优点:

  1. 更全面的服务治理功能:Nacos 不仅支持服务注册与发现,还提供了动态配置管理、服务健康检查等功能,有助于简化服务治理。
  2. 简单易用:Nacos 的界面友好,易于操作,对于开发者来说更加直观和便捷。
  3. 高可用性:Nacos 支持集群部署模式,能够提供更高的可用性和稳定性。
  4. 兼容多种协议和服务发现机制:除了 Dubbo,Nacos 还支持其他协议(如 gRPC、HTTP 等)和服务发现机制,增加了灵活性。
  5. 用户友好的界面:Zookeeper 自带的界面不够直观,通常需要额外的工具来增强用户体验。Nacos自带有界面

由于在上一篇文章中我们讲了Springboot集成Dubbo,所以如果大家不知道怎么搭建项目环境的可以去查看上一篇文章 如何使用Dubbo进行优雅的开发? ,首先来看下面讲的案例,注册中心是zookeeper。相比于上篇文章,我们改了一下模块架构。仓库地址 dubbo+zookeeper

zookeeper存的是什么样的数据

那么我们在讲如何使用Nacos集成Dubbo应用之前,我们需要看一下原来的zookeeper里面是怎么存Dubbo的服务的,即Dubbo的rpc接口在zk里面长什么样?

我们在没启动Dubbo服务之前来看一下zookeeper 的znode节点里面有什么东西:

可以看到的是根部有个zookeeper节点,里面是没有东西的

启动生产者

那么这个时候我们在本地启动一个Dubbo的生产者服务看看:rpc生产者服务

启动之后我们再去看看zookeeper的znode节点会发生什么变化:

我们可以看到里面的结构长这样:

/dubbo/{service_name}/configurators 路径下的数据通常用于存放针对特定服务的动态配置信息。这些配置可以是:

  1. 权重调整:可以为不同的服务提供者设置不同的权重值,以便控制流量分配。
  2. 路由规则:定义哪些请求应该路由到哪些服务提供者上,可以基于某些条件来过滤服务提供者。
  3. 其他参数调整:比如连接超时时间、重试次数等。

如果我们把这个node删掉,rpc服务会立马报错

ini 复制代码
org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /dubbo/com.masiyi.service.UserService/configurators
	at org.apache.zookeeper.KeeperException.create(KeeperException.java:111) ~[zookeeper-3.4.9.jar:3.4.9-1757313]
	at org.apache.zookeeper.KeeperException.create(KeeperException.java:51) ~[zookeeper-3.4.9.jar:3.4.9-1757313]

而我们的整体znode大概就是这样的:

sql 复制代码
/                        --> 根节点
│
├── dubbo                 --> 存储Dubbo服务信息的根节点
│   └── com.masiyi.service.UserService  --> 特定服务的ZNode
│       ├── configurators  --> 配置提供者
│       │   └── 172.19.48.1    --> 配置提供者的IP地址
│       │
│       └── providers      --> 提供者列表
│           └── dubbo://172.19.48.1:20881/com.masiyi.service.UserService?anyhost=true&application=rpc-server&dubbo=2.6.2&generic=false&interface=com.masiyi.service.UserService&methods=queryUser&pid=26136&side=provider&timestamp=1736649664500
│               └── 172.19.48.1 --> 服务提供者的IP地址

我们可以看到 providers znode(每当有一个服务提供者启动并成功注册到Dubbo注册中心时,相关信息就会被写入到这里。这样,当有服务消费方需要调用该服务时,就可以从这里获取服务提供者的地址和端口等信息。)提供者列表里面存的数据为:

markdown 复制代码
 com.masiyi.service.UserService 
 - Application: rpc-server       
 - Interface: com.masiyi.service.UserService                  
 - Methods: queryUser            
 - Protocol: dubbo              
 - Host: 172.19.48.1            
 - Port: 20881                  
 - Side: provider                
 - Timestamp: 1736649664500      

而我们在生产者的代码里面 添加一个方法 queryUser2 就会变成这样:

ini 复制代码
dubbo://172.19.48.1:20881/com.masiyi.service.UserService?anyhost=true&application=rpc-server&dubbo=2.6.2&generic=false&interface=com.masiyi.service.UserService&methods=queryUser2,queryUser&pid=32500&side=provider&timestamp=1736651113593

最后的ZNode里面的数据如下:

那么生产者这里我们可以看出什么东西来呢?每一个service都会在 /dubbo这个下面有一个对应的znode,这个znode命名就是这个类的全限定类名,在这个znode下面的 providersznode,里面存着生产者的具体信息。

那么有了这个我们就可以通过接口调用工具去调用这个rpc接口进行调试了,例如这样:

启动消费者

我们可以单独启动我们的消费者看一下,zookeeper里面的数据会发生变化吗?web消费者服务

可以看到,这次相对于单启动生产者多了个consumersrouters节点,而这个consumers节点则记录着被 @Reference注解修饰着的生产者类:(check = false 属性可以让消费者先启动的情况下不报错)每个Dubbo服务消费者在启动时会在此目录下创建一个节点,以表示它对服务的关注和使用情况。

java 复制代码
package com.masiyi.controller;


import com.alibaba.dubbo.config.annotation.Reference;
import com.masiyi.entity.Role;
import com.masiyi.entity.User;
import com.masiyi.service.RoleService;
import com.masiyi.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    private UserService userService;
    @Reference(check = false)
    private RoleService roleService;


    @GetMapping("/user")
    public User user() {
        User user = userService.queryUser();
        Role role = roleService.role();
        user.setRole(role);
        return user;
    }
}

同时启动

我们把生产者和消费者一起启动起来看看里面的znode会变成什么样

这四个子znode共同构成了Apache Dubbo框架中服务治理的基础架构:

  • Configurators用于动态配置管理,使得无需重启即可调整服务行为。
  • Consumers追踪所有服务消费者的信息,有助于服务调用关系的管理和监控。
  • Providers记录所有服务提供者的详情,是服务发现机制的核心部分。
  • Routers通过设定路由规则来控制请求的流向,支持复杂的服务治理需求

而我们看到Consumers 和 Providers 里面都有着对应的数据,也就是说现在这个类 com.masiyi.service.RoleService既有消费者又有生产者。那么我自然就可以进行正常的调用了:

ohhh,现在终于知道了zookeeper里面的数据长什么样了。那么我现在开始说正事:Dubbo集成Nacos。

Dubbo集成Nacos

前面我们说了那么多好像都没有什么关系,可能有小伙伴疑惑了。别急,我们先来看一个事情,小学二年级我们学过,一般我们都是用feign去将一个服务注册为一个生产者,如果我们的系统有很多的模块,每个模块对应一个微服务,那么我们在nacos上面则会看到这样的服务列表:

即每个微服务(可以理解为一个运行的jar包)都是对应一条数据,但是Dubbo不一样,就拿我们上面的例子,每个生产者提供的类就是一个对应的服务,很好理解,就是zookeeper里面对应的每个节点,就像这样:

Nacos 版本映射关系

Dubbo 推荐 Nacos 版本 Nacos 兼容范围
3.3.0 2.3.0 2.x
3.2.21 2.1.0 2.x
3.1.11 2.0.9 2.x
3.0.10 2.0.9 2.x
2.7.21 1.x最新版本 1.x
2.6.0 1.x最新版本 1.x

作者演示的环境nacos版本是2.2.0,Dubbo版本是3.3.0,JDK是1.8

搭建环境

第一步,搭建最少3个maven模块

作者这边搭建了4个maven模块,他们之间的关系是这样的

注册中心的主要职责是服务的注册与发现,它提供了一个中心化的视图,使得服务消费者能够找到并连接到适当的服务提供者。

  • 服务注册:当一个服务启动时,它会将自己的地址(如IP地址和端口号)、版本号等信息注册到注册中心。
  • 服务发现:服务消费者在需要调用其他服务时,会向注册中心查询所需服务的地址列表。基于某种策略(例如随机、轮询或根据负载情况),消费者选择一个合适的服务提供者进行直接通信。
  • 健康检查:注册中心还负责对已注册的服务进行健康检查。如果某个服务实例不可用了,注册中心会更新其状态,从而避免将请求路由到不健康的实例上。

第二步,编写pom文件

web-server

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>



    <groupId>com.masiyi</groupId>
    <artifactId>web-server</artifactId>
    <version>1.0-SNAPSHOT</version>


    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>

        <dependency>
            <groupId>com.masiyi</groupId>
            <artifactId>service</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-nacos-spring-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>

</project>

server

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>


    <groupId>com.masiyi</groupId>
    <artifactId>service</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.12</version>
        </dependency>

        <dependency>
            <groupId>com.masiyi</groupId>
            <artifactId>api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

rpc-server

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>



    <groupId>com.masiyi</groupId>
    <artifactId>rpc-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-nacos-spring-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.masiyi</groupId>
            <artifactId>api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- Spring Boot Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

    </dependencies>


</project>

api

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.masiyi</groupId>
    <artifactId>api</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

第三步,编写生产者和消费者

具体的代码请查看代码仓库 : dubbo-nacos

注意这里使用@EnableDubboConfig,@DubboComponentScan注解代替了老版本的 EnableDubbo,使用 @DubboService代替了老版本的 Service, @DubboReference代替了 Reference

第四步,编写配置文件

生产者(rpc-server)

properties 复制代码
spring.application.name=rpc-server

server.port=8082

dubbo.application.name=rpc-server
dubbo.registry.address=nacos://localhost:8848

dubbo.protocol.name=dubbo
dubbo.protocol.port=20881

消费者(web-server)

properties 复制代码
spring.application.name=web-server
server.port=8083

dubbo.application.name=web-server
dubbo.registry.address=nacos://localhost:8848

dubbo.protocol.port=20885

第五步,启动消费者和生产者

启动完成之后就可以在nacos看到这些消息了

上面的基本页面是没问题的,我们再来看一下每个服务具体的详情页面:

可以看到,这里面的详情信息就是之前我们在zookeeper里面providers znode看到的消息是一样的。那么我们也可以调用一下接口看看效果也是没问题的:

至此,我们的Dubbo集成Nacos就完成了,随着我们将Dubbo成功集成到Nacos中,这一过程不仅标志着技术上的里程碑,也象征着Dubbo"回归娘家"的历程。Dubbo最初由阿里巴巴开源,并于2011年作为一款高性能的Java RPC框架公开发布。它迅速成为了构建分布式服务架构的首选之一。

在2017年,Dubbo被捐赠给Apache软件基金会(Apache Software Foundation),并在2019年正式毕业成为Apache顶级项目(Top-Level Project)。这一过程不仅是对Dubbo技术实力的认可,更是对其社区活跃度和开放性的一种肯定。在Apache基金会的支持下,Dubbo得到了全球范围内更多的开发者和企业的关注与贡献,进一步推动了其发展和创新。

尽管Dubbo曾一度淡出公众视野,但它始终保持着强劲的生命力,这得益于其开放源代码的本质和活跃的社区支持。阿里巴巴重新重视并投入资源继续发展Dubbo,将其视为其云原生战略的关键部分,进一步巩固了Dubbo在现代分布式系统中的地位。

谢谢大家观看。

相关推荐
郑道30 分钟前
Docker 在 macOS 下的安装与 Gitea 部署经验总结
后端
3Katrina31 分钟前
妈妈再也不用担心我的课设了---Vibe Coding帮你实现期末课设!
前端·后端·设计
汪子熙32 分钟前
HSQLDB 数据库锁获取失败深度解析
数据库·后端
高松燈1 小时前
若伊项目学习 后端分页源码分析
后端·架构
没逻辑1 小时前
主流消息队列模型与选型对比(RabbitMQ / Kafka / RocketMQ)
后端·消息队列
倚栏听风雨2 小时前
SwingUtilities.invokeLater 详解
后端
Java中文社群2 小时前
AI实战:一键生成数字人视频!
java·人工智能·后端
王中阳Go2 小时前
从超市收银到航空调度:贪心算法如何破解生活中的最优决策谜题?
java·后端·算法
shepherd1112 小时前
谈谈TransmittableThreadLocal实现原理和在日志收集记录系统上下文实战应用
java·后端·开源
关山月3 小时前
使用 Ollama 和 Next.js 构建 AI 助手
后端