|
| 1 | +# SpringBoot-RabbitMQ 消息队列 |
| 2 | + |
| 3 | +这个指南将引导你建立一个RabbitMQ AMQP服务器发布和订阅消息的过程。 |
| 4 | + |
| 5 | +###声明 |
| 6 | +可以使用本人阿里云安装好的RabbitMQ服务器 |
| 7 | + |
| 8 | + host:http://120.27.114.229 |
| 9 | + username:root |
| 10 | + password:root |
| 11 | + web management: http://120.27.114.229:15672 |
| 12 | + |
| 13 | +###构建 |
| 14 | +你会使用 Spring AMQP的 RabbitTemplate构建应用系统来发布消息并且使用一个MessageListenerAdapter POJO来订阅消息 |
| 15 | + |
| 16 | +###需要 |
| 17 | +* 15分钟 |
| 18 | +* 一款文本编辑器或者IDE |
| 19 | +* [JDK 1.8+](http://www.oracle.com/technetwork/java/javase/downloads/index.html) |
| 20 | +* [Gradle2.3+](http://www.gradle.org/downloads) 或者[Maven3.0+](http://maven.apache.org/download.cgi) |
| 21 | +* 你也可以从这个项目中导入代码或者可以在导入[Spring Tool Suite(STS)](https://spring.io/guides/gs/sts)(个人非常喜欢的一款eclipse的IDE)中查看 |
| 22 | +* RabbitMQ服务器 |
| 23 | + |
| 24 | +###如何完成 |
| 25 | +像许多的Spring [Getting Started guides](https://spring.io/guides)项目,你可以从头开始并完成每一步,或者你可以绕过你已经熟悉的一些步骤,无论是哪种步骤,你最终可以完成代码 |
| 26 | + |
| 27 | +从头开始的话,请去看[使用Gradle构建](https://spring.io/guides/gs/messaging-rabbitmq/#scratch) |
| 28 | + |
| 29 | +如果要绕过你熟悉的,按照以下构建: |
| 30 | + |
| 31 | +* [下载](https://github.com/spring-guides/gs-messaging-rabbitmq/archive/master.zip)并解压得到源代码或者从[Git](https://spring.io/understanding/Git): |
| 32 | + >git clone https://github.com/spring-guides/gs-messaging-rabbitmq.git |
| 33 | +* 进入`gs-messaging-rabbitmq/initial`目录 |
| 34 | +* 跳过[创建RabbitMQ消息接收]() |
| 35 | + |
| 36 | +当你完成时,你可以对比在`gs-messaging-rabbitmq/complete.`目录中的结果和你的结果 |
| 37 | + |
| 38 | +###使用Gradle构建 |
| 39 | +第一步你需要建立一个基本的脚本,当你构建APP应用时你可以任何你喜欢的构建系统,但这些代码你必须要使用到[Gradle](http://gradle.org/)和[Maven](https://maven.apache.org/),如果你对这两个不熟悉,你可以参考[Building Java Projects with Gradle](https://spring.io/guides/gs/gradle)和[Building Java Projects with Maven](https://spring.io/guides/gs/maven) |
| 40 | + |
| 41 | +####1.创建目录结构 |
| 42 | +在你项目的文件夹中创建如下的子目录结构,例如,在*nix系统中使用命令创建`mkdir -p src/main/java/hello` |
| 43 | + |
| 44 | + └── src |
| 45 | + └── main |
| 46 | + └── java |
| 47 | + └── hello |
| 48 | + |
| 49 | +####2.创建Gradle配置文件build.gradle |
| 50 | +以下来自[初始化Gradle配置文件](https://github.com/silence940109/SpringBoot-RabbitMQ/blob/master/build.gradle) |
| 51 | + |
| 52 | +`build.gradle` |
| 53 | + |
| 54 | + buildscript { |
| 55 | + repositories { |
| 56 | + mavenCentral() |
| 57 | + } |
| 58 | + dependencies { |
| 59 | + classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.3.RELEASE") |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + apply plugin: 'java' |
| 64 | + apply plugin: 'eclipse' |
| 65 | + apply plugin: 'idea' |
| 66 | + apply plugin: 'org.springframework.boot' |
| 67 | + |
| 68 | + jar { |
| 69 | + baseName = 'gs-messaging-rabbitmq' |
| 70 | + version = '0.1.0' |
| 71 | + } |
| 72 | + |
| 73 | + repositories { |
| 74 | + mavenCentral() |
| 75 | + } |
| 76 | + |
| 77 | + sourceCompatibility = 1.8 |
| 78 | + targetCompatibility = 1.8 |
| 79 | + |
| 80 | + dependencies { |
| 81 | + compile("org.springframework.boot:spring-boot-starter-amqp") |
| 82 | + testCompile("junit:junit") |
| 83 | + } |
| 84 | + |
| 85 | +[Spring Boot gradle plugin](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-tools/spring-boot-gradle-plugin)提供了很多方便的特性: |
| 86 | + |
| 87 | +* 它集成了所有在类路径下的jar包并构建成单独的jar包,可执行的`über-jar`使它可以更加方便的执行和在你的服务中进行传输 |
| 88 | +* 它为`public static void main()`方法寻找可执行的类作为标志 |
| 89 | +* 它提供了一个内置的依赖解析器来匹配[Spring Boot Dependencies](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-dependencies/pom.xml)依赖版本号,你可以重写任何你希望的版本,但它默认启动时选择的版本集合 |
| 90 | + |
| 91 | +###使用Maven构建 |
| 92 | +第一步你需要建立一个基本的脚本,当你构建APP应用时你可以任何你喜欢的构建系统,但这些代码你必须要使用到[Maven](https://maven.apache.org/),如果你对Maven不熟悉,你可以参考[Building Java Projects with Maven](https://spring.io/guides/gs/maven) |
| 93 | + |
| 94 | +####1.创建目录结 构 |
| 95 | +在你项目的文件夹中创建如下的子目录结构,例如,在*nix系统中使用命令创建`mkdir -p src/main/java/hello` |
| 96 | + |
| 97 | + └── src |
| 98 | + └── main |
| 99 | + └── java |
| 100 | + └── hello |
| 101 | + |
| 102 | +`pom.xml` |
| 103 | + |
| 104 | +```xml |
| 105 | +<?xml version="1.0" encoding="UTF-8"?> |
| 106 | +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 107 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| 108 | + <modelVersion>4.0.0</modelVersion> |
| 109 | + <groupId>org.springframework</groupId> |
| 110 | + <artifactId>gs-messaging-rabbitmq</artifactId> |
| 111 | + <version>0.1.0</version> |
| 112 | + <parent> |
| 113 | + <groupId>org.springframework.boot</groupId> |
| 114 | + <artifactId>spring-boot-starter-parent</artifactId> |
| 115 | + <version>1.4.3.RELEASE</version> |
| 116 | + </parent> |
| 117 | + <properties> |
| 118 | + <java.version>1.8</java.version> |
| 119 | + </properties> |
| 120 | + <dependencies> |
| 121 | + <dependency> |
| 122 | + <groupId>org.springframework.boot</groupId> |
| 123 | + <artifactId>spring-boot-starter-amqp</artifactId> |
| 124 | + </dependency> |
| 125 | + </dependencies> |
| 126 | + <build> |
| 127 | + <plugins> |
| 128 | + <plugin> |
| 129 | + <groupId>org.springframework.boot</groupId> |
| 130 | + <artifactId>spring-boot-maven-plugin</artifactId> |
| 131 | + </plugin> |
| 132 | + </plugins> |
| 133 | + </build> |
| 134 | +</project> |
| 135 | +``` |
| 136 | +[Spring Boot gradle plugin](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-tools/spring-boot-gradle-plugin)提供了很多方便的特性: |
| 137 | + |
| 138 | +* 它集成了所有在类路径下的jar包并构建成单独的jar包,可执行的`über-jar`使它可以更加方便的执行和在你的服务中进行传输 |
| 139 | +* 它为`public static void main()`方法寻找可执行的类作为标志 |
| 140 | +* 它提供了一个内置的依赖解析器来匹配[Spring Boot Dependencies](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-dependencies/pom.xml)依赖版本号,你可以重写任何你希望的版本,但它默认启动时选择的版本集合 |
| 141 | + |
| 142 | +###使用IDE编译 |
| 143 | +####1.建立RabbitMQ沙箱 |
| 144 | +在你可以构建你的消息应用前,你需要建发布和订阅消息的服务器 |
| 145 | + |
| 146 | +RabbitMQ是一个AMQP(Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计)服务器,这个服务器是免费的,你可以在[http://www.rabbitmq.com/download.html](http://www.rabbitmq.com/download.html),你可以手动的下载,或者如果你使用的Mac可以自己制作 |
| 147 | + |
| 148 | + brew install rabbitmq |
| 149 | + |
| 150 | +打开服务器位置并使用默认的配置进行启动 |
| 151 | + |
| 152 | + rabbitmq-server |
| 153 | + |
| 154 | +你可以看到如下的一些输出信息: |
| 155 | +# |
| 156 | + RabbitMQ 3.1.3. Copyright (C) 2007-2013 VMware, Inc. |
| 157 | + ## ## Licensed under the MPL. See http://www.rabbitmq.com/ |
| 158 | + ## ## |
| 159 | + ########## Logs: /usr/local/var/log/rabbitmq/[email protected] |
| 160 | + ###### ## /usr/local/var/log/rabbitmq/[email protected] |
| 161 | + ########## |
| 162 | + Starting broker... completed with 6 plugins. |
| 163 | + |
| 164 | +# |
| 165 | +如果你有运行在本地的docker 你也可以使用[Docker Compose](https://docs.docker.com/compose/)(一个部署多个容器的简单但是非常必要的工具)来快速的启动RabbitMQ服务器,在这个项目的根目录中有一个`docker-compose.yml`,它非常简单: |
| 166 | + |
| 167 | +`docker-compose.yml` |
| 168 | + |
| 169 | + rabbitmq: |
| 170 | + image: rabbitmq:management |
| 171 | + ports: |
| 172 | + - "5672:5672" |
| 173 | + - "15672:15672" |
| 174 | + |
| 175 | +如果这个文件在你的当前目录中你可以运行`docker-compose up`来是RabbitMQ运行在容器中 |
| 176 | + |
| 177 | +####2.创建RabbitMQ消息订阅 |
| 178 | +任何基于消息的应用程序你都需要创建一个消息订阅来响应消息的发布 |
| 179 | + |
| 180 | +`src/main/java/hello/Receiver.java` |
| 181 | + |
| 182 | +```Java |
| 183 | +package hello; |
| 184 | + |
| 185 | +import java.util.concurrent.CountDownLatch; |
| 186 | +import org.springframework.stereotype.Component; |
| 187 | + |
| 188 | +@Component |
| 189 | +public class Receiver { |
| 190 | + |
| 191 | + private CountDownLatch latch = new CountDownLatch(1); |
| 192 | + |
| 193 | + public void receiveMessage(String message) { |
| 194 | + System.out.println("Received <" + message + ">"); |
| 195 | + latch.countDown(); |
| 196 | + } |
| 197 | + |
| 198 | + public CountDownLatch getLatch() { |
| 199 | + return latch; |
| 200 | + } |
| 201 | + |
| 202 | +} |
| 203 | +``` |
| 204 | + |
| 205 | +定义一个简单的`Receiver`类,类中receiveMessage方法用来接收消息,当你注册接受消息时,你可以随意命名它 |
| 206 | + |
| 207 | +>为了方便,这个POJO类有一个`CountDownLatch`类的属性,它允许当消息接收到时给它一个信号量,这是你在生产环境中你不太可能实现的 |
| 208 | +
|
| 209 | +####3.注册监听并发布消息 |
| 210 | +Spring AMQP的`RabbitTemplate`提供了你使用RabbitMQ发布和订阅消息所需要的一切,特别的,你需要如下配置: |
| 211 | + |
| 212 | +* 一个消息监听的容器 |
| 213 | +* 声明队列,交换空间,并且绑定他们 |
| 214 | +* 一个发送一些信息用来测试监听的组件 |
| 215 | + |
| 216 | +>Spring Boot会自动创建连接工场和RabbitTemplate,以便减少你需要编写的代码量 |
| 217 | +
|
| 218 | +你将会使用`RabbitTemplate`来发送消息,并且你要使用消息监听的容器注册一个`Receiver`来接收消息。连接工场驱动使它们可以连接到RabbitMQ服务器上 |
| 219 | + |
| 220 | +`src/main/java/hello/Application.java` |
| 221 | + |
| 222 | +```Java |
| 223 | +package hello; |
| 224 | + |
| 225 | +import org.springframework.amqp.core.Binding; |
| 226 | +import org.springframework.amqp.core.BindingBuilder; |
| 227 | +import org.springframework.amqp.core.Queue; |
| 228 | +import org.springframework.amqp.core.TopicExchange; |
| 229 | +import org.springframework.amqp.rabbit.connection.ConnectionFactory; |
| 230 | +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; |
| 231 | +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; |
| 232 | +import org.springframework.boot.SpringApplication; |
| 233 | +import org.springframework.boot.autoconfigure.SpringBootApplication; |
| 234 | +import org.springframework.context.annotation.Bean; |
| 235 | + |
| 236 | +@SpringBootApplication |
| 237 | +public class Application { |
| 238 | + |
| 239 | + final static String queueName = "spring-boot"; |
| 240 | + |
| 241 | + final static String HOST = "120.27.114.229"; |
| 242 | + |
| 243 | + final static String USERNAME = "root"; |
| 244 | + |
| 245 | + final static String PASSWORD = "root"; |
| 246 | + |
| 247 | + final static int PORT = 15672; |
| 248 | + |
| 249 | + @Bean |
| 250 | + Queue queue() { |
| 251 | + return new Queue(queueName, false); |
| 252 | + } |
| 253 | + |
| 254 | + @Bean |
| 255 | + TopicExchange exchange() { |
| 256 | + return new TopicExchange("spring-boot-exchange"); |
| 257 | + } |
| 258 | + |
| 259 | + @Bean |
| 260 | + Binding binding(Queue queue, TopicExchange exchange) { |
| 261 | + return BindingBuilder.bind(queue).to(exchange).with(queueName); |
| 262 | + } |
| 263 | + |
| 264 | + @Bean |
| 265 | + public ConnectionFactory connectionFactory() { |
| 266 | + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); |
| 267 | + connectionFactory.setHost(HOST); |
| 268 | + connectionFactory.setPort(PORT); |
| 269 | + connectionFactory.setUsername(USERNAME); |
| 270 | + connectionFactory.setPassword(PASSWORD); |
| 271 | + connectionFactory.setVirtualHost("/"); |
| 272 | + //必须要设置,消息的回掉 |
| 273 | + connectionFactory.setPublisherConfirms(true); |
| 274 | + return connectionFactory; |
| 275 | + } |
| 276 | + |
| 277 | + @Bean |
| 278 | + SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, |
| 279 | + MessageListenerAdapter listenerAdapter) { |
| 280 | + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); |
| 281 | + container.setConnectionFactory(connectionFactory); |
| 282 | + container.setQueueNames(queueName); |
| 283 | + container.setMessageListener(listenerAdapter); |
| 284 | + return container; |
| 285 | + } |
| 286 | + |
| 287 | + @Bean |
| 288 | + MessageListenerAdapter listenerAdapter(Receiver receiver) { |
| 289 | + return new MessageListenerAdapter(receiver, "receiveMessage"); |
| 290 | + } |
| 291 | + |
| 292 | + public static void main(String[] args) throws InterruptedException { |
| 293 | + SpringApplication.run(Application.class, args); |
| 294 | + } |
| 295 | + |
| 296 | +} |
| 297 | +``` |
| 298 | + |
| 299 | +`@SpringBootApplication`是一个非常方便的注解,它添加了所有如下的东西: |
| 300 | + |
| 301 | +* `@Configuration`标志着这个类作为资源被这个应用程序而定义的一个bean |
| 302 | +* `@EnableAutoConfiguration`告诉Spring Boot启动自动添加bean是基于类路径配置,其他Beans以及各种属性的设置 |
| 303 | +* 通常你会为Spring MVC的应用程序添加`@EnableWebMvc`注解,但是当Spring Boot看见Spring-webmvc在它的类路径下时它会自动添加,这个标志着这个应用程序是一个web应用程序,并且自动激活并配置例如`DispatcherServlet`的配置 |
| 304 | +* `@ComponentScan`告诉Spring去寻找其他的组件,配置以及在hello包中的其他services,并且允许它使用controllers组件 |
| 305 | + |
| 306 | +`main()`方法是用了Spring Boot的`SpringAPplication.run()`方法来启动一个应用程序,你注意到这里没有使用XML了吗?同样没有web.xml配置文件。这个web应用程序时纯粹的Java开发并且你不必处理任何配置信息 |
| 307 | + |
| 308 | +定义在`listenerAdapter()`bean方法在定义`container()`容器时注册成为一个消息监听器,它会为"spring-boot"的消息队列进行监听。因为`Receiver`是一个POJO,在你指定它被`receiveMessage`调用时,它需要被包装到`MessageListenerAdapter`适配器中 |
| 309 | + |
| 310 | +>JMS队列和AMQP队列有一些语义上的不同。例如,JMS向队列发送消息时只有一个消费者,然而AMQP队列做同样的事情,它虽然模仿JMS主题的概念,但AMQP生产者并不向队列直接发送消息,反而消息发送给的是交换空间,所以AMQP的消息它可以放到一个队列中,或者展开多个队列中。[更多](https://spring.io/understanding/AMQP) |
| 311 | +
|
| 312 | +消息监听容器和接收都是你为了监听到消息所必需的,为了发布一个消息,你也需要一个Rabbit模板 |
| 313 | + |
| 314 | +`queue()`方法创建了一个AMQP的队列,`exchang()`方法创建了exchange,`binding()`方法把他们两个绑定在了一起,并且定义了当RabbitTemplate发布给exchange时发生的动作 |
| 315 | + |
| 316 | +>Spring AMQP要求`Queue`,`TopicExchang`,`Binding`被Spring按照顺序定义为定理的bean |
| 317 | +
|
| 318 | +####4.发送文本消息 |
| 319 | +测试消息是通过`CommandLineRunner`发送的,它也可以等待并锁定接受者并且关闭应用程序: |
| 320 | + |
| 321 | +`src/main/java/hello/Runner.java` |
| 322 | + |
| 323 | +```Java |
| 324 | +package hello; |
| 325 | + |
| 326 | +import java.util.concurrent.TimeUnit; |
| 327 | + |
| 328 | +import org.springframework.amqp.rabbit.core.RabbitTemplate; |
| 329 | +import org.springframework.boot.CommandLineRunner; |
| 330 | +import org.springframework.context.ConfigurableApplicationContext; |
| 331 | +import org.springframework.stereotype.Component; |
| 332 | + |
| 333 | +@Component |
| 334 | +public class Runner implements CommandLineRunner { |
| 335 | + |
| 336 | + private final RabbitTemplate rabbitTemplate; |
| 337 | + private final Receiver receiver; |
| 338 | + private final ConfigurableApplicationContext context; |
| 339 | + |
| 340 | + public Runner(Receiver receiver, RabbitTemplate rabbitTemplate, |
| 341 | + ConfigurableApplicationContext context) { |
| 342 | + this.receiver = receiver; |
| 343 | + this.rabbitTemplate = rabbitTemplate; |
| 344 | + this.context = context; |
| 345 | + } |
| 346 | + |
| 347 | + @Override |
| 348 | + public void run(String... args) throws Exception { |
| 349 | + System.out.println("Sending message..."); |
| 350 | + rabbitTemplate.convertAndSend(Application.queueName, "Hello from RabbitMQ!"); |
| 351 | + receiver.getLatch().await(10000, TimeUnit.MILLISECONDS); |
| 352 | + context.close(); |
| 353 | + } |
| 354 | + |
| 355 | +} |
| 356 | +``` |
| 357 | + |
| 358 | +runner可以在测试中进行模拟,以此,reveive可以单独的进行测试 |
| 359 | + |
| 360 | +####5.启动应用 |
| 361 | +`main()`方法通过创建Spring应用环境来启动进程。这个进程启动了消息监听容器,它会开始监听消息.`Runner`bean会自动执行:它从应用上下文中检索`RabbitTemplate`并且往"sping-boot"队列中发送一个`Hello from RabbitMQ!`的消息,最后,它关闭Spring应用程序,程序结束 |
| 362 | + |
| 363 | +####6.编译可执行的JAR包 |
| 364 | +你可以使用Gradle或者Maven从命令行运行程序,或者你可以编译成一个包含了所有的依赖,类和资源的可执行的JAR文件,然后就可以直接运行。这使它在不同的环境和在整个应用程序的开发声明周期的部署中变得非常容易 |
| 365 | + |
| 366 | +如果你使用的时Gradle,你需要使用`./gradlew bootRun`来运行应用程序,或者你可以使用`./gradlew build`编译成JAR文件,然后你就可以运行JAR文件了 |
| 367 | + |
| 368 | + java -jar build/libs/gs-messaging-rabbitmq-0.1.0.jar |
| 369 | + |
| 370 | +如果你使用的时Maven,你需要使用`./mvnw spring-boot:run`来运行应用程序,或者你可以使用`./mvnw clean package`编译成JAR文件,然后你就可以运行JAR文件了 |
| 371 | + |
| 372 | + java -jar target/gs-messaging-rabbitmq-0.1.0.jar |
| 373 | + |
| 374 | +>上面的结果中会创建一个可执行的JAR文件,你也可以选择[构建一个典型的war文件](https://spring.io/guides/gs/convert-jar-to-war/) |
| 375 | +
|
| 376 | +然后你就可以看到如下的输出: |
| 377 | + |
| 378 | + Sending message... |
| 379 | + Received <Hello from RabbitMQ!> |
| 380 | + |
0 commit comments