06、Dubbo实战:微服务架构实战

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

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

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

1、 整体架构分析:;

三服务:订单business,库存stock,物流logistics, business 服务两个核心方法,一是未付订单处理,二是支付订单处理。 stock服务一 个核心方法, 更新 库存 。 logistics 服务 一个存快递单核心方法。四模块:common是公共模块,注册中心:business服务/stock服务因使用RPC要注册且订阅,logistics服务只订阅服务。

*

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、 mbp生成,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、 第二条流程测试:可以看到响应的是一个我们封装的对象;

*

结果,dubbo_delivery表中,快递可以出发了!

*

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,会提示出错:Circulardependencybetweenthefollowingtasks:;

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

8、 遇到错误>>>qos-servercannotbindlocalhost:22222,;

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

9、 遇到错误>>>com.alibaba.dubbo.rpc.RpcException:Unsupportedclienttype:curator,supportedclienttypeisgrizzlyminanettynetty3netty4;

可能的问题: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小时,上头了,点个右下角的"在看"吧![*][nbsp25];