RocketMQ implements a simple seckill interface

Preset scene:

The word "seckill" mostly appears in shopping, but it's not just shopping. For example, 12306 ticket purchase and school grabbing classes (the pain of college students) can also be regarded as a seckill. Spike should be a "three highs", which does not refer to high blood fat, high blood pressure and high blood sugar. Instead, it refers to "high concurrency", "high performance" and "high availability".

Assuming that there are 100 items in stock that need to be snapped up, you can try MQ for peak shaving to prevent downtime.
insert image description here
insert image description here

1. Create a rocketmq server module.

1.1. Configuration related files

  1. springboot2.6.13Version
    insert image description here
  2. xmlrely
 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- rocketmq -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.2.3</version>
        </dependency>

    </dependencies>
  1. application.propertiesrelated configuration
#应用名
spring.application.name=seckill-server
server.port=8081
rocketmq.producer.groupName=${spring.application.name}
# mq的nameserver地址
rocketmq.producer.namesrvAddr=127.0.0.1:9876

1.2. Controller code

  1. Here I use 1000 redundant quantities to control the number of interface accesses. Normally, middleware should be used to synchronize the real inventory, which I omitted here.
  2. My business logic here is relatively simple, and you can change the logic according to your own needs.
@RestController
public class OpenOrderController{
    
    
    int redundancy = 1000;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @GetMapping("/secKill")
   public String secKill(String id){
    
    
        redundancy--;
        if(redundancy > 0){
    
    
            rocketMQTemplate.convertAndSend("seckill-topic", id);
            return id+"正在抢购中请等待";
        }else{
    
    
            return "商品已售完";
        }
        
   }
}

2. Create rocketmq-consumer module

1.1. Configuration related files

  1. xml dependency configuration
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.2.3</version>
        </dependency>
  1. application.propertiesrelated configuration
spring.application.name=seckill-consumer
server.port=8082
rocketmq.consumer.group=${spring.application.name}
rocketmq.name-server=127.0.0.1:9876

1.2. Controller code

Here is the real message processing, and the listening processing of springboot greatly simplifies the configuration process of the listener.
Let's set the inventory here as a simple one 成员变量. In fact, it is possible to use synchronous real inventory in distributed projects redis.

In a real scenario, we can perform authentication in this step, whether it is a target user (black account), generate an order, send a text message (call back the execution result), and other operations. Since the traffic peak clipping has been performed by MQ, more operations can be performed in this step, and the business logic can be executed in an orderly manner.

Here is the sample code:

@Component
@RocketMQMessageListener(topic = "seckill-topic", consumerGroup = "seckill-consumer-group")
public class SeckillConsumer implements RocketMQListener<String> {
    
    
    int realInventory = 100;
    @Override
    public void onMessage(String id) {
    
    
        // 处理秒杀请求
        // 执行库存扣减和订单生成等操作
        // 返回秒杀结果给用户
        realInventory--;
        if(realInventory >= 0){
    
    
            System.out.println("当前商品剩余"+realInventory);
            System.out.println(id + "抢到商品");
        }else{
    
    
            System.out.println("商品已售完");
        }

    }
}

3. Create a test example

  1. Use 1w threads to send 1w requests for interface testing.
public class HttpTest {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService executorService = Executors.newFixedThreadPool(1000);
        for (int i = 0; i < 10000; i++) {
    
    
            executorService.execute(() -> {
    
    
                try {
    
    
                    URL url = new URL("http://localhost:8081/secKill?id=" + UUID.randomUUID().toString());
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.connect();
                    int responseCode = connection.getResponseCode();
                    if (responseCode == HttpURLConnection.HTTP_OK) {
    
    
                        System.out.println("Request success!");
                    } else {
    
    
                        System.out.println("Request failed!");
                    }
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
}

  1. Test Results
    Test Code Console
    insert image description here
    Producer Console
    insert image description here
    Consumer Console
    insert image description here

Here throws a question?
Why is there out-of-order consumption of messages? How to achieve sequential consumption?

Answer: springboot is consumed by asynchronous multi-threading by default, and the order cannot be guaranteed.
consumeMode = ConsumeMode.ORDERLYThe role of ConsumeMode.ORDERLY is to allow consumers to receive messages sequentially in a single thread, thereby ensuring the global order of messages

Guess you like

Origin blog.csdn.net/faker1234546/article/details/130915258