1. 导读
本文主要讲述RabbitMQ客户端的封装和使用,思路不一定准确,但目前生产中是这样使用的,有不对的地方,欢迎批评指正。
本文需要对springboot、spring的spel表达式、rabbitmq工作流程、死信队列机制有一定的了解,熟悉springboot starter的大概思路。
客户端思路
定义客户端
定义所有实体类的父类InterfaceFactoryBean
实现ImportBeanDefinitionRegistrar,扫描自定义注解标注的路径
公用service接口,第三方持久层需实现
2. 客户端
最近公司微服务架构中有需要同步数据库表数据的需求,考虑到封装性和扩展性,减少第三方项目的开发工作量,思路如下(本文不涉及缓存):
2.1. 服务端创建Fanout模式的交换机,提供数据发送接口
2.2. 封装客户端,客户端作为jar包存在,供第三方项目(其他项目,以下都以第三方代称)依赖使用
2.3. 第三方项目依赖的客户端启动创建队列绑定上面的交换机,并创建死信交换机、队列
2.4. 第三方项目依赖的客户端接到数据后,通过反射执行第三方实现的客户端中定义的持久层接口,实现数据入库。
2.5定义死信队列接收消费失败的消息,死信队列通过RestTemplate入库,客户端实现
2.6. 失败的消息会重试3次,重试后再次失败,消息进入死信队列。每次新接收的消息会检查库中是否存在同类型的死信,如果有,直接入库,然后尝试消费当前第一条,成功就继续,失败则终止。(同步的是人员、机构等数据,对顺序性要求高)
3. 开始撸代码 – 客户端
3.1 创建一个spring boot 项目引入依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.chinacoal.microservice</groupId>
<artifactId>common-utils</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
@Autowired
private QueueConfig queueConfig;
@RabbitListener(exclusive = false,bindings=@QueueBinding(
value=@Queue(autoDelete = "false",durable = "true",value = "#{queueConfig.getName()}",arguments = {
@Argument(name = "x-dead-letter-exchange", value = "#{queueConfig.getDLX_EXCHANGE_NAME()}"),
@Argument(name = "x-dead-letter-routing-key", value ="#{queueConfig.getROUTE_KEY()}"),
@Argument(name = "x_message_ttl", value = "30000")
}),exchange=@Exchange(value = "orgFanoutExchange", durable = "true",type = ExchangeTypes.FANOUT,autoDelete = "false")))
public void receiveMessage(String receiveMessage, Message message, Channel channel) throws Exception {
// 手动签收
log.info("接收到消息:[{}]", receiveMessage);
//执行业务逻辑
try {
if(doWork(receiveMessage,message)) {
//throw new FileUploadException();
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}else {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
}
} catch (Exception e) {
log.error("消息签收失败:", e);
Integer count = Integer.valueOf(String.valueOf(message.getMessageProperties().getHeaders().get("count")));
if(count >= 3) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
}else {
message.getMessageProperties().getHeaders().put("count", count + 1);
throw e;
}
}