rabbitmq消息中间件的案例

MQ 专栏收录该内容
1 篇文章 0 订阅

GitHub地址:https://github.com/hsowan/rabbitmq-demo

This demo will involve RabbitMQ and SpringAMQP.

Application

  • SecKill simulation
  • Reliable delivery

SecKill

Now, there is only one goods which is MacBook Pro and one hundred users want to get it, ok,
the only one MacBook Pro will be free after n minutes and all users can SecKill it. Let`s
realize it.

Using ThreadPoolExecutor to fake 100 users

/**
 * int corePoolSize,
 * int maximumPoolSize,
 * long keepAliveTime,
 * java.util.concurrent.TimeUnit unit,
 * java.util.concurrent.BlockingQueue<Runnable> workQueue
 */
private ThreadPoolExecutor threadPoolExecutor = 
        new ThreadPoolExecutor(100, 1000, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10));

Expose problem in SecKill situation

// Create 100 thread waiting for the SecKill
for (int i = 1; i <= 100; i++) {
    threadPoolExecutor.execute(() -> {
        while (true) {
            if (System.currentTimeMillis() >= startTime) {

                Timestamp createAndUpdateTime = new Timestamp(System.currentTimeMillis());

                String messageId = UUID.randomUUID().toString();

                // Create an order
                orderService.saveOrUpdate(new Order("order-macbookpro", OrderConstants.CREATING, createAndUpdateTime, createAndUpdateTime, messageId));

                // If count > 0, switch the status of an order to DONE
                Goods goods = goodsService.getById((long) 1);
                if (goods.getCount() > 0){

                    Order order = orderService.getByMessageid(messageId);
                    if (order != null){
                        order.setStatus(OrderConstants.DONE);
                        order.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                        orderService.saveOrUpdate(order);
                    }

                    goods.setCount(goods.getCount() - 1);
                    goodsService.saveOrUpdate(goods);
                }
                break;
            }
        }
    });
}

What will happen?

mysql> select count(1) from test_order where status = 1;
+----------+
| count(1) |
+----------+
|      100 |
+----------+
1 row in set (0.00 sec)

All users got the only one MacBook Pro!!!

Solve it with using RabbitMQ

// Create 100 thread waiting for the SecKill
for (int i = 1; i <= 100; i++) {
    threadPoolExecutor.execute(() -> {
        while (true) {
            if (System.currentTimeMillis() >= startTime) {

                Timestamp createAndUpdateTime = new Timestamp(System.currentTimeMillis());

                String messageId = UUID.randomUUID().toString();

                Order order = new Order("order-macbookpro", OrderConstants.CREATING, createAndUpdateTime, createAndUpdateTime, messageId);

                // Create an order
                orderService.saveOrUpdate(order);

                orderProducer.sendMessage(order);
                break;
            }
        }
    });
}

This is the OrderProducer that sends messages.

public void sendMessage(Order order){
    Long time = System.currentTimeMillis();
    Timestamp createAndUpdateTime = new Timestamp(time);
    Timestamp nextRetry = new Timestamp(time + 60000 * MessageConstants.INTERVAL);

    Message message = new Message(JSON.toJSONString(order), MessageConstants.SENDING, 0, nextRetry, createAndUpdateTime, createAndUpdateTime);

    rabbitTemplate.setConfirmCallback((correlationData, b, s) -> {
        if (b){
            System.out.println("ack");
        }else {
            System.out.println("nack");
        }
    });
    CorrelationData correlationData = new CorrelationData();
    rabbitTemplate.convertAndSend("order-exchange", "order.secKill", JSON.toJSONString(message), correlationData);
}

This is the OrderConsumer that mostly waits to receive messages.

@RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "order-queue", durable = "true"),
        exchange = @Exchange(name = "order-exchange", type = "topic"),
        key = "order.*"
))
@RabbitHandler
public void onMessage(@Payload String message, @Headers Map<String, Object> headers, Channel channel) throws IOException {
    Message m = JSON.parseObject(message, Message.class);
    System.out.println(m.toString());

    String messageId = JSON.parseObject(m.getBody(), Order.class).getMessageId();

    Goods goods = goodsService.getById((long) 1);
    Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
    int count = goods.getCount();
    if (count > 0){
        goods.setCount(count - 1);
        goodsService.saveOrUpdate(goods);

        Order order = orderService.getByMessageid(messageId);
        order.setStatus(OrderConstants.DONE);
        order.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        orderService.saveOrUpdate(order);

    }
    channel.basicAck(deliveryTag, false);
}

Make sure that there is a single RabbitMQ server running background.
Then run the consumer (run Application.java in client module).
Finally, run the producer (run the testUsingRabbitMQ test of the ApplicationTests.java in server module).

mysql> select count(1) from test_order where status = 1;
+----------+
| count(1) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

Cool!

AMQP

Core Concepts

  • Server
  • Connection
  • Channel(like session in jdbc)
  • Message(properties & body)
  • Virtual host
  • Exchange
  • Binding
  • Routing key
  • Queue

AMQ Model

在这里插入图片描述

Message Flow

在这里插入图片描述

Tutorial

Hello World

  • message broker
  • producer
  • consumer
  • queue

在这里插入图片描述

Work Queues

  • multiple workers
  • round-robin dispatching
  • message acknowledgment (ack)
  • message durability
  • fair dispatch
  • prefetch
  • qos

在这里插入图片描述

Publish/Subscribe

The core idea in the messaging model in RabbitMQ is that the producer never sends any messages directly to a queue.
Actually, quite often the producer doesn’t even know if a message will be delivered to any queue at all.

  • exchange
  • exchange type (direct, topic, headers, fanout)
  • broadcast
  • temporary queue (non-durable, exclusive, autodelete, random queue name)
  • binding
  • routingKey (its value is ignored for fanout exchanges)

在这里插入图片描述

在这里插入图片描述

Routing (Direct)

A binding is a relationship between an exchange and a queue.
This can be simply read as: the queue is interested in messages from this exchange.

The routing algorithm behind a direct exchange is simple - a message goes to the queues whose binding key exactly matches the routing key of the message.

  • binding key (routingKey)

在这里插入图片描述

Topics

Messages sent to a topic exchange can’t have an arbitrary routing_key - it must be a list of words, delimited by dots.
The words can be anything, but usually they specify some features connected to the message.
A few valid routing key examples: “stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit”.
There can be as many words in the routing key as you like, up to the limit of 255 bytes.

  • * (star) can substitute for exactly one word.
  • # (hash) can substitute for zero or more words.

Remote procedure call (RPC)

  • Callback queue
  • Correlation Id

在这里插入图片描述

Docker

$ docker run -d \
--name first-rabbit \
--hostname rabbitmq-demo \
-p 5672:5672 \
-p 8080:15672 \
-e RABBITMQ_DEFAULT_USER=user \
-e RABBITMQ_DEFAULT_PASS=password \
rabbitmq:3-management

You can then go to http://localhost:8080 or http://host-ip:8080 in a browser
and use user / password to gain access to the management console.

在这里插入图片描述

Documents

Blog

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值