[Kubernetes] - RabbitMQ学习

1.消息队列

  • 消息: 在应用间传送的数据
  • 队列,先进先出

1.2. 作用

  • 好处:解耦, 容错,削峰
  • 坏处:降低系统可用性,系统复杂度提高,一致性问题;

RabbitMQ组成部分:生产者,消费者,队列,交换机;

2. 安装部署rabbitmq

---
apiVersion: v1
kind: Secret
metadata:
  name: rabbitmq-secret
  namespace: rabbitmq
data:
  username: YWRtaW4K
  password: MTIzNDU2Cg==
type: Opaque
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: rabbitmq
  namespace: rabbitmq
  labels:
    app: rabbitmq
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rabbitmq
  serviceName: rabbitmq-headless
  template:
    metadata:
      labels:
        app: rabbitmq
    spec:
      containers:
        - name: rabbitmq
          image: registry.cn-hangzhou.aliyuncs.com/yuanli123/rabbitmq:3.9.22-management
          ports:
            - name: tcp-5672
              containerPort: 5672
              protocol: TCP
            - name: tcp-15672
              containerPort: 15672
              protocol: TCP
#              不知道为什么自己使用的username会多出一个回车字符导致rabbitmq无法识别到
#          env:
#            - name: RABBITMQ_DEFAULT_USER
#              valueFrom:
#                secretKeyRef:
#                  name: rabbitmq-secret
#                  key: username
#            - name: RABBITMQ_DEFAULT_PASS
#              valueFrom:
#                secretKeyRef:
#                  name: rabbitmq-secret
#                  key: password
          resources:
            limits:
              cpu: '1'
              memory: '2Gi'
            requests:
              cpu: '200m'
              memory: '500Mi'
      imagePullSecrets:
        - name: regcred

---
apiVersion: v1
kind: Service
metadata:
  name: rabbitmq-headless
  namespace: rabbitmq
  labels:
    app: rabbitmq
spec:
  ports:
    - name: tcp-rabbitmq-5672
      port: 5672
      targetPort: 5672
      nodePort: 32672
  selector:
    app: rabbitmq
  type: NodePort
---
apiVersion: v1
kind: Service
metadata:
  name: rabbitmq-external
  namespace: rabbitmq
  labels:
    app: rabbitmq-external
spec:
  ports:
    - name: http-rabbitmq-external
      protocol: TCP
      port: 15672
      targetPort: 15672
  selector:
    app: rabbitmq
  type: ClusterIP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rabbitmq-ingress
  namespace: rabbitmq
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: rabbitmq.liyuan.com
      http:
        paths:
          - backend:
              service:
                name: rabbitmq-external
                port:
                  number: 15672
            pathType: Prefix
            path: /

根据上述yaml,再结合修改 /etc/hosts 文件
在这里插入图片描述
通过 http://rabbitmq.liyuan.com:30001/#/exchanges 访问
在这里插入图片描述
并暴露了 192,168.31.175:32672 用于发消息

2.1.名词解释

  • Broker: 接收和分发消息的应用
  • Virtual Host: 虚拟主机,一个Broker可以有多个Virtual Host, 每个Virtual Host都有自己一套的Exchange和Queue
  • Connection: 生产者/消费者和Broker之间的TCP链接
  • Channel: 发送消息的通道,channel是在connection内部建立逻辑链接,AMQP method包含了channel id帮助客户端和message Broker识别Broker,减少建立TCP Connection的开销;
  • Exchange:message到达broker的第一站,根据分发规则,查询表中的routing key,分发消息到queue中去,常用类型有:direct, topic, fanout(multicast)
  • Queue: 存放消息的队列
  • Binding:Exchange和Queue之间的虚拟链接,binding中可以包含routing key,Binding信息被保存到exchange中的查询表中,用于message的分发依据;

3.使用测试

rabbitmq-test 源码参考

3.1.pom.xml

# pom.yaml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>rqbbitmq-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.16.0</version>
        </dependency>
    </dependencies>
</project>

3.2.生产者Producer

