利用Springboot+Dubbo,构建分布式微服务,全程注解开发

利用Springboot+Dubbo,构建分布式微服务,全程注解开发

作者: 鸭血粉丝 2020-05-19 08:15:16

开发

架构

分布式 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

创新互联建站坚持“要么做到,要么别承诺”的工作理念,服务领域包括:网站建设、成都网站制作、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的海丰网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

一、先来一张图

说起 Dubbo,相信大家都不会陌生!阿里巴巴公司开源的一个高性能优秀的服务框架,可以使得应用可通过高性能的 RPC 实现服务的输出和输入功能,同时可以和 Spring 框架无缝集成。

Dubbo 架构图

节点角色说明:

  • Provider:暴露服务的服务提供方
  • Consumer:调用远程服务的服务消费方
  • Registry:服务注册与发现的注册中心
  • Monitor:统计服务的调用次数和调用时间的监控中心
  • Container:服务运行容器

二、实现思路

今天,我们以一个用户选择商品下订单这个流程,将其拆分成3个业务服务:用户中心、商品中心、订单中心,使用 Springboot + Dubbo 来实现一个小 Demo!

服务交互流程如下:

本文主要是介绍 Springboot 与 Dubbo 的框架整合以及开发实践,而真实的业务服务拆分是一个非常复杂的过程,比我们介绍的这个要复杂的多,上文提到的三个服务只是为了项目演示,不必过于纠结为什么要这样拆分!

好了,废话也不多说了,下面我们开撸!

  • 1.在虚拟机创建 4 台 centos7,任意选择一台安装 zookeeper
  • 2.构建微服务项目并编写代码
  • 3.在 centos7 上部署微服务
  • 4.远程服务调用测试

三、zookeeper安装

在使用 Dubbo 之前,我们需要一个注册中心,目前 Dubbo 可以选择的注册中心有 zookeeper、Nacos 等,一般建议使用 zookeeper!

首先在安装 Zookeeper 之前,需要安装并配置好 JDK,本机采用的是Oracle Java8 SE。

  • 安装JDK(已经安装可以忽略)
  
 
 
 
  1. yum -y install java-1.8.0-openjdk 
  • 查看java安装情况
  
 
 
 
  1. java -version 

  • JDK安装完成之后,下载安装Zookeeper
  
 
 
 
  1. #创建一个zookeeper文件夹 
  2. cd /usr 
  3. mkdir zookeeper 
  4.  
  5. #下载zookeeper-3.4.14版本 
  6. wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz 
  7.  
  8. #解压 
  9. tar -zxvf zookeeper-3.4.14.tar.gz 
  • 创建数据、日志目录
  
 
 
 
  1. #创建数据和日志存放目录 
  2. cd /usr/zookeeper/ 
  3. mkdir data 
  4. mkdir log 
  5.  
  6. #把conf下的zoo_sample.cfg备份一份,然后重命名为zoo.cfg 
  7. cd conf/ 
  8. cp zoo_sample.cfg zoo.cfg 
  • 配置zookeeper
  
 
 
 
  1. #编辑zoo.cfg文件 
  2. vim zoo.cfg 

  • 启动Zookeeper
  
 
 
 
  1. #进入Zookeeper的bin目录 
  2. cd zookeeper/zookeeper-3.4.14/bin 
  3.  
  4. #启动Zookeeper 
  5. ./zkServer.sh start 
  6.  
  7. #查询Zookeeper状态 
  8. ./zkServer.sh status 
  9.  
  10. #关闭Zookeeper状态 
  11. ./zkServer.sh stop 

出现如下信息,表示启动成功!

四、项目介绍

  • springboot版本:2.1.1.RELEASE
  • zookeeper版本:3.4.14
  • dubbo版本:2.7.3
  • mybtais-plus版本:3.0.6
  • 数据库:mysql-8
  • 构建工具:maven
  • 服务模块:用户中心、商品中心、订单中心

五、代码实践

5.1、初始化数据库

