【Dubbo】构建SpringBoot整合Dubbo的Demo
参考乐字节的Dubbo教程
https://www.bilibili.com/video/BV19L4y1n7YE
Zookeeper单机部署 (Windows)
因为项目需要,这里我自己学习就采用Zookeeper作为注册中心
ZK的稳定版本镜像仓库
https://downloads.apache.org/zookeeper/stable/
下载后解压出来,拷贝一份zoo_sample.cfg重命名为zoo.cfg
配置信息:
主要是这三个信息需要根据自己需要变更
# The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=E:\\apache-zookeeper-3.7.0-bin\\data zk的数据输出目录 # the port at which the clients will connect clientPort=2181 zk的默认客户端口2181 admin.serverPort=2180 这个是zk的服务端口配置,默认8080 # the maximum number of client connections. # increase this if you need to handle more clients #maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir #autopurge.snapRetainCount=3 # Purge task interval in hours # Set to "0" to disable auto purge feature #autopurge.purgeInterval=1 ## Metrics Providers # # https://prometheus.io Metrics Exporter #metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider #metricsProvider.httpPort=7000 #metricsProvider.exportJvmInfo=true
然后双击cmd脚本启动
zkServer.cmd
Linux则是shell脚本,需要加上参数
zkServer.sh start
只要终端窗口没有 [ERROR]就表示ZK运行正常
Linux使用命令查看
zkServer.sh status
Demo工程结构:
新建一个Maven工程,把源代码目录删除后,配置Pom文件
<?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>cn.cloud9</groupId> <artifactId>Dubbo-Sample</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <!-- 管理的模块 --> <modules> <module>Common-API</module> <module>Provider</module> <module>Consumer</module> </modules> <!-- 配置dubbo的版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <java.version>1.8</java.version> <dubbo.version>2.0.0</dubbo.version> </properties> <parent> <!-- 指定Springboot版本 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> </parent> <!-- 管理依赖的版本,不是用来导入的 --> <dependencyManagement> <dependencies> <!-- dubbo-starter 组件 --> <dependency> <groupId>com.alibaba.spring.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version> </dependency> <!-- dubbo使用zk作为注册中心需要的依赖组件 --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-dependencies-zookeeper</artifactId> <version>2.7.8</version> <type>pom</type> </dependency> <!-- 上面zk组件没有客户端,还需要单独引入 --> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.9</version> </dependency> </dependencies> </dependencyManagement> </project>
Common-API包
也是普通Maven项目
这个里面只需要提供公共的PO和调用接口即可
所有通用的依赖组件都在这个包里面引入
没有任何Spring的配置信息
商品PO类
package cn.cloud9.common.pojo; import lombok.*; import java.io.Serializable; @Data @AllArgsConstructor @NoArgsConstructor @ToString @EqualsAndHashCode public class Goods implements Serializable { private String id; private String name; private Integer count; }
调用的接口规范
package cn.cloud9.common.service; import cn.cloud9.common.pojo.Goods; import java.util.List; public interface ProviderService { List<Goods> listEnableGoods(); }
common-api注入的依赖:
<?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"> <parent> <artifactId>Dubbo-Sample</artifactId> <groupId>cn.cloud9</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>Common-API</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>com.alibaba.spring.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-dependencies-zookeeper</artifactId> <type>pom</type> </dependency> <!-- https://blog.csdn.net/pri_sta_pub/article/details/79087592 --> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
Provider 生产者服务
生产者将规定的接口进行实现
注意这里打注解,按照DEMO演示只需要打上Dubbo的@Service就可以了
实际情况可能Spring也会需要,因为生产者服务自己也可能是一个web服务
这里把完整资源名称写出来
package cn.cloud9.provider.service; import cn.cloud9.common.service.ProviderService; import cn.cloud9.common.pojo.Goods; import java.util.ArrayList; import java.util.List; @com.alibaba.dubbo.config.annotation.Service(interfaceClass = ProviderService.class) @org.springframework.stereotype.Service public class ProviderServiceImpl implements ProviderService { @Override public List<Goods> listEnableGoods() { List<Goods> goodsList = new ArrayList<>(); Goods goods = null; for (int i = 0; i < 10; i++) { goods = new Goods("GOODS-1001-ZXC-" + i, "GOODS-NAME-" + i, 10); goodsList.add(goods); } return goodsList; } }
然后是启动类
除了Springboot启动类,还需要Dubbo配置注解
注意其他需要扫描的类,都需要和启动类同包内
package cn.cloud9.provider; import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @EnableDubboConfiguration @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
生产者的dubbo配置信息:
server:
port: 8081
spring:
application:
name: dubbo-provider # 服务名称
dubbo: # dubbo配置
server: true
application:
name: provider # dubbo注册的名称
registry: # 注册中心地址和端口
address: zookeeper://localhost:2181
protocol: # 采用的协议和端口
name: dubbo
port: 20880
scan: cn.cloud9.provider.service # 扫描的资源地址
生产者引入的依赖只需要Common-API一个就满足了
<?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"> <parent> <artifactId>Dubbo-Sample</artifactId> <groupId>cn.cloud9</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>Provider</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>cn.cloud9</groupId> <artifactId>Common-API</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
Consumer 消费者服务
消费者和生产者一样都是只需要引入Common-API
消费只需要注入接口引用,调用即可
注意,需要使用Dubbo提供的@Reference注解注入
package cn.cloud9.consumer.controller; import cn.cloud9.common.service.ProviderService; import cn.cloud9.common.pojo.Goods; import com.alibaba.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/consume") public class ConsumerController { @Reference(interfaceClass = ProviderService.class) private ProviderService providerService; /** * /consume/goods * @return */ @GetMapping("/goods") public String getEnableGoodsList() { final List<Goods> goodsList = providerService.listEnableGoods(); return goodsList.toString(); } }
消费者的启动类和生产者一样,需要配置@DubboConfiguration
package cn.cloud9.consumer; import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @EnableDubboConfiguration @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
然后是消费者的配置信息,和生产者区分开来
1、服务不能和生产者一个端口号
2、服务名称
3、dubbo注册的服务名称
4、扫描的包
server:
port: 8083
spring:
application:
name: dubbo-consumer
dubbo:
server: true
application:
name: consumer
registry:
address: zookeeper://localhost:2181
protocol:
name: dubbo
port: 20880
scan: cn.cloud9.consumer
调用运行测试
上述步骤配置完成后
使用接口测试工具或者浏览器访问我们提供的web接口
localhost:8083/consume/goods
如果响应了数据,则调用正常
[Goods(id=GOODS-1001-ZXC-0, name=GOODS-NAME-0, count=10), Goods(id=GOODS-1001-ZXC-1, name=GOODS-NAME-1, count=10), Goods(id=GOODS-1001-ZXC-2, name=GOODS-NAME-2, count=10), Goods(id=GOODS-1001-ZXC-3, name=GOODS-NAME-3, count=10), Goods(id=GOODS-1001-ZXC-4, name=GOODS-NAME-4, count=10), Goods(id=GOODS-1001-ZXC-5, name=GOODS-NAME-5, count=10), Goods(id=GOODS-1001-ZXC-6, name=GOODS-NAME-6, count=10), Goods(id=GOODS-1001-ZXC-7, name=GOODS-NAME-7, count=10), Goods(id=GOODS-1001-ZXC-8, name=GOODS-NAME-8, count=10), Goods(id=GOODS-1001-ZXC-9, name=GOODS-NAME-9, count=10)]
使用Zookeeper Assistant 查看
可以发现这里放置的地址:
consumer://192.168.124.8/cn.cloud9.common.service.ProviderService?application=consumer &category=consumers &check=false&dubbo=2.6.0 &interface=cn.cloud9.common.service.ProviderService &methods=listEnableGoods &pid=27832 &side=consumer ×tamp=1646562849952 dubbo://192.168.124.8:20880/cn.cloud9.common.service.ProviderService?anyhost=true &application=provider &dubbo=2.6.0 &generic=false &interface=cn.cloud9.common.service.ProviderService &methods=listEnableGoods &pid=23608 &side=provider ×tamp=1646562834480
实际业务案例场景:
订单服务, 商品服务 作为 【消息的生产者】
酒店端,小程序端, 商家端, 后台管理端,4个服务作为【消息的消费者】
API包,约束Dubbo要求的接口和PO序列化类
一、API包内容
本来应该Dubbo其它框架应该一起放在API里面,其它服务都只要引入API包就可以了
这里API包就是简单的组件
<?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"> <parent> <artifactId>aisw-root</artifactId> <groupId>cn.ymcd.aisw</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>aisw-api</artifactId> <name>aisw-api</name> <packaging>jar</packaging> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.6</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.78</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> </build> </project>
订单接口,和商品接口:
package cn.ymcd.aisw.service; import cn.ymcd.aisw.service.dto.ECommodityDTO; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; /** * aisw_e_commodity 电竞酒店商品表 服务类 * * @projectName: * @author:daizhizhou * @date:2022-03-14 * @version 1.0 */ public interface IECommodityService extends IService<ECommodityDTO> { /** * 电竞酒店商品翻页查询 * * @param eCommodityDTO 商品参数 * @return * @author wangkun * @createTime 2022/3/7 14:51 */ IPage<ECommodityDTO> eCommodityPageQuery(ECommodityDTO eCommodityDTO); }
继承了MybatisPlus的IService接口,约定了处理的 PO序列化类
package cn.ymcd.aisw.service; import cn.ymcd.aisw.service.dto.EOrderDTO; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; /** * TE_E_ORDER电竞酒店订单表服务类 * * @version 1.0 * @projectName:aisw-api * @author:wangkun * @date:2022-03-07 */ public interface IEOrderService extends IService<EOrderDTO> { /** * 分页查询订单列表 * * @param eOrderQueryReq 电竞酒店订单查询传入参数 * @return * @author wangkun * @createTime 2022/3/7 14:51 */ IPage<EOrderDTO> queryEOrderList(EOrderDTO eOrderQueryReq); }
对应的PO类只要求一点,必须序列化
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("aisw_e_commodity") public class ECommodityDTO extends BaseDTO implements Serializable { private static final long serialVersionUID = 1L; // 这里省略属性... ... }
二、生产者服务配置:
API 包 + Dubbo框架 + ZK客户端
订单服务包和商品服务包都是这样的依赖信息
<dependencies> <dependency> <groupId>cn.ymcd.aisw</groupId> <artifactId>aisw-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--dubbo依赖 --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <!-- zkclient客户端 --> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
管理的框架版本在API根版本里面:
<dependencyManagement> <dependencies> <!--dubbo rpc框架 --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency> <!--zkclient客户端 --> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> </dependencies> </dependencyManagement>
生产者实现API接口定义的规范
package cn.ymcd.aisw.ehotel; import cn.ymcd.aisw.ehotel.dao.ECommodityDAO; import cn.ymcd.aisw.service.IECommodityService; import cn.ymcd.aisw.service.dto.ECommodityDTO; import com.alibaba.dubbo.config.annotation.Service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; /** * @projectName: aisw-root * @author: cloud9 * @date: 2022年03月31日 11:02 * @version: 1.0 */ @Service(version = "1.0.0") public class EHotelCommodityServiceImpl extends ServiceImpl<ECommodityDAO, ECommodityDTO> implements IECommodityService { @Override public IPage<ECommodityDTO> eCommodityPageQuery(ECommodityDTO eCommodityDTO) { QueryWrapper<ECommodityDTO> queryWrapper = Wrappers.query(eCommodityDTO); return baseMapper.selectPage(eCommodityDTO.getPage(), queryWrapper); } }
启动类需要打上开启Dubbo的注解
package cn.ymcd.aisw; import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * 应用启动程序 * @return * @author wangkun * @createTime 2022/03/14 11:14 */ @SpringBootApplication(scanBasePackages = "cn.ymcd", exclude = {DynamicDataSourceAutoConfiguration.class}) @EnableTransactionManagement @EnableDubbo public class AiswWxAppletBootApplication { public static void main(String[] args) { SpringApplication.run(AiswWxAppletBootApplication.class, args); } }
然后是yml配置:
1、Dubbo注册的服务名称
2、Zookeeper的客户端端口,需要把服务信息放在Zookeeper上面,这里本机调试只开启一个ZK
3、服务上线时,需要向下面这样放集群ZK的信息
4、Dubbo协议,端口20881,端口只能一个生产者服务对应一个端口,不能共用端口
5、最重要的,你的接口实现类必须要被Dubbo找到,@Service注解是给Dubbo扫描的,SpringBoot不会接管这个Bean对象
所以需要在配置上写包扫描的位置,这样Dubbo才会去加载这个实现类
#[dubbo config] #当前服务/应用的名字 dubbo: application: name: aisw-commodity-server registry: address: zookeeper://172.17.29.7:2181 # address: zookeeper://172.17.29.7:62001?backup=172.17.29.7:62002,172.17.29.7:62003,172.17.29.7:62004 protocol: name: dubbo port: 20881 scan: base-packages: cn.ymcd.aisw
三、消费者服务配置
消费者也需要遵守共同的API规范,引入一样的组件:
<dependency> <groupId>cn.ymcd.aisw</groupId> <artifactId>aisw-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--dubbo rpc框架 --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <!--zkclient客户端 --> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> </dependency>
消费者的消息消费,接口实现是通过Dubbo代理完成的,同样的Springboot不接管这个接口Bean
package cn.ymcd.aisw.commodity.controller; import cn.ymcd.aisw.service.IECommodityService; import cn.ymcd.aisw.service.dto.ECommodityDTO; import cn.ymcd.comm.base.BaseController; import com.alibaba.dubbo.config.annotation.Reference; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @projectName: aisw-root * @author: cloud9 * @date: 2022年03月15日 16:43 * @version: 1.0 */ @RestController @RequestMapping("${sys.path}/commodity") public class ECommodityController extends BaseController { @Reference(version = "1.0.0") private IECommodityService eCommodityService; /** * 电竞酒店商品 * sys/commodity/paging * @param eCommodityDTO 电竞酒店订单查询传入参数 * @return * @author wangkun * @createTime 2020/12/4 11:35 */ @ApiOperation(value = "电竞酒店商品列表", notes = "电竞酒店商品列表") @PostMapping(value = "/paging") public IPage<ECommodityDTO> queryECommodityPage(@RequestBody @ApiParam(name = "商品列表基本对象", value = "传入json格式", required = true) ECommodityDTO eCommodityDTO) { return eCommodityService.eCommodityPageQuery(eCommodityDTO); } }
消费者启动类也需要开启Dubbo:
package cn.ymcd.aisw; import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.transaction.annotation.EnableTransactionManagement; /*** * 应用启动程序 * @return * @author wangkun * @createTime 2022/03/07 11:14 */ @SpringBootApplication(scanBasePackages = "cn", exclude = {DynamicDataSourceAutoConfiguration.class}) @EnableTransactionManagement @EnableDubbo @MapperScan("cn.ymcd.aisw") public class AiswEHotelBootApplication { public static void main(String[] args) { SpringApplication.run(AiswEHotelBootApplication.class, args); } }
消费者服务的配置:
和生产者差不多,也需要把服务信息放到Zookeeper上,注解扫描
但是消费者不需要提供Dubbo协议和端口
#[dubbo config] #dubbo服务名称 dubbo.application.name=aisw-eHotel #注册中心地址 # dubbo.registry.address=zookeeper://192.168.200.25:2181 # dubbo.registry.address=zookeeper://172.17.29.7:62001?backup=172.17.29.7:62002,172.17.29.7:62003,172.17.29.7:62004 dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.scan.base-packages=cn.ymcd.aisw
在调用的时候Debug可以看见接口的代理对象实例: