Dubbo学习系列之六(微服务架构实战)

看了最近文章的反馈,似乎波澜不惊的样子,应该是看官觉得都是小菜,那我就直上硬菜,人狠话不多,开始!
准备:Idea201902/JDK11/ZK3.5.5/Gradle5.4.1/RabbitMQ3.7.13/Mysql8.0.11/Lombok0.26/Erlang21.2/postman7.5.0

难度:新手--战士--老兵--大师

目标:1,模拟商城系统,订单服务RPC调用库存服务,同时物流服务 RPC调用订单服务(订单服务双重身份) 2.订单服务通过MQ消息机制与物流服务通信  

步骤

1.整体架构分析:

三服务:订单business,库存stock,物流logistics,business服务两个核心方法,一是未付订单处理,二是支付订单处理。stock服务一个核心方法,更新库存。logistics服务一个存快递单核心方法。四模块:common是公共模块,business

 2.数据库设计,具体见sql脚本,5个table,其中delivery是快递,item是商品,order_detail是订单商品明细,

 来个代表,order表字段设计,id做PK,BIGINT自增,order_id做订单号,为啥这么设计?id仅是PK,自增,有利于mysql的索引查询效率,mysql是按PK有序存储,pk简单有序,非常利于索引树查找,order_id具有业务含义,变化,值复杂,兼职做PK效率不高,反例就是直接使用自增id做订单号,等需要DB迁移时,就呵呵了,试想迁入的DB也有自增id字段。。。

 3.mbg生成, RabbitMQ,Cors,统一异常,统一响应,均见往期文章,

4.改造下之前的MQ组件,com.biao.mall.common.service.MsgConsumer,将其中的URL变量去掉,放到sendPost参数中,注意这里是个json字符串,这样的妙处就是MsgConsumer完全作为一个独立的收消息组件了,只要有MQ消息,就处理,并根据json字符串的URL,发往目标URL,“高内聚,低耦合”,完美!反例就是将MQ消息接收放到各服务端。

 5.写了两个BO(BusinessObject),业务对象,用于抽象对应真实的业务场景,只有与具体实际业务相关的属性。好处就是减少信息暴露风险,瘦身传输对象,还有如VO,DTO等,如下为OrderBO:

 6. 写com.biao.mall.common.service.DubboOrderService,

两个方法,因是update,都加了事务注解,自动使用spring的事务管理,

 7.写DubboOrderService实现,

com.biao.mall.business.impl.DubboOrderServiceImpl

只看重点:保存未付款订单方法,

 前端传来一个orderBO,生成订单流程:使用雪花算法取得order_id,(见下节文章),然后就是写入数据库一个订单。常见的就是一个订单里多个商品,所以for循环处理,生成多个orderDetail,保存进DB。mbp的CRUD很方便,拿来就用,这里的 stockService即是RPC调用对象,不是spring的bean注入模式。

库存处理要注意,先看看剩余量,搞出库存负数就要和boss说拜拜了!

减库存模式:下单即减,下单加锁。下单即减,就直接减掉库存了,订单取消再返回来,简单粗暴。下单加锁,下单即锁住部分库存,不能用,但库存的数量包括锁住的,订单取消,只是取消锁住的部分。适用场景不同,下单即减在库存充足使用,下单加锁在秒杀,库存有限使用。我采用使用锁模式。12306就是典型的锁模式。

8.那既然调用到了stock服务,就来写service:

com.biao.mall.common.service.DubboStockService

updateStockRPC是DubboOrderServiceImpl中调用的,还有个updateStock,开发过程中废弃了,因不能RPC使用,因为参数QueryWrapper不支持RPC传递,MBP的缺陷?!

9.写stock服务的实现

com.biao.mall.stock.impl.DubboStockServiceImpl

注意点:updateById和update都是dao方法,但前者每次只更新一个,后者,呵呵,qw为null的话,全表update,务必保证qw非空!

10.再回来写com.biao.mall.business.impl.DubboOrderServiceImpl

订单付款处理:增加了快递服务的MQ逻辑,直接写入URL,并封装进消息。

for循环展开,里面一个细节:不需要if判断了?留给看官吧,

11. 写快递服务:com.biao.mall.common.service.DubboDeliveryService

