一天,IT 小王不经意间上 了下 脉脉,发现这年头50W的 年薪其实是很普遍的,小王马上 躁动起来,内心感慨自己就是那被埋没的人才啊 ,机会终于来了,阿里的电话面开始了, “现在 微服务很流行,那 一 般 架构如何? ”小王: “。 。 。 ”, “那你擅长哪方面? ”小王: “ SSM ”。 第二天,小王默默的网上 下了书单--- 《15天微服务从入门到精通》。如此看来微服务是那么的重要,不懂点微服务是不敢出来混的了。那微服务是啥,其实就是将大的应用切割成小的服务,每个服务可独立部署,每个服务语言独立,架构自由,升级独立,还能按需水平扩展。那今天开始就真正玩一下微服务吧,那服务间必须有沟通交流啊,可以是REST,也可是MQ,那开始动手吧。准备:Idea201902/JDK11/ ZK3.5.5/Gradle5.4.1/RabbitMQ3.7.13 /Mysql8.0.11/Lombok0.25-EAP/Erlang21.2/postman7.5.0难度:新手--战士--老兵--大师目标:1,模拟一个商城系统,订单服务给库存服务通过MQ发消息 2,统一异常处理 3. 跨域设置 4.统一后端响应
步骤: 1,整体思路: 三个模块,business是业务订单业务服务,common是公共区,stock是库存业务服务。订单生产后,要减库存处理。最后完整展开如下图, business和 stock依赖于common,当然,还是从头整起。
2、 lombok插件安装:;
3、 建项目和子模块,略,可参考前面篇章4.common模块跑一下mbp,自动生成下各种对象,移动一下相关的文件到对应的模块,如order相关的到business,其实本节并没涉及DB操作,只是为了用mbp生成一些初始文件而已;
5、 开始MQ的配置:com.biao.mall.common.conf.RabbitConf“配置分离”原则,rabbbit的连接信息从配置文件读取,当然可以放到外部配置中心,如Nacos/Apollo上;
消息队列和交换器设置:
为了形成这样一个结构:
P生产消息,放到queueA或exchangeA中,C1正常消费并处理:订单被付款,真实减库存。如果订单消息30分钟未付款,那就成为“死信”,进入DLX,再进入DLQ,C2消费并处理:取消订单,回退库存。这次,我们只做到简单收发消息即可。复杂的待后续。
注入连接工厂类,获取MQ连接,
注入消息发送模板类,用来操作消息对象的,这里说明了必须是prototype类型,为啥,这个template使用非常频繁,防止多线程并发抢占。
声明一个queue,并设置其消息的属性,TTL值,过期后自动进入由routingKey指定的DLX中,这就是延迟消息的核心了。
特别注意下最后一行的queue构造函数的参数:
durable 是否持久化到磁盘,MQ重启,会自动恢复该消息,exclusive 是否只能由声明者使用该queueautoDelete 当该queue不再使用,是否自动删除arguments 参数Map类型,指定TTl,路由等
消费者开启多线程:
6、 消息生产者:com.biao.mall.business.service.MsgProducer类;
注意,一是rabbitTemplate没有用@Autowired,二是,使用@Autowired的构造函数注入rabbitTemplate,为啥?配合scope属性
面试题:如何保证消息的幂等?答:“每个消息可以设置一个UUID,这样消费时看是否已经消费了该ID,即可防止重复消费”,代码在此:
面试题:如何知道消息是否被消费?“消息被消费后,有个回调confirm方法,获得ack就知道消费成功了”“嗯,不错!”,代码在此:
7、 com.biao.mall.business.controller.MsgProducerController类:;
我这里没有直接@Autowired两个成员变量,为啥?这是个细节,因为java的加载顺序是:静态对象-->构造函数-->成员变量,Java类会先执行构造方法,然后再给注解了@Autowired 的user注入值,这样写就防止了NPE。
两个测试方法,一个直接发到queueA上,一个是使用生产者操作,先发到exchangeA
8、 来到消费方,消息处理:;
com.biao.mall.stock.service.MsgConsumer类:
这里我并没有将消息处理直接放controller里,可以查看controller中
是完全没有MQ处理相关的,那怎么收消息呢,即由MsgConsumer处理消息后主动推过去,这样“高内聚,低耦合”,下个章节,我来说下这样的妙处。
注解@RabbitListener指定监听的queue,Handler就是处理方法了。
后面这个sendPost就是给URL推消息的,有点长,直接隐藏方法体了,具体请看源码,看到上图的URL就是DubboStockController的一个RequestMapping,
9、 终于可以跑一把了,写的好累;
启动ZK-->stock-->business,
进入rabbbit管理界面就可以看到创建的queue和exchange了,蓝色TTL指time-to-live,死信时间,DLX是指有绑定死信交换器,DLK指有routingKey,
10、 使用Postman,因为是测试Post请求,;
11、 business的console下:;
stock的console下:消息走向非常清晰!
12、 也可使用http://localhost:8083/producer/msg,然后在rabbit管理界面,观察下延迟消息的运行过程;
13、 统一异常处理,简单描述下,这个不难:;
先可自定义一些异常类,略,
然后@ControllerAdvice 注解统一异常处理类,对所有controller层的异常都可捕获,可以根据捕获的异常类型,做不同的处理,结束。
14、 统一响应:封装一个响应类对象,com.biao.mall.common.component.ResEntity;
后面的response都可以装在这里面,
然后做一个过滤器:使用@WebFilter方式注入,可以做一些全局处理,再次防止把错误抛到前端。比如,200状态做啥,500状态做啥。
15、 跨域处理:略,只是说明下,跨域处理方法很多,JDK1.8以上中,只需要在controller上加注解@CrossOrigin即可解决跨域;
源码地址:其中的day05
https://github.com/xiexiaobiao/dubbo-project.git
项目复盘记:
1、 有些模块启动后一直提示无法注入其他模块的bean,解决方法:现将该模块启动类所在包提升,如从com.biao.mall.business--->com.biao.mall,提升到所有module的公共包最高级别,并且所有模块的启动类都放在公共包最高级别,才能都正常扫描所有包,其他所谓的main上@scanBasePackages指定或扩大扫描包范围的方法都不行,亲测几个晚上的结果2.MQ中间件有很多,流行的有alibaba的RocketMQ,RabbitMQ,KAFKA,其中KAFKA性能最强,他们略有差异,在此不表,都支持集群模式扩展3.Rabbit中重要概念:;
创建一个vhost,系统会自动建立一些该host下的exchange;
default Exchange不能进行Binding,也不需要进行绑定;其他任何Exchange都需要和Queue进行Binding;
Queue属性:x-message-ttl:一个消息在队列中的存活时间
Queue属性:x-dead-letter-exchange:当消息被拒绝或者消息过期,消息重新发送到的交换机(Exchange)的可选名称。
Queue属性:x-dead-letter-routing-key:当消息被拒绝或者消息过期,消息重新发送到的交换机绑定的Route key的名称
4、 多模块间dao调用原则:;
Service A不应直接调用B的Dao层,而是应该调用B的Service层业务实现,因为B的Service是对其Dao的CRUD封装,在多库且有缓存的情况下,可能存在跨库操作,直接调用B的Dao显然是不合理的。简单的说就是为了代码结构更清晰,长远点说就是方便以后不同模块之间进行项目拆分。如果被调用service没有需要的逻辑,可再抽象出一个新的service供调用。
5、 如果在使用中,修改了比如exchange或queue的配置,再启动程序会报错,提示属性如routingKey冲突,可以直接先MQ管理界面删除exchange或queue,启动程序会自动重建6.关于一些引入什么依赖,连接配置之类的,我觉得没说的必要,看看代码即可最后项目文件做了些精简,图1中架构有些差异,但不影响7.码字三小时,点个右下角“在看”吧![*][nbsp26];