• 使用 RabbitMQ 实现异步调用


    引言

    除了上篇文章所讲的 ActiveMQ,还有一种流行的开源消息中间件叫 RabbitMQ。和 ActiveMQ 相比,它具有更高的性能。

    RabbitMQ 不再基于 JMS 规范,也没有选择 Java 作为底层实现语言。 它基于另一种消息通信协议,名为 AMQP,并采用 Erlang 语言作为技术实现。 RabbitMQ 提供了众多语言客户端,能够与 Spring 框架整合,Spring Boot 也提供了对 RabbitMQ 的支持。

    RabbitMQ 官网: http://www.rabbitmq.com

    启动 RabbitMQ 服务器

    运行 rabbitmq 容器

    RabbitMQ 官方已经提供了自己的 Docker 容器,先下载 rabbitmq:3-management 镜像来启动 RabbitMQ 容器, 之所以选择这个镜像是因为它拥有一个 web 控制台,可以通过浏览器来访问。

    docker pull rabbitmq:3-management
    

    RabbitMQ 除了控制台,还提供了 HTTP API 方式,可方便应用程序使用。

    下面使用如下 Docker 命令启动 RabbitMQ

     docker run -d -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin --name rabbitmq rabbitmq:3-management
    

    在启动 RabbitMQ 容器时,它对宿主机暴露了两个端口号

    • 15672: 表示RabbitMQ 控制台端口号,可在浏览器中通过控制台来执行 RabbitMQ 的相关操作
    • 5672 表示 RabbitMQ 监听的TCP 端口号,应用程序可以通过该端口号与 RabbitMQ 建立 TCP 连接,并完成后续的异步消息通信

    此外,启动时还有两个环境变量

    • RABBITMQ_DEFAULT_USER : 设置控制台默认用户名, 默认为 guest
    • RABBITMQ_DEFAULT_PASS: 设置控制台默认密码,默认为 guest

    RabbitMQ 控制台

    RabbitMQ 容器启动完毕后,打开浏览器,并在地址栏中输入 http://localhost:15672/ ,并且输入登录的用户名和密码,就可以看到控制台如下所示

    在上面管理界面中,包含 6 个功能菜单

    • Overview: 用于查看 RabbitMQ 基本信息
    • Connections: 用于查看 RabbitMQ 客户端的连接信息
    • Channels: 用于查看 RabbitMQ 的通道
    • Exchanges:用于查看 RabbitMQ 的交换机
    • Queues: 用于查看 RabbitMQ 的队列
    • Admin: 用于管理 RabbitMQ 的用户,虚拟主机,策略等数据

    Exchange 和 Queue

    RabbitMQ 只有 Queue, 没有 Topic,因为可通过 Exchange 与 Queue 的组合来实现 Topic 所具备的功能。RabbitMQ 的消息模型如下图所示

    在 Exchange 和 Queue 间有一个 Binding 关系,当消息从 Producer 发送到 Exchange 中时,会根据 Binding 来路由消息的去向。

    • 如果 Binding 各不相同,那么该消息将路由到其中一个 Queue 中,随后将被一个 Consumer 所消费,此时实现了 "点对点"的消息通信模型。
    • 如果 Binding 完全相同,那么该消息就会路由到每个 Queue 中,随后将会被每个 Consumer 消费,这样就实现了 “发布与订阅” 的消息通信模型

    因此可将 Binding 理解为 Exchange 到 Queue 的路由规则,这些规则可通过 RabbitMQ 所提供的客户端 API 来控制,也可通过 RabbitMQ 提供的控制台来管理。

    RabbitMQ 提供了一个默认的 Exchange(AMQP default),在控制台的 Exchange 菜单中就可以看到它,简单情况下,只需要使用默认的 Exchange 即可,当需要提供发布与订阅功能时才会使用自定义的 Exchange。

    开发服务端和客户端

    下面我们就将 Spring Boot 与 RabbitMQ 进行整合,先开发一个服务端作为消息的消费者,再开发一个客户端作为消息的生产者,随后运行客户端,并查看服务端中接收到的消息。

    开发服务端

    创建一个名为 rabbitmq-hello-server 的 maven 项目或者 Spring Starter Project, 在 pom.xml 文件中添加下面 Maven 依赖

    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.19.RELEASE</version>
    		<relativePath/> <!-- lookup parent from repository -->
    	</parent>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-amqp</artifactId>
    		</dependency>
    	</dependencies>
    

    Spring Boot 框架中已经添加了对 RabbitMQ 的支持,只需要依赖 spring-boot-starter-amqp 就可以启动 RabbitMQ,此时还需要在 application.properties 配置文件中添加 RabbitMQ 的相关配置项

    spring.rabbitmq.addresses=127.0.0.1:5672
    spring.rabbitmq.username=admin
    spring.rabbitmq.password=admin
    

    接下来创建 HelloServer 类,封装服务端代码

    package demo.msa.rabbitmq;
    
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloServer {
    
        @RabbitListener(queues = "hello-queue")
        public void receive(String message) {
            System.out.println(message);
        }
    }
    

    只需要在 receive() 方法上定义 @RabbitListener ,并且设置 queues 参数来指定消费者需要监听的的队列名称。

    最后,编写一个 Spring Boot 应用程序启动类来启动服务器

    package demo.msa.rabbitmq;
    
    import org.springframework.amqp.core.Queue;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    
    @SpringBootApplication
    public class RabbitmqHelloServerApplication {
    
        @Bean
        public Queue helloQueue() {
            return new Queue("hello-queue");
        }
        
    	public static void main(String[] args) {
    		SpringApplication.run(RabbitmqHelloServerApplication.class, args);
    	}
    
    }
    

    在 RabbitMQ 中,必须通过程序来显式创建队列。服务端启动完毕后,将持续监听 RabbitMQ 的 hello-queue 队列中即将到来的消息,该消息由客户端来发送。

    开发客户端

    创建一个名为 rabbitmq-hello-client 的 maven 项目或者 Spring Starter Project, pom 中的依赖与服务端一致。客户端的 application.properties 文件与服务端一致。

    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.19.RELEASE</version>
    		<relativePath/> <!-- lookup parent from repository -->
    	</parent>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-amqp</artifactId>
    		</dependency>
    	</dependencies>
    

    接下来创建一个名为 HelloClient 的类,将其作为客户端

    package demo.msa.rabbitmq;
    
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloClient {
        
        @Autowired
        private RabbitTemplate rabbitmqTemplate;
        
        public void send(String message) {
            rabbitmqTemplate.convertAndSend("hello-queue", message);
        }
    }
    

    最后编写 Spring Boot 应用程序启动类来启动客户端

    package demo.msa.rabbitmq;
    
    import javax.annotation.PostConstruct;
    
    import org.springframework.amqp.core.Queue;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    
    @SpringBootApplication
    public class RabbitmqHelloClientApplication {
        
        @Autowired
        private HelloClient helloClient;
        
        @Bean
        public Queue helloQueue() {
            return new Queue("hello-queue");
        }
        
        @PostConstruct
        public void init() {
            helloClient.send("hello world!");
        }
        
    	public static void main(String[] args) {
    		SpringApplication.run(RabbitmqHelloClientApplication.class, args).close();
    	}
    
    }
    

    与服务端一样,此处使用 @Bean 注解的 helloQueue() 方法创建一个名为 hello-queue 的队列,这样可以保证当客户端在服务端之前启动时,也能创建所需的队列。而且 RabbitMQ 可以确保不会创建同名的队列,因此可分别在服务端与客户端创建同名的队列。

    运行 main 方法可以启动客户端应用程序,此时将在服务端看到客户端发送的消息,也可以在 RabbitMQ 控制台中看到消息队列当前的状态。

    Java Bean 类型传输

    上面发送和接收的消息只是 String 类型,如果发送的消息是一个普通的 Java Bean 类型,应该如何调用呢?

    Java Bean 类型则必须实现 Serializable 序列化接口才能正常调用,这是因为 RabbitMQ 所传送的消息是 byte[] 类型,当客户端发送消息需要进行序列化(也就是讲 Java 类型转换为 byte[] 类型),当服务端接收消息前需要先反序列化,因此发送和接收的消息对象必须实现 JDK 的序列化接口。

    除了这种序列化方式外,我们也可以使用 Jackson 来实现,而且 RabbitMQ 已经为我们提供了 jackson 序列化的方式,这种方式更加高效。所需要做的是定义一个 Jackson2JsonMessageConverter 的 Spring Bean。

        @Bean
        public Jackson2JsonMessageConverter messageConverter() {
            return new Jackson2JsonMessageConverter();
        }
    

    结语

    RabbitMQ 的性能非常高效和稳定,也能非常方便的与 Spring Boot 应用程序集成,还拥有非常丰富的官方文档和控制台,因此选择 RabbitMQ 作为服务之间的异步消息调用平台,将成为整个微服务架构中的 "消息中心"。

    参考

    • 《架构探险—轻量级微服务架构》
  • 相关阅读:
    ASP.NET MVC 入门9、Action Filter 与 内置的Filter实现(介绍) 【转】
    一个建议,看看大家的意见。
    发现不错的文章,推!
    有个小问题,大家一起研究。
    逼不得已,这个我确实不会,昨办?
    MSN Message6.2 的小BUG
    在IE7浏览器中切换成以资源管理器方式
    手机罗盘(指南针)校准方法
    G13/ Wildfire S/A510e link2SD教程,干净清洁的安装程序到内存卡
    HTC G13电池怎么鉴别真伪
  • 原文地址:https://www.cnblogs.com/reycg-blog/p/10266888.html
Copyright © 2020-2023  润新知