SpringBoot: 整合消息服务之AMQP

AMQP

 AMQP:Advanced Message Queuing Protocol高级消息队列协议。

其是一个线路层的协议规范,不是API规范(e.g: JMS)

所以它是天然跨平台的。按照规范的格式发送数据,任何平台都可以通过AMQP进行消息交互。

主流的StormMQ,RabbitMQ都实现了AMQP


RabbitMQ的安装:

安装指南:安装指南 [推荐访问我这个链接,下面的安装步骤就可以跳过]

RabbitMQ是一个实现了AMQP的开源消息中间件,使用高性能的Erlang编写(很古老的语言).RabbitMQ具有可靠性,支持多种协议、高可用、支持消息集群和多语言客户端等特点,分布式系统中存储转发消息,具有不错的性能表现。

例如Go+RabbitMQ实现商品秒杀系统,再配合CDN,实现高并发处理水平扩展。

这里再梳理一下安装步骤:

Erlang安装:

 1.先安装Erlang:

wget http://erlang.org/download/otp_src_21.0.tar.gz

2. 解压:

tar -zxvf otp_src_21.0.tar.gz

3.编译:

cd otp_src_21.0

./otp_build autoconf
./configure
make

4.安装:

make install

5.检验:

erl

RabbitMQ安装:

yum仓库中默认的Erlang版本较低,需要将最新的Erlang包添加到yum源中。

vi /etc/yum.repos.d/rabbitmq-erlang.repo

添加:

[rabbitmq-erlang]
name=rabbitmq-erlang
baseurl=https://dl.bintray.com/rabbitmq/rpm/erlang/21/el/7
gpgcheck=l
gpgkey=https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc
repo_gpgcheck=0
enabled=l

添加后清楚原来的缓存并创建新缓存:

yum clean all
yum makecache

安装RabbitMQ:

1.下载文件:

wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.7/rabbitmq-server-3.7.7-1.el7.noarch.rpm

2.安装:

yum install rabbitmq-server-3.7.7-1.el7.noarch.rpm

我安装的过程中说缺少socat依赖,安装即可:

yum install socat

3.启动RabbitMQ并进行用户管理:

# 启动
service rabbitmq-server start

# 查看状态
rabbitmqctl status

# 开启web插件
rabbitmq-plugins enable rabbitmq_management

#重启
service rabbitmq-server restart

# 添加一个用户:yinlei是用户名,yinleilei是密码
rabbitmqctl add_user yinlei yinleilei

#设置yinlei角色为管理员
rabbitmqctl set_user_tags yinlei administrator

#配置yinlei可以远程登录
rabbitmqctl set_permissions -p "/" yinlei ".*" ".*" ".*" 

启动成功后,默认是一个guest用户,但是该用户只能本地登录,无法远程登录,所以添加了一个yinlei作为管理员身份,可以远程登录。

利用上面的用户名和密码进行登录:


SpringBoot整合AMQP:

1.添加依赖

2.配置RabbitMQ连接信息:

3.RabbitMQ配置:

Rabbitmq中所有的消息生产者提交的消息都会交给Exchange进行再分配,Exchange会根据不同的策略将消息发布到不同的Queue中。

Rabbitmq共提供了4种不同的Exchange策略:

  • Direct
  • Fanout
  • Topic
  • Header(使用频率低)

1.Direct:

DirectExchange的路由策略是将消息队列绑定到一个DirectExchange上,当一条消息到达DirectExchange时候会被转发到与该条消息routing key相同的Queue上。

DirectExchange配置:

package com.yinlei.rabbitmq;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class RabbitDirectConfig {
    public final static String DIRECTINAME="yinlei-direct";

    /**
     * 提供一个消息队列Queue
     * @return
     */
    @Bean
    Queue queue(){
        return new Queue("hello-queue");
    }

    /**
     * 创建DirectExchange对象
     * @return
     */
    @Bean
    DirectExchange directExchange(){
        //参数1是名字,参数2是重启后是否以然有效,参数3是长期未使用时是否删除
        return new DirectExchange(DIRECTINAME,true,false);
    }

    /**
     * 创建一个Binding对象将Exchange和Queue绑定再一起
     * @return
     */
    @Bean
    Binding binding(){
        return BindingBuilder.bind(queue()).to(directExchange()).with("direct");
    }
}