首先在 mysql 客户端,创建3个数据库,分别是:dianshang-user、dianshang-platform、dianshang-business。

  • 在 dianshang-user 数据库中,创建用户表 tb_user,并初始化数据

  • 在 dianshang-platform 数据库中,创建商品表 tb_product,并初始化数据

  • 在 dianshang-platform 数据库中,创建订单表 tb_order、订单详情表 tb_order_detail

5.2、创建工程

数据库表设计完成之后,在 IDEA 下创建一个名称为dianshang的Springboot工程。

最终的目录如下图:

目录结构说明:

  • dianshang-common:主要存放一些公共工具库,所有的服务都可以依赖使用
  • dianshang-business:订单中心,其中api模块主要是提供dubbo服务暴露接口,provider模块是一个springboot项目,提供服务处理操作
  • dianshang-user:用户中心,其中api模块和provider模块,设计与之类似
  • dianshang-platform:商品中心,其中api模块和provider模块,设计与之类似

在父类pom文件中加入dubbo和zookeeper客户端,所有依赖的项目都可以使用。

  
 
 
 
  1.  
  2.  
  3.     org.projectlombok 
  4.     lombok 
  5.     1.18.4 
  6.     provided 
  7.  
  8.  
  9.  
  10.  
  11.     org.apache.dubbo 
  12.     dubbo-spring-boot-starter 
  13.     2.7.3 
  14.  
  15.  
  16.  
  17.     org.apache.zookeeper 
  18.     zookeeper 
  19.     3.4.13 
  20.      
  21.          
  22.             org.slf4j 
  23.             slf4j-api 
  24.          
  25.          
  26.             org.slf4j 
  27.             slf4j-log4j12 
  28.          
  29.          
  30.             log4j 
  31.             log4j 
  32.          
  33.      
  34.  
  35.  
  36.  
  37.     org.apache.curator 
  38.     curator-framework 
  39.     4.2.0 
  40.  
  41.  
  42.     org.apache.curator 
  43.     curator-recipes 
  44.     4.2.0 
  45.  

温馨提示:小编在搭建环境的时候,发现一个坑,工程中依赖的zookeeper版本与服务器的版本,需要尽量一致,例如,本例中zookeeper服务器的版本是3.4.14,那么在依赖zookeeper文件库的时候,也尽量保持一致,如果依赖3.5.x版本的zookeeper,项目在启动的时候会各种妖魔鬼怪的报错!

5.3、创建用户中心项目

在 IDEA 中,创建dianshang-user子模块,并依赖dianshang-common模块

  
 
 
 
  1.  
  2.      
  3.         org.project.demo 
  4.         dianshang-common 
  5.         1.0.0 
  6.      
  7.  

同时,创建dianshang-user-provider和dianshang-user-api模块。

  • dianshang-user-api:主要对其他服务提供接口暴露
  • dianshang-user-provider:类似一个web工程,主要负责基础业务的crud,同时依赖dianshang-user-api模块

5.3.1、配置dubbo服务

在dianshang-user-provider的application.yml文件中配置dubbo服务,如下:

  
 
 
 
  1. #用户中心服务端口 
  2. server: 
  3.   port: 8080 
  4. #数据源配置 
  5. spring: 
  6.   datasource: 
  7.     druid: 
  8.       driver-class-name: com.mysql.cj.jdbc.Driver 
  9.       url: "jdbc:mysql://localhost:3306/dianshang-user" 
  10.       username: root 
  11.       password: 111111 
  12. #dubbo配置 
  13. dubbo: 
  14.   scan: 
  15.     # 包名根据自己的实际情况写 
  16.     base-packages: org.project.dianshang.user 
  17.   protocol: 
  18.     port: 20880 
  19.     name: dubbo 
  20.   registry: 
  21.     #zookeeper注册中心地址 
  22.     address: zookeeper://192.168.0.107:2181 

5.3.2、编写服务暴露接口以及实现类