// Producer.java
package com.liyuan.rabbitmq;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.31.175");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setPort(32672);
        try (Connection connection = connectionFactory.newConnection()) {
            Channel channel = connection.createChannel();
            String exchangeName = "xc_exchange_name";
            AMQP.Exchange.DeclareOk exchangeDeclare = channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true, false, null);
            String queueName = "xc_queue_name";
            AMQP.Queue.DeclareOk queueDeclare = channel.queueDeclare(queueName, false, false, false, null);
            channel.queueBind(queueName, exchangeName, queueName);

            String message = "Hello, my name is liyuan.";

            channel.basicPublish(exchangeName, queueName, null, message.getBytes());

            channel.close();
        }

    }
}

3.3.消费者Consumer

package com.liyuan.rabbitmq;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.31.175");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setPort(32672);
        try (Connection connection = connectionFactory.newConnection()) {
            Channel channel = connection.createChannel();
            String exchangeName = "xc_exchange_name";
            String queueName = "xc_queue_name";

            DeliverCallback deliverCallback = new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    System.out.println("Delivered consuming: " + consumerTag + " " + new String(message.getBody()));
                }
            };
            CancelCallback cancelCallback = new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    System.out.println("Canceled: " + consumerTag);
                }
            };
            channel.basicConsume(queueName, true, deliverCallback, cancelCallback);

            channel.close();
        }
    }
}

4.rabbitmq交换机类型

4.1. BuiltinExchangeType.DIRECT

路由键与队列完全匹配交换机,通过routingKey路由键将交换机和队列进行绑定,消息被发送到exchange时,根据消息的routingKey来进行匹配,只将消息发送到完全匹配此routingKey的队列;
且同一个key可以绑定多个queue,因此会同时将消息发给多个queue;

queueNameroutingKey
queue01“direct_key01”
queue02“direct_key02”

根据上述表格的规则来发送消息,当发送消息时的routingKey为以下值时,以下队列会收到消息;

routingKeyqueueName
“direct_key01”queue01 will receive
“direct_key02”queue02 will receive

4.2. BuiltinExchangeType.FANOUT

将消息分发给所有绑定了此交换机的队列;

queueNameroutingKey
queue01“fanout_key01”
queue02“fanout_key01”
queue03“fanout_key01”

根据上述表格的规则来发送消息,当发送消息时的routingKey为以下值时,以下队列会收到消息;

routingKeyqueueName
“fanout_key01”queue01, queue02, queue03 will receive

4.3. BuiltinExchangeType.TOPIC

类似于direct方式,但是topic可以模糊匹配routingKey;通过此种方式,我们可以使得一个队列模糊绑定多个routingKey;

queueNameroutingKey
queue01key1.key2.key3.*
queue02key1.#
queue03*.key2.*.key4
queue04#.key3.key4
  • #:代表0个或多个部分
  • *: 代表一个部分
  • . : 用于分隔不同的routingKey;

根据上述表格的规则来发送消息,当发送消息时的routingKey为以下值时,以下队列会收到消息;

routingKeyqueueName
“key1”queue02 will receive
“key3”no queue will receive
“key1.key2.key3”queue02 will receive
“key1.key2.key3.key4”queue01, queue02, queue03, queue04 will receive

4.3.BuiltinExchangeType.HEADERS

headers 匹配AMQP消息的header而不是路由键,此外headers交换器和direct交换器完全一致,但性能差了很多;
消费方要求指定的headers中必须包含一个"x-match"的键;

  • x-match = all,表示所有的键值对都匹配才能接收到消息;
  • x-match = any,表示只要有键值对匹配就能接收到消息;

生产者按照 x-match 配置的规则发送消息到指定的queue上;

queueNamex-match
queue01{name:“liyuan01”, sex:“male”, x-match: all}
queue02{name:“liyuan02”, sex:“male”, x-match: any}

通过上述规则,按照指定的消息头发送消息时;

x-matchqueueName
{name: “liyuan01”}no queue will receive
{name: “liyuan02”}queue02 will receive
{name: “liyuan01”, sex:“male”}queue02, queue01 will receive

5. 部署 RabbitmqCluster

