Nacos注册、配置中心整合Dubbo远程调用
Nacos注册、配置中心整合Dubbo远程调用(文末含demo代码地址)
- 背景
- 开发环境
- 搭建父项目
-
- 创建项目
- 声明依赖
- 建立子模块
-
- 创建order子模块
- 抽取公共的代码结构
- 编写order模块中的Dubbo相关代码
-
- 编写common模块中OrderService的实现类
- 编写配置文件
- 启动Nacos
- 进入Nacos管理界面创建配置文件
- 将order模块注册到Naocs
- 编写服务消费者模块
-
- 调用order服务的接口
- 编写配置文件
- 启动main模块,将其注册到Nacos中
- 测试
- 源码地址
背景
Nacos作为阿里巴巴技术栈中的一员,目前在应用得也是比较广泛。作为一个后起之秀,它相较于老一辈得Eureka注册中心也有很多优势。Dubbo同样也是阿里巴巴技术栈的成员,那么它和Nacos相结合无疑是非常不错的选择。最近看了很多帖子,要么是很老的,要么就是参考价值不大,索性,我自己写一个,Nacos和Dubbo整合使用的坑还是比较多。
开发环境
类别 | 名称及版本 |
---|---|
开发工具 | IntelliJ IDEA 2021.2.3 |
MAVEN | Apache Maven 3.8.1 |
JAVA | JDK1.8.291 |
SpringBoot | 2.4.2 |
SpringCloudAlibaba | 2021.1 |
MyBatis | 2.1.4 |
Dubbo | 2.7.8 |
Nacos | 2.0.3 |
请注意,以上版本(SpringBoot、SpringCloudAlibaba 、Dubbo)对应关系完全按照官方推荐来搭配的。版本差异或许会导致其他问题,但如果您有其他需求,请您查看官方文档以获取相关信息
版本参考地址
搭建父项目
通常来讲,我们会使用聚合的方式来统一项目中的依赖,直接在父项目中声明好依赖,并管理好版本,在子项目中我们就不再需要写版本号了,方便统一。
创建项目
我们的父项目事实上只有一个功能,那就是帮我们管理依赖版本,所以我们可以删除其不要的结构,让他看起来干净些。项目结构如下(项目名我只是随便写的一个):
声明依赖
创建好父项目之后,我们接下来把需要用到的各种依赖申明在父项目的POM的中dependencyManagement
标签中,以方便我们管理其版本,使其版本统一。这里您需要注意以下packaging
标签我们要声明为pom
,因为我们这个只是作为父项目出现。如果您非常熟悉SpringBoot项目,那么您肯定知道spring-boot-maven-plugin
插件,一般的SpringBoot项目导入spring-boot-maven-plugin插件的方式一般是这样的:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build>
但是现在我们不能这么声明了,不然会报错,其他相关信息您可以参考官方文档。您应该以如下方式申明(版本我声明在properties中作为一个变量出现的,和SpringBoot的版本号一致):
<build> <plugins><plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot-dependencies.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions></plugin> </plugins></build>
完整的POM文件代码如下:
<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> <packaging>pom</packaging> <groupId>com.fenzhichuanmei.sunflower</groupId> <artifactId>sunflower</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <mybatis.spring-boot.version>2.1.4</mybatis.spring-boot.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot-dependencies.version>2.4.2</spring-boot-dependencies.version> <dubbo-spring-boot-starter.version>2.7.8</dubbo-spring-boot-starter.version> <spring-cloud-alibaba-dependencies.version>2021.1</spring-cloud-alibaba-dependencies.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot-dependencies.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba-dependencies.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring-boot.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo-spring-boot-starter.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot-dependencies.version}</version> <executions> <execution> <goals><goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build></project>
我这里没有通过继承的方式导入SpringBoot依赖,而是在dependencyManagement标签中声明,所有用到的依赖的版本号我在上方的properties用变量方式声明以方便管理。
建立子模块
创建order子模块
我创建了一个名为order的子模块继承sunflower项目,项目结构如下:
order子模块POM文件内容如下
<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"> <parent> <artifactId>sunflower</artifactId> <groupId>com.fenzhichuanmei.sunflower</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>order</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>/*.yaml</include> </includes> </resource> </resources> </build></project>
因为我需要用到mybatis,且我将mybatis的mapper.xml文件放在了类路径中和接口同包,所以我在POM中配置了将类路径下的.xml文件导出,您可以根据您自己的情况具体问题具体分析。
抽取公共的代码结构
Dubbo官方推荐我们将接口、异常等抽取到一个模块中,然后其他模块通过导入的形式引用公共的结构。所以我根据官方的推荐,建立了一个common模块,其结构如下:
这里您需要注意,如果您的实体类要在Dubbo调用的过程中传递,那么您需要将这个实体类实现java.io.Serializable
接口,否则会报错。举个例子:
package com.fenzhichuanmei.sunflower.common.order.pojo;import com.fenzhichuanmei.sunflower.common.pojo.BasePojo;import lombok.Data;import java.io.Serializable;/ * @Author: DaiYi * @Email: 484201132@qq.com * @CreateTime: 2021-11-09 9:48 * @Description: an entity class that encapsulates order information */@Datapublic class Order extends BasePojo implements Serializable { private String goodsId; private String orderNumber; private String phoneNumber; private String recipient; private String province; private String city; private String county; private String town; private String detail;}
我在公共模块中抽取出了OrderService接口,并声明了两个方法:
package com.fenzhichuanmei.sunflower.common.order.service;import com.fenzhichuanmei.sunflower.common.order.pojo.Order;import com.fenzhichuanmei.sunflower.common.response.ResponseBody;/ * @Author: DaiYi * @Email: 484201132@qq.com * @CreateTime: 2021-11-09 10:20 * @Description: */public interface OrderService { ResponseBody queryOrders(Order order); ResponseBody queryOrderProperties();}
编写order模块中的Dubbo相关代码
编写common模块中OrderService的实现类
我们刚刚把OrderService抽取到公共模块common中,但是其实现类我们应该在具体的模块中编写,代码如下(其他代码请下载源码参考):
package com.fenzhichuanmei.sunflower.order.service.impl;import com.fenzhichuanmei.sunflower.common.order.pojo.Order;import com.fenzhichuanmei.sunflower.common.order.service.OrderService;import com.fenzhichuanmei.sunflower.common.response.ResponseBody;import com.fenzhichuanmei.sunflower.common.response.StatusCode;import com.fenzhichuanmei.sunflower.order.config.OrderProperties;import com.fenzhichuanmei.sunflower.order.mapper.OrderMapper;import org.apache.dubbo.config.annotation.DubboService;import javax.annotation.Resource;import java.util.List;/ * @Author: DaiYi * @Email: 484201132@qq.com * @CreateTime: 2021-11-09 10:21 * @Description: */@DubboServicepublic class OrderServiceImpl implements OrderService { @Resource private OrderMapper orderMapper; @Resource private OrderProperties orderProperties; @Override public ResponseBody queryOrders(Order order) { List<Order> orders = orderMapper.queryOrders(order); return ResponseBody.of(StatusCode.SUCCESS, orders); } @Override public ResponseBody queryOrderProperties() { return ResponseBody.of(StatusCode.SUCCESS, orderProperties.getPdfCourierOrderStorageLocation()); }}
@DubboService
注解表示这是一个Dubbo组件,他将可以被其他服务所调用,声明了这个注解之后可以不用再声明Spring框架的注解了。
编写配置文件
注意:这个配置文件应该叫bootstrap.yaml
,因为他将作为引导的配置文件,而非传统的application.yaml
server: port: 8081spring: application: name: order cloud: nacos: server-addr: localhost:8848 discovery: namespace: 2c1bff8e-b530-4c7e-9dda-4d404371c605 config: namespace: ${spring.cloud.nacos.discovery.namespace} group: configuration-files file-extension: yamldubbo: scan: base-packages: com.fenzhichuanmei.sunflower.order.service.impl protocols: dubbo: name: dubbo port: 20881 registry: address: nacos://${spring.cloud.nacos.server-addr} parameters: namespace: ${spring.cloud.nacos.discovery.namespace}
启动Nacos
这里我使用单机启动,没有使用集群,windows中命令为:startup.cmd -m standalone
。
进入Nacos管理界面创建配置文件
因为我们刚刚写了config相关的配置,所以我们现在要去nacos中新建一个配置文件与其对应。
给大家展示一下我引用了远程配置的类:
package com.fenzhichuanmei.sunflower.order.config;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.cloud.context.config.annotation.RefreshScope;import org.springframework.stereotype.Component;import java.io.Serializable;/ * @Author: DaiYi * @Email: 484201132@qq.com * @CreateTime: 2021-11-09 15:56 * @Description: order module configuration class */@Data@Component@RefreshScope@ConfigurationProperties(prefix = "order")public class OrderProperties implements Serializable { / * note that this path must be a directory */ private String pdfCourierOrderStorageLocation;}
@RefreshScope
注解表示这个类中的配置信息将会动态刷新
我在nacos的配置列表中创建了一个名为order
,group为configuration-files
的配置文件与我上面的配置相对应。配置项如下:
order: pdf-courier-order-storage-location: https://www.baidu.com
将order模块注册到Naocs
启动order模块的主启动类,将其注册到nacos。这里一定要注意,在高版本中,bootstrap.yaml
默认不会先加载。通常有两个解决方法,一个是导入相关jar包,另一个就是配置参数了,通常我选择后者。所以我在order模块的VM options配置项中添加了-Dspring.cloud.bootstrap.enabled=true
配置:
启动之后,在服务列表中即可看到我们的服务和暴露出来的接口
编写服务消费者模块
我编写了一个叫main的模块,作为服务消费者。其POM文件内容如下:
<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"> <parent> <artifactId>sunflower</artifactId> <groupId>com.fenzhichuanmei.sunflower</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>main</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <sunflower.common.version>1.0-SNAPSHOT</sunflower.common.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>com.fenzhichuanmei.sunflower</groupId> <artifactId>common</artifactId> <version>${sunflower.common.version}</version> </dependency> </dependencies></project>
项目结构如下:
调用order服务的接口
package com.fenzhichuanmei.sunflower.main.controller;import com.fenzhichuanmei.sunflower.common.order.pojo.Order;import com.fenzhichuanmei.sunflower.common.order.service.OrderService;import com.fenzhichuanmei.sunflower.common.response.ResponseBody;import org.apache.dubbo.config.annotation.DubboReference;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RestController;/ * @Author: DaiYi * @Email: 484201132@qq.com * @CreateTime: 2021-11-09 15:00 * @Description: */@RestControllerpublic class OrderController { @DubboReference(interfaceClass = OrderService.class) protected OrderService orderService; @PostMapping("/queryOrders") public ResponseBody queryOrders(@RequestBody Order order) { return orderService.queryOrders(order); } @PostMapping("/queryOrderProperties") public ResponseBody queryOrderProperties() { return orderService.queryOrderProperties(); }}
@DubboReference
注解表示引用一个服务,可以用interfaceClass
属性指定这个接口
特别注意: 在nacos中,不同命名空间(namespace)之间的服务是不可以相互调用的,而且,即使在同一个namespace下,但是不在同一个组中也是不可以相互调用的,他们之间是隔离起来的
相关帖子:https://developer.aliyun.com/ask/317473
传送门
编写配置文件
server: port: 8082 servlet: context-path: /orderspring: application: name: main cloud: nacos: server-addr: localhost:8848 discovery: namespace: 2c1bff8e-b530-4c7e-9dda-4d404371c605dubbo: protocols: dubbo: name: dubbo port: 20882 registry: address: nacos://${spring.cloud.nacos.server-addr} parameters: namespace: ${spring.cloud.nacos.discovery.namespace}
启动main模块,将其注册到Nacos中
测试
源码地址
gitee:https://gitee.com/daiyi-personal/sunflower.git