用spring boot + redis + dubbo +rabbitmq + mybatis 搭建一个项目。
项目链接地址:
1、创建项目,目录结构如下图。controller和server通过dubbo链接。在server中根据不同业务决定是否调用rabbitmq-publisher
2、在主pom文件中加入常规所需要的依赖jar
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.3-jre</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <!-- AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
3、配置依赖关系
将rabbitmq-common配置成rabbitmq-consumer和rabbitmq-publisher依赖包
将entity配置成controller和server依赖包
将rabbitmq-publisher配置成server依赖包
rabbitmq-consumer pom.xml
rabbitmq-publisher pom.xml
controller pom.xml
server pom.xml文件
4、配置application文件加载信息
controller 下application.yml
配置dubbo相关信息,端口,和数据库相关信息。
server 下 application.yml
配置dubbo,rabbitmq,端口,数据库 相关信息
注:在配置dubbo 信息时候需要配置dubbo.scan.basePackages: 属性,指定需要扫面的包,本项目配置的是com.wan。否则在dubbo调用时会报找不到类,接口,或对应prodive服务。
如下错误是没在application.yml时产生
5、启动文件配置
ControllerApplication.java
由于在controller 项目userConsumer 中需要引用entity接口中 IUserServer 接口。所以需要在启动类中添加 scanBasePackages = "com.wan"。否则在install打包或者调用时会报找不到 IUserServer 类或接口
ServerApplication.java
若不配置scanBasePackages=“com.wan" ,在server调用依赖包rabbitmq-publisher中对象时会报错。
6、代码详解
1.dubbo 消费者
在调用dubbo服务时需要用注解@Reference 注解(alibaba包)。将其注入
2. dubbo 提供者
在提供dubbo服务时需要用@Service 注解(alibaba包)
3. RabbitMQ 配置加载初始化
package com.wan.comm.common; import org.springframework.amqp.core.*; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.listener.MessageListenerContainer; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration public class RabbitMQConfig { @Value("${spring.rabbitmq.host}") private String host; @Value("${spring.rabbitmq.port}") private Integer port; @Value("${spring.rabbitmq.username}") private String username; @Value("${spring.rabbitmq.password}") private String password; @Value("${spring.rabbitmq.workQueueName}") private String workQueueName; @Value("${spring.rabbitmq.workRoutingKey}") private String workRoutingKey; @Value("${spring.rabbitmq.workExchange}") private String workExchange; @Value("${spring.rabbitmq.errorQueueName}") private String errorQueueName; @Value("${spring.rabbitmq.errorRoutingKey}") private String errorRoutingKey; @Value("${spring.rabbitmq.errorExchange}") private String errorExchange; /** 并发消费数量 */ @Value("${spring.rabbitmq.concurrency}") private Integer consumeConcurrency; /** 队列超时时间 */ @Value("${spring.rabbitmq.connection.timeout}") private Integer connectionTimeout; /** 设置线程核心线程数 **/ @Value("${spring.rabbitmq.connection.min_threads}") private Integer connectionMinThreads; /** 设置线程最大线程数 **/ @Value("${spring.rabbitmq.connection.max_threads}") private Integer connectionMaxThreads; /** 线程池所使用的缓冲队列 */ @Value("${spring.rabbitmq.connection.max_queued}") private Integer connectionMaxQueued; @Value("${spring.rabbitmq.virtual-host}") private String virtualHost; /** 线程名称前缀 */ private static final String CONNECTION_THREAD_PREFIX = "rabbitmq-connection-thread"; @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } /** * rabbit 连接创建 * @return */ @Bean ConnectionFactory connectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port); connectionFactory.setUsername(username); connectionFactory.setPassword(password); connectionFactory.setVirtualHost(virtualHost); connectionFactory.setConnectionTimeout(connectionTimeout); //线程池设置 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadNamePrefix(CONNECTION_THREAD_PREFIX); executor.setCorePoolSize(connectionMinThreads); executor.setMaxPoolSize(connectionMaxThreads); executor.setQueueCapacity(connectionMaxQueued); executor.setWaitForTasksToCompleteOnShutdown(true); executor.initialize(); connectionFactory.setExecutor(executor); return connectionFactory; } /** * 设置发送队列时的数据格式,默认编码为UTF-8 * @return */ @Bean MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } /** * 设置发送队列时的数据格式 * @return */ @Bean RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory,MessageConverter messageConverter) { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); rabbitTemplate.setMessageConverter(messageConverter); return rabbitTemplate; } /** * 工作交换器 * @returns */ @Bean DirectExchange workExchange(){ return new DirectExchange(workExchange); } /** * 失败交换器 * @return */ @Bean DirectExchange errorExchange(){return new DirectExchange(errorExchange);} /** * 工作队列,默认正常情况下从该队列消费消息 * * @return */ @Bean Queue workQueue() { return QueueBuilder.durable(workQueueName).build(); } /** * 失败队列,消息消费失败则入该队列 * * @return */ @Bean Queue errorQueue() { return QueueBuilder.durable(errorQueueName).build(); } /** * 队列与交换器绑定 * @param workQueue * @param workExchange * @return */ @Bean Binding workQueuqBindingExchange(Queue workQueue,DirectExchange workExchange){ return BindingBuilder.bind(workQueue).to(workExchange).with(workRoutingKey); } @Bean Binding errorQueuqBindingExchange(Queue errorQueue,DirectExchange errorExchange){ return BindingBuilder.bind(errorQueue).to(errorExchange).with(errorRoutingKey); } @Bean MessageListenerContainer workMessageListenerContainer(ConnectionFactory connectionFactory, WorkRabbitMQConsumer workRabbitMQConsumer) { SimpleMessageListenerContainer messageListenerContainer = new SimpleMessageListenerContainer(); messageListenerContainer.setConnectionFactory(connectionFactory); //设置队列名 messageListenerContainer.setQueueNames(workQueueName); //设置监听类 messageListenerContainer.setMessageListener(new MessageListenerAdapter(workRabbitMQConsumer)); // 并发消费信息的数量 messageListenerContainer.setConcurrentConsumers(consumeConcurrency); //设置ack messageListenerContainer.setAcknowledgeMode(AcknowledgeMode.MANUAL); //设置返回格式 在发送convertAndSend 中会将Obj转换成jsonString,默认编码为UTF-8 //messageListenerContainer.setMessageConverter(new Jackson2JsonMessageConverter()); return messageListenerContainer; } }
4.RabbitMQ 发送。
若使用 rabbitTemplate.convertAndSend(routingKey,obj); 方法,在未指定交换器时,会根据传入的 routingKey 去匹配queueName 去查找队列。
rabbitTemplate 在 RabbitMQConfig.java 初始化是已注入。并rabbitTemplate.setMessageConverter(messageConverter);设置了数据格式转换方式。如下
Jackson2JsonMessageConverter 默认将数据转成jsonString 在封装成Message对象进行传输。具体代码如下:
—— RabbitTemplate.class中send()
—— AbstractMessageConverter.class 中 toMessage()
找到createMessage(),进入初始化注入的 Jackson2JsonMessageConverter 中的实现
—— AbstractJackson2MessageConverter
如下图,将我们传入的Object对象转换成jsonString ,并封装成Message对象。
在RabbitMQ消费者取用时,只需直接从Message中获取
String jsonString = new String(message.getBody(),"UTF-8");
就能直接获取到json字符串。