5.1. 部署 Rabbitmq Cluster-Operator

wget https://github.com/rabbitmq/cluster-operator/releases/latest/download/cluster-operator.yml -O cluster-operator.yaml

# 找到自己的cluster-operator image
# HINT: 若cpu架构为arm,需要执行如下command
sed -i 's#rabbitmqoperator/cluster-operator:2.2.0#cyrilix/rabbitmq-cluster-operator:2.2.0#g' cluster-operator.yaml

kubectl apply -f cluster-operator.yaml && kubectl get po -n rabbitmq-system 

5.2. 部署Rabbitmq 集群

  • rabbitmq-cluster.yaml
# rabbitmq-cluster.yaml
apiVersion: rabbitmq.com/v1beta1
kind: RabbitmqCluster
metadata:
  name: rabbitmq-cluster
  namespace: default
spec:
  replicas: 2
#  image: registry.cn-hangzhou.aliyuncs.com/yuanli123/rabbitmq:3.9.22-management
  persistence:
    storage: 3Gi
    storageClassName: nfs-client
  resources:
    limits:
      cpu: 2000m
      memory: 1Gi
    requests:
      cpu: 1000m
      memory: 500Mi
  secretBackend:
    externalSecret:
      name: rabbitmq-cluster-username-password
#  rabbitmq:
#    additionalConfig: |
#      cluster_formation.k8s.host = 10.1.0.1
#      cluster_formation.k8s.host = kubernetes.default.svc.cluster.local
#  imagePullSecrets:
#    - name: regcred
---
apiVersion: v1
stringData:
  default_user.conf: |
    default_user = liyuan
    default_pass = liyuan
  password: "liyuan"
  username: "liyuan"
  host: "rabbitmq-cluster.default.svc"
  port: "5672"
  provider: "rabbitmq"
kind: Secret
metadata:
  annotations:
    rabbitmq.com/queueRebalanceNeededAt: "2023-06-05T13:16:17Z"
  labels:
    app.kubernetes.io/component: rabbitmq
    app.kubernetes.io/name: rabbitmq-cluster
    app.kubernetes.io/part-of: rabbitmq
  name: rabbitmq-cluster-username-password
  namespace: default
type: Opaque
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: default
  name: rabbitmq-cluster-ingress
spec:
  rules:
    - host: rabbitmq.liyuan.com
      http:
        paths:
          - backend:
              service:
                name: rabbitmq-cluster
                port:
                  number: 15672
            pathType: Prefix
            path: /
# HINT: 注意我们使用默认的rabbitmq镜像即可 'rabbitmq:3.11.10-management'
kubectl apply -f rabbitmq-cluster.yaml

username: liyuan
password: liyuan
在这里插入图片描述
在这里插入图片描述

6.镜像队列

默认情况下消息只会被路由到某一个节点的符合条件的队列上,并不会痛不到其他节点的相同队列上;若此时该节点宕机,那么该消息就会丢失;因此我们可以开启镜像队列,将集群中的队列彼此之间进行镜像,此时消息就会被拷贝到处于同一个镜像分组中的所有队列上;
在这里插入图片描述

  • Name: policy的名称,用户自定义;
  • Pattern: queue的匹配模式(正则表达式),^表示所有队列都是镜像队列;
  • Definition: 镜像定义,包括三个部分,ha-sync-mode, ha-mode, ha-params;
    • ha-mode:
      • all: 表示在集群所有的代理上进行镜像
      • exactly: 表示在指定个数的代理上进行镜像,代理的个数由ha-params指定;
      • nodes: 表示在指定的代理上进行镜像,代理名称通过ha-params指定;
    • ha-params: ha-mode模式需要用到的参数;
    • ha-sync-mode: 表示镜像队列中消息的同步方式,有效取值为: automatic, manually;
      • automatic: 表示自动想master同步数据;
      • manually: 表示手动向master同步数据;
  • Policy: 可选参数,policy优先级;
    在这里插入图片描述

当使用如上配置时,当我们发送消息给符合该名称^liyuan_*的队列时,该消息会被自动镜像

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值