小白如何在nestjs上使用RabbitMq

語言: CN / TW / HK

起因

專案寫著寫著發現出現了服務與服務之間出現了迴圈依賴的情況,而且大有愈演愈烈的趨勢,雖然說nest在框架中有提供這種情況下的預備措施,但服務與服務之間存在太多的迴圈依賴總歸不是一個好的現象,對未來可能發生的微服務拆分會造成很大的麻煩,趁著現在專案還不算太複雜,及時解決這個問題避免未來帶來更大的維護成本才是當務之急。

訊息佇列選型

在選用RabbitMq之前,其實也有考慮其他型別的訊息佇列,甚至考慮過要不要使用Redis的訊息佇列功能,畢竟Redis未來也是可能引入到專案中替代原生快取的,在參考了一些文章之後,一篇訊息佇列詳解最後讓我打定了主意使用RabbitMq,裡面有這麼一段話: 中小型軟體公司,建議選RabbitMQ,一方面,erlang語言天生具備高併發的特性,而且他的管理介面用起來十分方便。 他的弊端也在這裡,雖然RabbitMQ是開源的,然而國內有幾個能定製化開發erlang的程式設計師呢? 所幸,RabbitMQ的社群十分活躍,可以解決開發過程中遇到的bug,這點對於中小型公司來說十分重要。 不考慮RocketMQ和kafka的原因是,一方面中小型軟體公司不如網際網路公司,資料量沒那麼大,選訊息中介軟體,應首選功能比較完備的,所以kafka排除。 不考慮RocketMQ的原因是,RocketMQ是阿里出品,如果阿里放棄維護RocketMQ,中小型公司一般抽不出人來進行RocketMQ的定製化開發,因此不推薦。 作為一家中小型軟體公司的開發,顯然我是不想冒著出了bug還要看中介軟體原始碼的風險來使用一樣東西的,而且誰能拒絕遇到的問題都能從網上找到答案呢

RabbitMq 啟動

在使用RabbitMq之前,我先看了一下nest的官方文件中微服務的部分(當然,如果你喜歡英文文件,咱也不攔著)。說實話,這篇文件至少對我來說是相當的不友好,這也是我寫這篇文章的原因,照著這篇文件真的很難把整個流程跑通,因為很多地方他只顧著自己寫得爽了,完全也沒考慮到小白的感受。當然,如果你是大佬覺得有這篇官方文件就夠了的,那當咱沒說,請自便。

而如果你有興趣,能跟著我把這些都配置完,至少我能保證你可以在nest框架中做到簡單的訊息傳送和接收。既然我走了彎路,那儘量把路給後來的小白們都掰直了。

安裝RabbitMq

差點忘了說RabbitMq的安裝,這個其實沒什麼好提的,但其實這上面我也遇到了一點點問題,所以還是提一下。在官網選擇自己需要下載的版本(這裡本來是打算截圖講解一下怎麼下載,但想想還是算了,一方面感覺下載介面可能會發生變動,反而造成困擾;二方面,我相信各位的實力可以找到的)。下載完成後會得到一個安裝包

image.png

在安裝的過程中會讓你選擇安裝目錄或者如果沒安裝erlang的話會讓你安裝,$\color{red}{安裝過程中請使用預設路徑,謝謝}$,我不知道為什麼當我修改了軟體目錄之後安裝完成之後會提示開啟失敗,如果你不想遇到一些奇奇怪怪的問題的話,建議在這一步把你的整理癖收一收,使用預設路徑安裝。

安裝nest依賴

文件中提到這麼一句話:

image.png

如果你照做了的話,會發現在接下來的使用過程中,你會有很多找不到的類,因為下面還有一句話:

image.png

所以完整的依賴應該是: $ npm i --save @nestjs/microservices $ npm i --save amqplib amqp-connection-manager 不光是Transport,本文中提到的所有的類都是來自於@nestjs/microservices(我在用這個包的時候裡面所有的類都不會自動補全,都要手動import,知道要怎麼弄的小夥伴也可以教我一下),所以我也不清楚amqplib有什麼作用,如果有知道的小夥伴可以告訴我一下。

main.ts

image.png

不知道有多少小夥伴在這一步就卡住了的,因為main.ts中,app是有定義的(nest建立的時候就定義了啊,你可別說你是徒手敲nest),特別是像我這種替換了日誌元件的,必不可能把app初始化方式換了

image.png

但是也不用著急,如果你正常引入了上面的依賴,你的app物件中應該會出現connectMicroservice方法,來跟著我打: ```ts import { MicroserviceOptions, Transport } from '@nestjs/microservices';

// app的初始化我跳過了 app.connectMicroservice({ // 型別:rabbitmq transport: Transport.RMQ, options: { // rabbitmq地址 urls: ['amqp://localhost:5672'], // 佇列名稱 queue: 'cats_queue', queueOptions: { // 訊息是否持久化 durable: false }, } }) app.startAllMicroservices(); `` 這一條會給你的服務增加一條全域性的連結,用來監聽訊息用的,沒有這個你在下面的@MessagePattern`啥也不是(當然,如果有更好的方法評論區也可以指導一下哈)。

發訊息

可能是寫文件的大佬覺得這實在是太簡單了,以至於壓根就沒寫在文件裡,文件裡能用作參考的有這麼一段

image.png

確實是這樣,但其實沒那麼麻煩,首先需要在你的module中引入這麼一段: ts ClientsModule.register([ { name: 'MATH_SERVICE', transport: Transport.RMQ, options: { urls: ['amqp://localhost:5672'], queue: 'cats_queue', queueOptions: { durable: false }, }, } ])

image.png

其中最需要關注的是name屬性,因為在service中,你需要自動注入連線(就是文件中提到了但沒講怎麼來的那個this.client)時用的就是這個屬性中的值。簡單來說就是這樣: ```ts constructor(@Inject("MATH_SERVICE") private readonly client: ClientProxy) { }

async create() { const record = "hello"; // send中第一個引數對應的是接收方法的監聽欄位,也可以放物件,但接收的時候也必須是相同的物件,第二個引數對應的是傳送的內容,如果需要在headers中增加內容的話可以參考官方文件 const result = this.client.send('test', record).subscribe(); console.log("result", result); return "訊息傳送成功"; } ```

image.png

收訊息

收訊息就更簡單了,咱們一筆帶過,這樣: ts // 這裡的'test'對應的是send裡的第一個欄位 @MessagePattern('test') test(@Payload() data: string) { console.log("get message", data); }

image.png

就可以看到發出來的訊息了 image.png

監控訊息

這個部分我不太想詳談,因為其他有比我講得好得多的文章,大家可以自行查閱,我就不多班門弄斧了,大家可以瞭解一下RabbitMq自帶的監控平臺,預設在15672埠。

結束語

大概就這樣,起步是可以正常起步了,還有更多的功能比如說廣播啊,或者多佇列監聽啊之類的配置目前還在探索,以後有機會再更新吧,如果看的人少的話也可能就不更新了,我這人也是挺懶的...