在dianshang-user-api模块中,创建一个UserApi接口,以及返回参数对象UserVo!

  
 
 
 
  1. public interface UserApi { 
  2.  
  3.     /** 
  4.      * 查询用户信息 
  5.      * @param userId 
  6.      * @return 
  7.      */ 
  8.     UserVo findUserById(String userId); 

其中UserVo,需要实现序列化,如下:

  
 
 
 
  1. @Data 
  2. @EqualsAndHashCode(callSuper = false) 
  3. @Accessors(chain = true) 
  4. public class UserVo implements Serializable { 
  5.  
  6.     private static final long serialVersionUID = 1L; 
  7.  
  8.     /** 
  9.      * 用户ID 
  10.      */ 
  11.     private String userId; 
  12.  
  13.     /** 
  14.      * 用户中文名 
  15.      */ 
  16.     private String userName; 

在dianshang-user-provider模块中,编写UserApi接口实现类,如下:

  
 
 
 
  1. @Service(interfaceClass =UserApi.class) 
  2. @Component 
  3. public class UserProvider implements UserApi { 
  4.  
  5.     @Autowired 
  6.     private UserService userService; 
  7.  
  8.     @Override 
  9.     public UserVo findUserById(String userId) { 
  10.         QueryWrapper queryWrapper = new QueryWrapper(); 
  11.         queryWrapper.eq("user_id",userId); 
  12.         User source = userService.getOne(queryWrapper); 
  13.         if(source != null){ 
  14.             UserVo vo = new UserVo(); 
  15.             BeanUtils.copyProperties(source,vo); 
  16.             return vo; 
  17.         } 
  18.         return null; 
  19.     } 

其中的注解@Service指的是org.apache.dubbo.config.annotation.Service下的注解,而不是Spring下的注解哦!

接着,我们继续创建商品中心项目!

5.4、创建商品中心项目

与用户中心项目类似,在 IDEA 中,创建dianshang-platform子模块,并依赖dianshang-common模块

  
 
 
 
  1.  
  2.      
  3.         org.project.demo 
  4.         dianshang-common 
  5.         1.0.0 
  6.      
  7.  

同时,创建dianshang-platform-provider和dianshang-platform-api模块。

  • dianshang-platform-api:主要对其他服务提供接口暴露
  • dianshang-platform-provider:类似一个web工程,主要负责基础业务的crud,同时依赖dianshang-platform-api模块

5.4.1、配置dubbo服务

在dianshang-platform-provider的application.yml文件中配置dubbo服务,如下:

  
 
 
 
  1. #用户中心服务端口 
  2. server: 
  3.   port: 8081 
  4. #数据源配置 
  5. spring: 
  6.   datasource: 
  7.     druid: 
  8.       driver-class-name: com.mysql.cj.jdbc.Driver 
  9.       url: "jdbc:mysql://localhost:3306/dianshang-platform" 
  10.       username: root 
  11.       password: 111111 
  12. #dubbo配置 
  13. dubbo: 
  14.   scan: 
  15.     # 包名根据自己的实际情况写 
  16.     base-packages: org.project.dianshang.platform 
  17.   protocol: 
  18.     port: 20881 
  19.     name: dubbo 
  20.   registry: 
  21.     #zookeeper注册中心地址 
  22.     address: zookeeper://192.168.0.107:2181 

5.4.2、编写服务暴露接口以及实现类

在dianshang-platform-api模块中,创建一个ProductApi接口,以及返回参数对象ProductVo!

  
 
 
 
  1. public interface ProductApi { 
  2.  
  3.     /** 
  4.      * 通过商品ID,查询商品信息 
  5.      * @param productId 
  6.      * @return 
  7.      */ 
  8.     ProductVo queryProductInfoById(String productId); 

其中ProductVo,需要实现序列化,如下:

  
 
 
 
  1. @Data 
  2. @EqualsAndHashCode(callSuper = false) 
  3. @Accessors(chain = true) 
  4. public class ProductVo implements Serializable { 
  5.  
  6.     private static final long serialVersionUID = 1L; 
  7.  
  8.     /**商品ID*/ 
  9.     private String productId; 
  10.  
  11.     /**商品名称*/ 
  12.     private String productName; 
  13.  
  14.     /**商品价格*/ 
  15.     private BigDecimal productPrice; 

在dianshang-platform-provider模块中,编写ProductApi接口实现类,如下:

  
 
 
 
  1. @Service(interfaceClass = ProductApi.class) 
  2. @Component 
  3. public class ProductProvider implements ProductApi { 
  4.  
  5.     @Autowired 
  6.     private ProductService productService; 
  7.  
  8.     @Override 
  9.     public ProductVo queryProductInfoById(String productId) { 
  10.         //通过商品ID查询信息 
  11.         Product source = productService.getById(productId); 
  12.         if(source != null){ 
  13.             ProductVo vo = new ProductVo(); 
  14.             BeanUtils.copyProperties(source,vo); 
  15.             return vo; 
  16.         } 
  17.         return null; 
  18.     } 

接着,我们继续创建订单中心项目!

5.5、创建订单中心项目

与商品中心项目类似,在 IDEA 中,创建dianshang-business子模块,并依赖dianshang-common模块

  
 
 
 
  1.  
  2.      
  3.         org.project.demo 
  4.         dianshang-common 
  5.         1.0.0 
  6.      
  7.  

同时,创建dianshang-business-provider和dianshang-business-api模块。

  • dianshang-business-api:主要对其他服务提供接口暴露
  • dianshang-business-provider:类似一个web工程,主要负责基础业务的crud,同时依赖dianshang-business-api模块

5.5.1、配置dubbo服务

在dianshang-business-provider的application.yml文件中配置dubbo服务,如下:

  
 
 
 
  1. #用户中心服务端口 
  2. server: 
  3.   port: 8082 
  4. #数据源配置 
  5. spring: 
  6.   datasource: 
  7.     druid: 
  8.       driver-class-name: com.mysql.cj.jdbc.Driver 
  9.       url: "jdbc:mysql://localhost:3306/dianshang-business" 
  10.       username: root 
  11.       password: 111111 
  12. #dubbo配置 
  13. dubbo: 
  14.   scan: 
  15.     # 包名根据自己的实际情况写 
  16.     base-packages: org.project.dianshang.business 
  17.   protocol: 
  18.     port: 20882 
  19.     name: dubbo 
  20.   registry: 
  21.     #zookeeper注册中心地址 
  22.     address: zookeeper://192.168.0.107:2181 

5.5.2、编写服务暴露接口以及实现类

在dianshang-business-api模块中,创建一个OrderApi接口,以及返回参数对象OrderVo!

  
 
 
 
  1. public interface OrderApi { 
  2.  
  3.     /** 
  4.      * 通过用户ID,查询用户订单信息 
  5.      * @param userId 
  6.      * @return 
  7.      */ 
  8.     List queryOrderByUserId(String userId); 

其中OrderVo,需要实现序列化,如下:

  
 
 
 
  1. @Data 
  2. @EqualsAndHashCode(callSuper = false) 
  3. @Accessors(chain = true) 
  4. public class OrderVo implements Serializable { 
  5.  
  6.     private static final long serialVersionUID = 1L; 
  7.  
  8.     /**订单ID*/ 
  9.     private String orderId; 
  10.  
  11.     /**订单编号*/ 
  12.     private String orderNo; 
  13.  
  14.     /**订单金额*/ 
  15.     private BigDecimal orderPrice; 
  16.  
  17.     /**下单时间*/ 
  18.     private Date orderTime; 

在dianshang-business-provider模块中,编写OrderApi接口实现类,如下:

  
 
 
 
  1. @Service(interfaceClass = OrderApi.class) 
  2. @Component 
  3. public class OrderProvider implements OrderApi { 
  4.  
  5.     @Autowired 
  6.     private OrderService orderService; 
  7.  
  8.     @Override 
  9.     public List queryOrderByUserId(String userId) { 
  10.         QueryWrapper queryWrapper = new QueryWrapper(); 
  11.         queryWrapper.eq("user_id",userId); 
  12.         List sourceList = orderService.list(queryWrapper); 
  13.         if(!CollectionUtils.isEmpty(sourceList)){ 
  14.             List voList = new ArrayList<>(); 
  15.             for (Order order : sourceList) { 
  16.                 OrderVo vo = new OrderVo(); 
  17.                 BeanUtils.copyProperties(order, vo); 
  18.                 voList.add(vo); 
  19.             } 
  20.             return voList; 
  21.         } 
  22.         return null; 
  23.     } 

至此,3个项目的服务暴露接口已经开发完成!接下来我们来编写怎么进行远程调用!

5.6、远程调用

5.6.1、编写创建订单服务

在dianshang-business-provider模块中,编写创建订单接口之前,先依赖dianshang-business-api和dianshang-user-api,如下:

  
 
 
 
  1.  
  2.  
  3.     org.project.demo 
  4.     dianshang-platform-api 
  5.     1.0.0 
  6.  
  7.  
  8.  
  9.     org.project.demo 
  10.     dianshang-user-api 
  11.     1.0.0 
  12.  

在dianshang-business-provider模块中,编写创建订单服务,如下:

  
 
 
 
  1. @RestController 
  2. @RequestMapping("/order") 
  3. public class OrderController { 
  4.  
  5.     @Autowired 
  6.     private OrderService orderService; 
  7.  
  8.     @Autowired 
  9.     private OrderDetailService orderDetailService; 
  10.  
  11.     @Reference(check =false) 
  12.     private ProductApi productApi; 
  13.  
  14.     @Reference(check =false) 
  15.     private UserApi userApi; 
  16.  
  17.     /** 
  18.      * 新增 
  19.      */ 
  20.     @JwtIgnore 
  21.     @RequestMapping(value = "/add") 
  22.     public boolean add(String productId,String userId){ 
  23.         LocalAssert.isStringEmpty(productId,"产品Id不能为空"); 
  24.         LocalAssert.isStringEmpty(userId,"用户Id不能为空"); 
  25.         ProductVo productVo = productApi.queryProductInfoById(productId); 
  26.         LocalAssert.isObjectEmpty(productVo,"未查询到产品信息"); 
  27.         UserVo userVo = userApi.findUserById(userId); 
  28.         LocalAssert.isObjectEmpty(userVo,"未查询到用户信息"); 
  29.         Order order = new Order(); 
  30.         order.setOrderId(IdGenerator.uuid()); 
  31.         order.setOrderNo(System.currentTimeMillis() + ""); 
  32.         order.setOrderPrice(productVo.getProductPrice()); 
  33.         order.setUserId(userId); 
  34.         order.setOrderTime(new Date()); 
  35.         orderService.save(order); 
  36.  
  37.         OrderDetail orderDetail = new OrderDetail(); 
  38.         orderDetail.setOrderDetailId(IdGenerator.uuid()); 
  39.         orderDetail.setOrderId(order.getOrderId()); 
  40.         orderDetail.setProductId(productId); 
  41.         orderDetail.setSort(1); 
  42.         orderDetailService.save(orderDetail); 
  43.         return true; 
  44.     } 

其中的@Reference注解,是属于org.apache.dubbo.config.annotation.Reference下的注解,表示远程依赖服务。

参数check =false表示启动服务时,不做远程服务状态检查,这样设置的目的就是为了防止当前服务启动不了,例如用户中心项目没有启动成功,但是订单中心又依赖了用户中心,如果check=true,此时订单中心启动会报错!

5.6.2、编写用户查询自己的订单信息

同样的,在dianshang-user-provider模块中,编写用户查询自己的订单信息接口之前,先依赖dianshang-business-api和dianshang-user-api,如下:

  
 
 
 
  1.  
  2.     org.project.demo 
  3.     dianshang-business-api 
  4.     1.0.0 
  5.  
  6.  
  7.     org.project.demo 
  8.     dianshang-user-api 
  9.     1.0.0 
  10.  

在dianshang-user-provider模块中,编写用户查询自己的订单信息接口,如下:

  
 
 
 
  1. @RestController 
  2. @RequestMapping("/user") 
  3. public class UserController { 
  4.  
  5.     @Reference(check =false) 
  6.     private OrderApi orderApi; 
  7.  
  8.  
  9.     /** 
  10.      * 通过用户ID,查询订单信息 
  11.      * @param userId 
  12.      * @return 
  13.      */ 
  14.     @RequestMapping("/list") 
  15.     public List queryOrderByUserId(String userId){ 
  16.         return orderApi.queryOrderByUserId(userId); 
  17.     } 

至此,远程服务调用,编写完成!

六、服务测试

在将项目部署在服务器之前,咱们先本地测试一下,看服务是否都可以跑通?

  • 启动用户中心dianshang-user-provider

  • 继续启动商品中心dianshang-platform-provider

接着启动订单中心dianshang-business-provider

最后,我们来测试一下服务接口是否为我们预期的结果?

打开浏览器,输入http://127.0.0.1:8082/order/add?productId=1&userId=1测试创建订单接口,页面运行结果显示正常!

我们再来看看数据库,订单是否生成?

ok!很清晰的看到,数据已经进去了,没啥问题!

我们再来测试一下在用户中心订单查询接口,输入http://127.0.0.1:8080/user/list?userId=1,页面运行结果如下!

到此,本地服务测试基本通过!

七、服务器部署

在上文中,我们介绍了服务的构建、开发和测试,那如何在服务器端部署呢?

首先,修改各个项目的application.yml文件,将其中的数据源地址、dubbo注册中心地址修改为线上能联通的地址,然后在dianshang目录下使用maven工具对整个工程执行如下命令进行打包!

  
 
 
 
  1. mvn clean install 

也可以在 IDEA 环境下,通过maven配置clean install命令执行打包。

将各个项目target目录下的dianshang-user-provider.jar、dianshang-platform-provider.jar、dianshang-business-provider.jar拷贝出来。

分别上传到对应的服务器目录,本服务器采用的是 CentOS7,总共4台服务器,其中一台部署zookeeper,另外三台部署三个微服务项目。

登录服务器,输入如下命令,确保JDK已经安装完成!

  
 
 
 
  1. java -version 

关闭所有服务器的防火墙,放行端口访问!

  
 
 
 
  1. #关闭防火墙 
  2. systemctl stop firewalld.service 
  3.  
  4. #禁止开机启动 
  5. systemctl disable firewalld.service 
  • 启动用户中心服务,日志信息输出到service.log(虚拟机ip:192.168.0.108)
  
 
 
 
  1. nohup java -jar  dianshang-user-provider.jar > service.log 2>&1 & 
  • 启动商品中心服务,日志信息输出到service.log(虚拟机ip:192.168.0.107)
  
 
 
 
  1. nohup java -jar  dianshang-platform-provider.jar > service.log 2>&1 & 
  • 启动订单中心服务,日志信息输出到service.log(虚拟机ip:192.168.0.109)
  
 
 
 
  1. nohup java -jar  dianshang-business-provider.jar > service.log 2>&1 & 

打开浏览器,输入http://192.168.0.109:8082/order/add?productId=1&userId=1测试创建订单接口,页面运行结果显示正常!

我们再来测试一下在用户中心订单查询接口,输入输入http://192.168.0.108:8080/user/list?userId=1,页面运行结果如下!

很清晰的看到,输出了2条信息,第二条订单是在测试环境服务器生成的,第一条是本地开发环境生成的。

到此,服务器部署基本已经完成!

如果是生产环境,可能就需要多台zookeeper来保证高可用,至少2台服务器来部署业务服务,通过负载均衡来路由!

八、总结

整片文章比较长,主要是围绕 springboot + dubbo 的整合,通过注解开发实现远程服务调用像传统的 springmvc 开发一样轻松,当然还可以通过xml配置方式实现dubbo服务的调用,这个会在后期去介绍。

同时也介绍了服务器的部署,从中可以看出,开发虽然简单,但是由于分布式部署,如何保证服务高可用成了开发人员头等工作任务,所以,分布式微服务开发虽然开发简单,但是如何确保部署的服务高可用?运维方面会带来不少的挑战!

九、参考

1、apache - dubbo - 官方文档

当前标题:利用Springboot+Dubbo,构建分布式微服务,全程注解开发
网站地址:http://www.hantingmc.com/qtweb/news44/248094.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联