DirectExchange和Binding的2个bean配置可以省略,如果使用DirectExchange,只配置一个Queue实例即可。

配置消费者:

测试验证:

2.Fanout

FanoutExchange的数据交换策略是把所有到达FanoutExchange的消息转发给所有与它绑定的Queue,

routingkey不起任何作用了。

FanoutExchange配置方式:

package com.yinlei.rabbitmq;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitFanoutExchange {
    public final static String FANOUTNAME ="yinlei-fanout";
    
    @Bean
    FanoutExchange fanoutExchange(){
        return new FanoutExchange(FANOUTNAME,true,false);
    }
    
    @Bean
    Queue queueOne(){
        return new Queue("queue-1");
    }

    @Bean
    Queue queueTwo(){
        return new Queue("queue-2");
    }
    
    @Bean
    Binding bindingOne(){
        return BindingBuilder.bind(queueOne()).to(fanoutExchange());
    }

    @Bean
    Binding bindingTwo(){
        return BindingBuilder.bind(queueTwo()a).to(fanoutExchange());
    }
}

创建2个消费者,分别消费2条消息队列中的消息:

最后进行测试:

这里不再需要routingkey,指定exchange即可。routingkey可以传null

3.Topic

TopcExchange比较复杂和灵活。

Queue中通过routingkey绑定到TopicExchange上,当消息到达TopicExchange后,TopicExchange将消息路由到一个或多个Queue上。

TopicExchange配置:
 

package com.yinlei.rabbitmq;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitTopicConfig {
    public final static String TOPICNAME="yinlei-topic";
    
    @Bean
    TopicExchange topicExchange(){
        return new TopicExchange(TOPICNAME,true,false);
    }
    
    @Bean
    Queue zhangming(){
        return new Queue("zhangming");
    }
    @Bean
    Queue songyang(){
        return new Queue("songyang");
    }
    @Bean
    Queue yinwei(){
        return new Queue("yinwei");
    }
    
    @Bean
    Binding zhangmingBinding(){
        return BindingBuilder.bind(zhangming()).to(topicExchange()).with("zhangming.#");
    }

    @Bean
    Binding songyangBinding(){
        return BindingBuilder.bind(songyang()).to(topicExchange()).with("songyang.#");
    }

    @Bean
    Binding yinweiBinding(){
        return BindingBuilder.bind(yinwei()).to(topicExchange()).with("yinwei.#");
    }
    
}

创建了3个queue

分别绑定到topicExchange上,yinwei.#表示消息的routingkey凡是以yinwei开头的,都将被路由到名称为yinwei的queue上。其他同理。

创建3个消费者:

测试:

4.Header

HeaderExchange会根据消息的Header将消息路由到不同的Queue上,也和routingkey无关。

HeaderExcahnge配置如下:

package com.yinlei.rabbitmq;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.HeadersExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class RabbitHeaderConfig {
    public final static String HEADNAME = "yinlei-header";
    
    @Bean
    HeadersExchange headersExchange(){
        return new HeadersExchange(HEADNAME,true,false);
    }
    
    @Bean
    Queue queueName(){
        return new Queue("name-queue");
    }

    @Bean
    Queue queueAge(){
        return new Queue("age-queue");
    }

    @Bean
    Binding bindingName(){
        Map<String,Object> map = new HashMap<>();
        map.put("name","yinlei");;
        
        return BindingBuilder.bind(queueName()).to(headersExchange()).whereAny(map).match();
    }

    @Bean
    Binding bindingAge(){
        return BindingBuilder.bind(queueAge()).to(headersExchange()).where("age").exists();
    }
}

whereAny表示消息的header中只要有一个Header匹配上map中的key/value,都把消息路由到名为name-queue的Queue上。

而对于age,表示无论age是多少,只要header包含age,都将消息路由到age-queue的Queue上。

2个消费者:

消息的发送也和routingkey无关:

 

发布了356 篇原创文章 · 获赞 67 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/qq_39969226/article/details/104993663