12,写快递logistics服务com.biao.mall.logistic.impl.DubboDeliveryServiceImpl

的实现:RPC调用方式,

 

13.最后各模块的展开的全家福:

13.主体流程应该是:

14. 让马儿欢快地跑起来!启动:zk-->business-->stock-->logistics,可以看到服务已经成功注册到zk:

15.postman给个测试,模拟前端:第一条流程

16.订单数据库:

其他数据库就不一一细看了,

17.第二条流程测试:可以看到响应的是一个我们封装的对象。

18.项目源码地址:其中的day08

https://github.com/xiexiaobiao/dubbo-project.git

项目复盘记:

1.这个demo做出来,花了5个晚上,完全是从New-->Project开始的,一句话:纸上得来终觉浅,绝知此事要躬行!

2.dubbo的微服务架构,其实最核心就是这些,实际生产中就是服务更多,通信方式更多,并常有集群和负载均衡,其他一些中间件等。

3.Lombok不兼容典型表现,虽然实体类加了@Data,

4.既然是老兵级别,我想说些细节:a.字符串判空用StringUtil.isEmpty(); b.对象比较用Object.equals(), c.时间属性全部更新为localDateTime,代替Date,为了线程安全,d.对MQ做了抽象集中, e.实体属性用Integer f.表字段设计细节,

5.一个偶然发现,druid和mbg的版本要搭配好,否则出现,druid无法解析Date-->LocalDateTime

 >>>>attempting to get column 'gmt_create' from result set.  Cause: java.sql.SQLFeatureNotSupportedException

  亲测:mbg3.1.2+druid1.1.18报错。mbg3.1.2+druid1.1.19正常!

5.如果前端传入就是非ISO格式,不带T的日期,我就是硬杠这个问题!经诊断,原因就是fastjson不能解析这个格式。解决:添加能解析的日期格式:com.biao.mall.common.util.MyDateFormat,并注入:com.biao.mall.common.conf.WebConfig

6.对DB中自增长的字段,务必要在entity上加上@TableId(type = IdType.AUTO),这个mbp也不会给自动加上,要手动设置。

测试:去掉后,insert前打印实体:DubboOrderEntity{ id=null, gmtCreate=2019-08-16T00:00,gmtModified=null,orderId='360059542158184448', >>>>>>>>> java.lang.IllegalArgumentException: argument type mismatch>>>>可见 id =null,导致无法insert,

添加后:  ==>  Preparing: INSERT INTO dubbo_order ( gmt_create, order_id, detail_id, order_desc, user_id ) VALUES ( ?, ?, ?, ?, ? ),可见,insert语句忽略了id字段,这正是想要的效果!

6.双重@service注解:有点意思! 测试完全ok。听说dubbo的@service不能和@Transactional 同时存在,有待验证!

7.模块间不能循环依赖,如 A-->B,B-->A,会提示出错:Circular dependency between the following tasks:

   解决方法:将B中的部分转移到A,或到公共模块,避免循环。

8.遇到错误 >>>  qos-server can not bind localhost:22222,

  知识点:Qos=Quality of Service,qos是Dubbo的在线运维命令,可以对服务进行动态的配置、控制及查询,原因:provider已经使用了默认的qos端口导致consumer启动时使用默认的出错,配置为不同的端口即可。

9.遇到错误>>>com.alibaba.dubbo.rpc.RpcException: Unsupported client type: curator, supported client type is grizzly mina netty netty3 netty4

  可能的问题:org.apache.curator 依赖未加入模块依赖

10.遇到错误提示:

问题分析:这里提示键值重复,我在DB中将该字段设置为UQ,目的是防止重复,这里却产生了问题,因为mybatis的单表默认方法:dubboStockDao.update(stockEntity,qw)生成的SQL是除了PK外,直接set其他所有字段,导致无法更新。解决:1.dao中自己写update方法,除去UQ字段  2,更好的:DB中的字段,都不要使用UQ,应该在应用层处理,这是个原则,DB只存数据,尽量少逻辑

11.其实上面这个项目有个重大的bug,各位看官可以思考下,且看我下回分解。

12.码字N小时,上头了.

往期文章推荐:

猜你喜欢

转载自www.cnblogs.com/xxbiao/p/12050525.html