Ubuntu20.04にRabbitMQをインストールする
RabbitMQ公式サイト:https://www.rabbitmq.com/
1. インストール前の準備
sudo apt-get update -y
sudo apt-get install curl gnupg -y
2. RabbitMQ 署名キーをインストールします
curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo apt-key add -
3.適切なHTTPSトランスポートをインストールする
sudo apt-get install apt-transport-https
4.最新の RabbitMQ および Erlang バージョンを提供する Bintray リポジトリを追加します
/etc/apt/sources.list.d ディレクトリに bintray.erlang.list ファイルを作成し、ファイルに次の内容を入力します (ここでは Ubuntu20.04、Erlang バージョン 23.x を例として取り上げます)。
deb https://dl.bintray.com/rabbitmq-erlang/debian focal erlang-23.x
ここでファイルに入力する内容はUbuntuとErlangのバージョンによって異なりますので、バージョンの関係と選択については以下に紹介します。
上記入力内容において、focusはUbuntu20.04を表しており、具体的な対応関係は以下の通りです
このうち、erlang-23.x は Erlang の 23.x バージョンを表します。
ファイルを保存して終了します
5. Erlang パッケージをインストールする
sudo apt-get update -y
sudo apt-get install -y erlang-base
erlang-asn1 erlang-crypto erlang-eldap erlang-ftp erlang-inets
erlang-mnesia erlang-os-mon erlang-parsetools erlang-public-key
erlang-runtime-tools erlang-snmp erlang-ssl
erlang-syntax-tools erlang-tftp erlang-tools erlang-xmerl
有効なパッケージを指定してください
sudo apt-get update -y
/etc/apt/preferences.d ディレクトリに新しい erlang ファイルを作成し、次の内容を入力します。
Package: erlang*
Pin: release o=Bintray
Pin-Priority: 1000
次のコマンドを実行します
sudo apt-cache policy
/etc/apt/preferences.d ディレクトリ内の erlang ファイルを次の内容に変更します (ここでは erlang はバージョン 23.0.3-1 を選択します)
Package: erlang*
Pin: version 1:23.0.3-1
Pin-Priority: 1000
Package: esl-erlang
Pin: version 1:22.3.4.1
Pin-Priority: 1000
6. /etc/apt/preferences.d/ ディレクトリ内の Rabbitmq ファイルを変更し、次の内容を追加します。
パッケージ: Rabbitmq-server
ピン: バージョン 1:3.8.7
ピン優先度: 1000
7.RabbitMQをインストールする
次のコマンドを実行します
sudo apt-get update -y
sudo apt-get install rabbitmq-server -y --fix-missing
8. RabbitMQ のインストールを確認する
起動
//启动管理界面和外部监控系统
sudo rabbitmq-plugins enable rabbitmq_management
// 启动RabbitMQ
sudo service rabbitmq-server start
9. ページにアクセスして効果を確認します
ブラウザで http://localhost:15672/ にアクセスします。
ここではキューを確立しているため、パスワードとユーザー名がゲストになっており、メッセージが表示されます。
9. RabbitMQ で使用される 4 つのコマンドをまとめると次のようになります。
//
sudo サービスを開始します Rabbitmq-server start
//
sudo サービスを再起動します Rabbitmq-server restart
//
sudo サービスを停止します Rabbitmq-server stop
// ステータスを表示します
sudo service Rabbitmq-server status
go言語統合rabbitmq(手動ack)
準備ができたバッグ
github.com/streadway/amqpにあるパッケージを使用します。ここでは gomod の形式でパッケージを管理しているので、mod モードに応じてパッケージをダウンロードします。
プロデューサー、プロデューサー.go
package main
import (
"github.com/streadway/amqp"
"log"
"reflect"
"strconv"
"time"
)
//初始化全局变量,方便复用
var connection *amqp.Connection
var ch *amqp.Channel
var queue amqp.Queue
var c chan int
var confirms chan amqp.Confirmation
/**
初始化函数,默认在main方法前执行
*/
func init() {
var err error
//建立连接
connection, err = amqp.Dial("amqp://guest:guest@ip:5672/")
if nil != err {
log.Fatalf("connect the rabbitmqProducer error: %s",err)
}
//设置通道
ch, err = connection.Channel()
if nil != err {
log.Fatalf("connect the channel error: %s",err)
}
//开启确认机制
ch.Confirm(false)
confirms = ch.NotifyPublish(make(chan amqp.Confirmation, 1)) // 处理确认逻辑
//定义队列
queue, _ = ch.QueueDeclare("guet-block-1",false,false,
false,false,nil)
if nil != err {
log.Fatalf("create the queue error: %s",err)
}
}
/**
因为channel通道隔一段时间会被关闭,需要重连,否则手动ack会报错。
*/
func reconnectRabbitmq() () {
var err error
//建立连接
connection, err = amqp.Dial("amqp://guest:guest@ip:5672/")
if nil != err {
log.Fatalf("connect the rabbitmqProducer error: %s",err)
}
//设置通道
ch, err = connection.Channel()
if nil != err {
log.Fatalf("connect the channel error: %s",err)
}
//开启确认机制
ch.Confirm(false)
// 处理确认逻辑
confirms = ch.NotifyPublish(make(chan amqp.Confirmation, 1))
//定义队列
queue, _ = ch.QueueDeclare("guet-block-1",false,false,
false,false,nil)
if nil != err {
log.Fatalf("create the queue error: %s",err)
}
}
func main(){
//模拟阻塞
c = make(chan int)
go producer("")
<- c
}
/**
msg是需要的消息内容,可以是json或其他内容。入参可以加上发送内容的类型,如:ContentType: "text/plain"
*/
func producer(msg string) {
//此处msg均写死。
for i := 0; i < 300; i++ {
msg = strconv.Itoa(i)
//通过反射拿到channel结构体的closed字段值
closed := getClosed(*ch)
if closed == 1 {
log.Printf("重连。。。。。。。。。。。。。。。。。")
//0:channel未关闭,1:channel已关闭,为了不报错,我们重新建立连接
reconnectRabbitmq()
}
//发布消息
//exchange, key string, mandatory, immediate bool, msg Publishing
err :=ch.Publish("",queue.Name,false,false,amqp.Publishing{
Body: []byte(msg),
})
if(err != nil){
log.Panic("send mesg error %v",msg)
}
// 生产者是否confirm成功
isSuccess := confirmOne(confirms)
//未成功需要重新调用
if ! isSuccess{
//log.Printf("重发======================== ",msg)
//通过反射拿到channel结构体的closed字段值
closed := getClosed(*ch)
if closed == 1 {
log.Printf("重连。。。。。。。。。。。。。。。。。")
//0:channel未关闭,1:channel已关闭,为了不报错,我们重新建立连接
reconnectRabbitmq()
}
//发布消息
//exchange, key string, mandatory, immediate bool, msg Publishing
err :=ch.Publish("",queue.Name,false,false,amqp.Publishing{
Body: []byte(msg),
})
if(err != nil){
log.Panic("send mesg error %v",msg)
}
}
log.Printf("发送的数据为:%s ",msg)
//间隔三秒发一次信息
time.Sleep(time.Duration(3)*time.Second)
}
c <- 0
}
func getClosed(ch amqp.Channel) int64 {
d :=reflect.ValueOf(ch)
// 根据名字查找字段并转换为Int64
i := d.FieldByName("closed").Int()
return i
}
// 消息确认
func confirmOne(confirms <-chan amqp.Confirmation) bool{
if confirmed := <-confirms; !confirmed.Ack {
//log.Printf("confirmed delivery with delivery tag: %d", confirmed.DeliveryTag)
return false
}
return true
}
消費者 Consumer.go
package main
import (
"github.com/streadway/amqp"
"log"
"reflect"
)
//初始化全局变量,方便复用
var connection *amqp.Connection
var ch *amqp.Channel
var queue amqp.Queue
var consumerMsg <-chan amqp.Delivery
/**
初始化函数,默认在main方法前执行
*/
func init() {
var err error
//建立连接
connection, err = amqp.Dial("amqp://guest:guest@ip:5672/")
if nil != err {
log.Fatalf("connect the rabbitmqConsumer error: %s",err)
}
//设置通道
ch, err = connection.Channel()
if nil != err {
log.Fatalf("connect the channel error: %s",err)
}
//定义队列
queue, _ = ch.QueueDeclare("guet-block",false,false,
false,false,nil)
if nil != err {
log.Fatalf("create the queue error: %s",err)
}
consumerMsg, err = ch.Consume(queue.Name, "", false,
false, false, false, nil)
if nil != err {
log.Fatalf("receive error %v", err)
}
}
//因为channel通道隔一段时间会被关闭,需要重连,否则手动ack会报错。
func reconnectRabbitmq() () {
var err error
//建立连接
connection, err = amqp.Dial("amqp://guest:guest@ip:5672/")
if nil != err {
log.Fatalf("connect the rabbit error: %v",err)
}
//defer connection.Close()
//设置通道
ch, err = connection.Channel()
if nil != err {
log.Fatalf("get connect fail: %v",err)
}
//defer ch.Close()
//定义队列
queue, _ = ch.QueueDeclare("guet-block", false, false,
false, false, nil)
//获取消息
consumerMsg, err = ch.Consume(queue.Name, "", false,
false, false, false, nil)
if nil != err {
log.Fatalf("receive error %v", err)
}
}
func main() {
// 创建一个channel
c := make(chan int)
log.Println("正在异步请求 consumer")
go consumer()
<- c
}
/**
消费者
*/
func consumer() {
//无限监听
for {
select {
case msg := <-consumerMsg:
//channel被强制关闭,需要重新建立连接
//通过反射拿到channel结构体的closed字段值
closed := getClosed(*ch)
if closed == 1 {
//0:channel未关闭,1:channel已关闭,为了不报错,我们重新建立连接
reconnectRabbitmq()
}else {
log.Printf("接受到的数据为 :%s\n",msg.Body)
//手动ack,false:代表只对当前消息ack,ture,对这条消息前的所有的消息都ack
if err := msg.Ack(false); err != nil {
log.Fatalf("hand ack fail msg: ",err)
}
}
}
}
}
/**
反射获取channel关闭位标识位
*/
func getClosed(ch amqp.Channel) int64 {
d :=reflect.ValueOf(ch)
// 根据名字查找字段并转换为Int64
i := d.FieldByName("closed").Int()
return i
}
シミュレーション効果: