小白入門微服務(2) - 訊息佇列初體驗
概述
- 前言
- 訊息佇列使用場景
- 什麼是訊息佇列
- 常用訊息佇列庫對比
- Kafka 初體驗
- RabbitMQ 初體驗
- 後記
前言
前面兩篇我們學習了
- 小白入門微服務(0) - 什麼是微服務
- 小白入門微服務(1) - RPC 初體驗
接下來我們來學習微服務中的非同步通訊 - 訊息佇列。在這篇文章的學習中,預設你已經掌握了 Docker、docker-compose 的知識。如果你還沒有掌握,可以翻閱我的歷史文章。如果我的文章對你有幫助,歡迎關注、點贊、轉發,這樣我會更有動力做原創分享。
訊息佇列的使用場景
非同步處理
場景描述:在正常的使用者註冊流程中,使用者註冊完成都需要,傳送郵件與簡訊,其中傳統情況下,有序列與並行兩種方式。
註冊成功之後,寫入資料庫,寫完之後,再發郵件,最後再發簡訊。總體耗時:
50 + 50 + 50 = 150 ms
註冊成功之後,寫入資料庫,並行發郵件和發簡訊。總體耗時:
50 + 50 = 100 ms
註冊成功之後,寫入資料庫,將寫入成功的資訊,傳送至訊息佇列,由於消費者自己去訊息佇列中取訊息,這樣在寫入訊息之後,即可成功返回。總體耗時:
50 + 5 = 55 ms
三個資料一對比,誰優誰劣就很明顯了
應用解耦
場景說明:使用者下單後,需要減去庫存系統中相應數量的庫存。
在傳統模式下(上圖),如果庫存系統出現錯誤,則訂單建立失敗,而且兩者過度耦合,對後面的新需求與維護也是相當大的挑戰。在原有的架構上進行升級,則有下圖:
- 訂單系統:使用者下單後,訂單系統進行資料持久化處理,然後將訊息寫入訊息佇列,返回訂單建立成功
- 庫存系統:訂閱訊息,獲取下單資訊,庫存系統根據訂單資訊,進行庫存操作。
當下,兩者就解耦了,庫存系統出現錯誤,也可以正常進行下單了。因為只是入門文章,應用場景就先介紹到這裡。更多使用場景請自行百度、Google。
什麼是訊息佇列
如圖,P 為 producer 生產者,C 為 consumer 消費者,紅色部分為訊息佇列。通俗地解釋一下訊息佇列,你想象一個場景:你到報社訂閱了一份報紙,報社每日生產一份新報紙,便將新報紙發往郵局並告訴郵局你的地址,郵遞員將你的報紙送往你的郵箱,你便可以愉快地閱讀今天的時事新聞了。當然,可能一個人訂閱了好幾家報社,一家報社也可以被多個人訂閱。在這個場景中,訊息佇列就擔任了,郵箱、郵局、郵遞員的角色。
常用訊息佇列庫對比
在網路上搜索到一個比較全面的對比圖,我這邊就直接引用原圖,不再造輪子了。原圖地址:(https://cloud.tencent.com/developer/article/1006035)
廣泛來說,電商、金融等對事務性要求很高的,可以考慮RabbitMQ和RocketMQ,對效能要求高的可考慮Kafka。
Kafka 初體驗
kafka 術語:
- Broker:Kafka 叢集包含一個或多個伺服器,這種伺服器被稱為 broker。
- Topic:每條釋出到 Kafka 叢集的訊息都有一個類別,這個類別被稱為 Topic。(物理上不同 Topic 的訊息分開儲存,邏輯上一個 Topic 的訊息雖然保存於一個或多個 broker 上,但使用者只需指定訊息的 Topic 即可生產或消費資料而不必關心資料存於何處)。
- Partition:Partition 是物理上的概念,每個 Topic 包含一個或多個 Partition。
- Producer:負責釋出訊息到 Kafka broker。
- Consumer:訊息消費者,向 Kafka broker 讀取訊息的客戶端。
- Consumer Group:每個 Consumer 屬於一個特定的 Consumer Group(可為每個 Consumer 指定 group name,若不指定 group name 則屬於預設的 group)。
安裝單節點 kafka
由於這裡只是入門,就只使用單節點了。這裡使用 Docker 來構建 kafka,其程式碼如下:docker-compose.yaml
version: '3'
services:
#<!--定義zk層服務-->
zookeeper:
image: wurstmeister/zookeeper
ports:
- "2181:2181"
#<!--定義Kafka層-->
kafka:
image: wurstmeister/kafka
depends_on: [ zookeeper ]
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_HOST_NAME: 123.45.567.89
KAFKA_CREATE_TOPICS: "test:1:1"
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
volumes:
- /var/run/docker.sock:/var/run/docker.sock
docker-compose up 啟動 kafaka。
nodejs 與 python 互調
我們先來看看,nodejs 為 producer ,python 為 comsumer 的情況:
再看看,python 為 producer,nodejs 為consumer 的情況:
程式碼展示
nodejs producer
var kafka = require('kafka-node'),
Producer = kafka.Producer,
KeyedMessage = kafka.KeyedMessage,
client = new kafka.KafkaClient({kafkaHost: '123.45.567.89:9092'}),
producer = new Producer(client),
payloads = [
{ topic: 'my_favorite_topic', messages: 'produce by nodejs', partition: 0 }
];
producer.on('ready', function () {
producer.send(payloads, function (err, data) {
console.log(data);
});
});
producer.on('error', function (err) {})
nodejs consumer
var kafka = require('kafka-node'),
Consumer = kafka.Consumer,
client = new kafka.KafkaClient({kafkaHost: '123.45.567.89:9092'}),
consumer = new Consumer(
client,
[
{topic: 'my_favorite_topic', partition: 0}
],
{
autoCommit: false
}
);
consumer.on('message', function (message) {
console.log(message);
});
python producer
from kafka import KafkaProducer
producer = KafkaProducer(bootstrap_servers=['123.45.567.89:9092'],
api_version=(0, 10, 1)
) # 此處ip可以是多個['0.0.0.1:9092','0.0.0.2:9092','0.0.0.3:9092' ]
for _ in range(100):
producer.send('my_favorite_topic', b'produce by python')
producer.close()
python consumer
from kafka import KafkaConsumer
consumer = KafkaConsumer('my_favorite_topic', bootstrap_servers=['123.45.567.89:9092'], api_version=(0, 10, 1)
for msg in consumer:
print(msg)
RabbitMq 初體驗
介紹過 Kafka 之後,RabbitMQ 就不在詳細介紹了,請看原始碼了。
後記
不知不覺,訊息佇列也講完了,要真真切切地區實踐,才能真正地掌握。
個人的知識儲備總是有限的,如有錯誤的地方,還請大佬斧正。點選閱讀原文,連結到我的知乎,我會在知乎上對文章錯誤的地方進行修改。
本篇文章首發於公眾號「zone7」,關注公眾號獲取最新推文,後臺回覆【小白微服務】獲取原始碼。