rabbitmq series (three) idempotent message processing

A, springboot integration rabbitmq

  1. We need to create two projects, as a producer, and the other as a consumer. Add amqp depend in pom.xml:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
  1. Add information rabbitmq in application.yml file:
spring:
  rabbitmq:
    # 连接地址
    host: 127.0.0.1
    # 端口
    port: 5672
    # 登录账号
    username: guest
    # 登录密码
    password: guest
    # 虚拟主机
    virtual-host: /
  1. In the new producer project CI rabbitmqConfig.java, affirmed entitled "byte-zb" and switch directly connected to the queue, using the "byte-zb" the routing-key switch queues and binding, as follows:
@Configuration
public class RabbitConfig {

    public static final String QUEUE_NAME = "byte-zb";

    public static final String EXCHANGE_NAME = "byte-zb";

    public static final String ROUTING_KEY = "byte-zb";

    // 队列申明
    @Bean
    public Queue queue(){
        return new Queue(QUEUE_NAME);
    }

    // 申明交换机
    @Bean
    public DirectExchange directExchange(){

        return new DirectExchange(EXCHANGE_NAME);
    }

    // 数据绑定申明
    @Bean
    public Binding directBinding(){

        return BindingBuilder.bind(queue()).to(directExchange()).with(ROUTING_KEY);
    }
}
  1. Create producer sends a message, as follows:
@RestController
public class Producer {
    public static final String QUEUE_NAME = "byte-zb";

    public static final String EXCHANGE_NAME = "byte-zb";

    @Autowired
    private AmqpTemplate amqpTemplate;

    @RequestMapping("/send")
    public void sendMessage(){

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("email","11111111111");
        jsonObject.put("timestamp",System.currentTimeMillis());
        String json = jsonObject.toJSONString();
        System.out.println(json);
        amqpTemplate.convertAndSend(EXCHANGE_NAME,QUEUE_NAME,json);
    }

}
  1. Create a message consumer spending in the consumer project, the code is as follows:
@Component
public class Consumer throws Exception{

    public static final String QUEUE_NAME = "byte-zb";

    @RabbitListener(queues = QUEUE_NAME)
    public void receiveMessage(String message){

        System.out.println("接收到的消息为"+message);
    }
}

We start the producer, and then send the request interface, and then turn on the console rabbitmq find many switches and queue named "byte-zb", and there was a queue of non-consumption message, and then start the consumers, we will found prints a message on the console, and the console rabbitmq "byte-zb" no message queue.

Second, the automatic compensation mechanism

If consumer news consumption is not successful, what happens then? We'll revise the consumer code and see.

@Component
public class Consumer {

    public static final String QUEUE_NAME = "byte-zb";

    @RabbitListener(queues = QUEUE_NAME)
    public void receiveMessage(String message) throws Exception {

        System.out.println("接收到的消息为"+message);
        int i = 1 / 0;
    }
}

We will see consumers engineering console has been refreshed error, when a consumer with an abnormal, that is to say when the message consumer is unsuccessful, the message will be stored in the server rabbitmq, has been retried, do not throw up abnormal so far.

If you have been throwing exceptions, our service is very easy to hang, and that there is no way to control the retry several unsuccessful will not retry it? The answer is yes. We have added some configuration in the consumer application.yml.

spring:
  rabbitmq:
    # 连接地址
    host: 127.0.0.1
    # 端口
    port: 5672
    # 登录账号
    username: guest
    # 登录密码
    password: guest
    # 虚拟主机
    virtual-host: /
    listener:
      simple:
        retry:
          enabled: true # 开启消费者进行重试
          max-attempts: 5 # 最大重试次数
          initial-interval: 3000 # 重试时间间隔

Means disposed above the abnormal consumption, five retries, each compartment 3s. Consumers continue to start to see results, we found that after five retries, it is no longer a retry.

Third, the actual case to use messaging compensation mechanism

In fact, no matter how abnormal retry will be successful like the kind of situation that appears above, in fact, be used to compensate the message is to call such third-party interface.

Case: living to throw a message queue, it contains the mailbox and send content. Consumers call interface to send e-mail messages will get the message. Mail may sometimes be due to network interfaces such as blocked, this time on the need to re-tried.

In the call interface tools, if an exception occurs we direct return null , tools specific code is not posted, how to deal with it then if it returns null? We just need to throw an exception, rabbitListener Exception caught after it automatically retries.

We transform what consumers Code:

@Component
public class Consumer {

    public static final String QUEUE_NAME = "byte-zb";

    @RabbitListener(queues = QUEUE_NAME)
    public void receiveMessage(String message) throws Exception {

        System.out.println("接收到的消息为"+message);
        JSONObject jsonObject = JSONObject.parseObject(message);
        String email = jsonObject.getString("email");
        String content = jsonObject.getString("timestamp");

        String httpUrl = "http://127.0.0.1:8080/email?email"+email+"&content="+content;
        // 如果发生异常则返回null
        String body = HttpUtils.httpGet(httpUrl, "utf-8");
        //
        if(body == null){
            throw new Exception();
        }
    }
}

Of course, we can customize exception is thrown. Specifically how to test it, the first step in starting producers and consumers, this time we found that consumers in the retry, the second step we start the mail service, this time we will find that the message sent is successful, the consumer not retry .

Fourth, to solve the problem of power and other news

Some students new to java may not be clear to idempotency. Idempotence is repeated consumption caused by inconsistent results. In order to ensure idempotency, so consumers can only consume messages consume a message. What can I use to control the overall message id idempotency. When the message is that we can choose to consume this cache to save the message id, and then again when consumption, we can query cache, if there is news id, we can be pretty good handle direct return. First transformation producer code, add a message id in the message:

@RequestMapping("/send")
    public void sendMessage(){

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("email","11111111111");
        jsonObject.put("timestamp",System.currentTimeMillis());
        String json = jsonObject.toJSONString();
        System.out.println(json);

            Message message = MessageBuilder.withBody(json.getBytes()).setContentType(MessageProperties.CONTENT_TYPE_JSON)
                .setContentEncoding("UTF-8").setMessageId(UUID.randomUUID()+"").build();
        amqpTemplate.convertAndSend(EXCHANGE_NAME,QUEUE_NAME,message);
    }

Consumer Code reform:

@Component
public class Consumer {

    public static final String QUEUE_NAME = "byte-zb";

    @RabbitListener(queues = QUEUE_NAME)
    public void receiveMessage(Message message) throws Exception {

        Jedis jedis = new Jedis("localhost", 6379);

        String messageId = message.getMessageProperties().getMessageId();
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println("接收导的消息为:"+msg+"==消息id为:"+messageId);

        String messageIdRedis = jedis.get("messageId");

        if(messageId == messageIdRedis){
            return;
        }
        JSONObject jsonObject = JSONObject.parseObject(msg);
        String email = jsonObject.getString("email");
        String content = jsonObject.getString("timestamp");

        String httpUrl = "http://127.0.0.1:8080/email?email"+email+"&content="+content;
        // 如果发生异常则返回null
        String body = HttpUtils.httpGet(httpUrl, "utf-8");
        //
        if(body == null){
            throw new Exception();
        }
        jedis.set("messageId",messageId);
    }
}

We use it in consumer redis store the message id, only the presentation, specific projects please select the appropriate tool to store the actual situation.

If the article helpful, please remember to focus on points like yo ~
Welcome to my public concern number: Byte legend, technical articles for daily push them to learn.

Guess you like

Origin www.cnblogs.com/zhixie/p/12204095.html