RabbitMQ--SpringBoot

RabbitMQ--SpringBoot

1、消息队列

消息队列的功能:
    1、异步

    2、削峰

    3、解耦
    
消息队列的规范:
	1、JMS
		Java MessageService,实际上是指JMS API。JMS是由Sun公司早期提出的消息标准,旨在为java应用提供统一的消息操作,包括create、send、receive等。JMS已经成为Java Enterprise Edition的一部分。从使用角度看,JMS和JDBC担任差不多的角色,相关产品有	activeMq、Redis等。
	
	2、AMQP
		AMQP(advanced message queuing protocol),顾名思义,AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。这使得实现了AMQP的provider天然性就是跨平台的。意味着我们可以使用Java的AMQP provider,同时使用一个python的producer加一个rubby的consumer。从这一点看,AQMP可以用http来进行类比,不关心实现的语言,只要大家都按照相应的数据格式去发送报文请求,不同语言的client均可以和不同语言的server链接。具体实现有rabbitMQ.

2、SpringBoot整合rabbitMq

rabbitmq是AMQP的实现,加入amqp的starter即可。

<!--        rabbitmq-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

<!--楼上starter包含了这两个依赖-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-messaging</artifactId>
      <version>5.2.8.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.amqp</groupId>
      <artifactId>spring-rabbit</artifactId>
      <version>2.2.10.RELEASE</version>
      <scope>compile</scope>
    </dependency>

在yml中配置rabbitmq的相关参数。

spring:
    rabbitmq:
        host: xxxx	#所有参数都有默认值,不指定则是localhost
        port: 5672  #默认通信地址
        #默认虚拟主机等
        username: guest
        password: guest #默认

关于rabbitmq的配置类,在这里用代码生成了交换机、队列,并且通过路由进行了绑定。

如果rabbitmq的服务上已经有了这些,也可以直接用。

/**
 * @author cgl
 * @version 1.0
 * @date 2020/9/16 16:51
 */
@Configuration
@EnableRabbit
public class MqConfig {

    //设置rabbitTemplate的序列化方式,不设置默认使用jdk序列化
    //jdk序列化,对象需要继承Serializable
    //解析收到的消息为对象时,使用的是setter,所以对象需要有无参构造器
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        //使用json序列化
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        return rabbitTemplate;
    }


    //交换机
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("ek.directExchange");
    }
    //队列
    @Bean
    public Queue queue(){
        return new Queue("ek.queue",true);
    }

    //绑定两者
    @Bean
    public Binding binding(){
        return BindingBuilder.bind(queue()).to(directExchange()).with("ekRouting");
    }


}

测试一下:

@Data
@AllArgsConstructor
@NoArgsConstructor //rabbitMq需要空参构造器
public class User implements Serializable {//默认序列化需要

    private String name;
    private Integer id;
    private String sex;
    private Integer age;
}

@SpringBootTest
@RunWith(SpringRunner.class)
public class MqTest {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    public void test(){
        //转换并发送,队列接受的是Json数据
    	rabbitTemplate.convertAndSend("ek.directExchange","ekRouting","Hello RabbitMq!");
        User user = new User("Jack", 10010, "男", 18);
        rabbitTemplate.convertAndSend("ek.directExchange","ekRouting",user);

        //接收队列中的一个消息,返回对应的Json数据,然后被转换为对象
        //System.out.println(rabbitTemplate.receiveAndConvert("ek.queue"));
    }


    @Test
    public void test2(){
        User user = new User("Jack", 10010, "男", 18);

        //使用字节数组作为消息体,消息头使用默认即可
        Message message=new Message((user.toString()).getBytes(),new MessageProperties());
        
        //直接发送,需要一个Message类型的参数,参数中封装了字节数据(消息体),以及传输参数(消息头)
        rabbitTemplate.send("ek.directExchange","ekRouting",message);
        
        //如果接收数据,也是Message对象,自行解析即可。
        {Message receive = rabbitTemplate.receive("ek.queue");
        System.out.println(receive);
        byte[] body = receive.getBody();
        String s = new String(body);
        System.out.println(s);}
    }
}

3、序列化

//jdk默认序列化使用二进制,
/*
content_type:	
	application/x-java-serialized-object
Payload
	218 bytes
	Encoding: base64
*/
rO0ABXNyABRjb20uY2dsLmVrLnBvam8uVXNlcigRewaMt+OQAgAETAADYWdldAATTGphdmEvbGFuZy9JbnRlZ2VyO0wAAmlkcQB+AAFMAARuYW1ldAASTGphdmEvbGFuZy9TdHJpbmc7TAADc2V4cQB+AAJ4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAASc3EAfgAEAAAnGnQABEphY2t0AAPnlLc=
    

    
//使用字节数组
/*
content_type:	
	application/octet-stream
Payload
    47 bytes
    Encoding: string
*/
 User(name=Jack, id=10010, sex=男, age=18)

 //使用json序列化
 /*
 headers:	
    __TypeId__:	com.cgl.ek.pojo.User
content_encoding:	
	UTF-8
content_type:	
	application/json
Payload
    47 bytes
    Encoding: string	
 */
{"name":"Jack","id":10010,"sex":"男","age":18}

可以看到无论使用json还是字节数组都优于jdk的序列化方式

4、消费者、监听

生产的消息会被消费,之前直接接收也是消费。

常用的是直接监听消息队列,有事件进入就直接取出。

Spring中提供了消息监听,只要监听到队列有数据,就直接调用方法。

@Component
public class RabbitConsumer {

    //注解,监听队列
    @RabbitListener(queues = "ek.queue")
    public void receive(String msg) {
	//监听到之后,方法就会执行
        System.out.println(msg);
    }


}

循环发送消息,测试监听器。

@Autowired
    RabbitConsumer rabbitConsumer;

    @Test
    public void  test4() throws InterruptedException {

        //循环发送消息给队列
        for(int i=0;i<5;i++){
            int nextInt = new Random().nextInt(100);
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("现在是yyyy-MM-dd hh:mm:ss a");
            String format = dateTimeFormatter.format(now);
            rabbitTemplate.convertAndSend("ek.directExchange","ekRouting", format+nextInt);
            //睡会儿,让监听器得以启动
            Thread.sleep(3000);
        }

        //让主线程多睡会儿,便于观察可视化界面
        Thread.sleep(10000);

    }


此时控制栏会输出数据:
    "现在是2020-09-17 11:22:03 上午66"
    "现在是2020-09-17 11:22:08 上午37"
    "现在是2020-09-17 11:22:13 上午53"
    "现在是2020-09-17 11:22:18 上午11"
    "现在是2020-09-17 11:22:23 上午78"

主线程休眠的时间里,监听器获得足够的时间启动,在可视化界面中可以看到消费者一栏出现ip,点进去,可以看到连接和信道:
    Channel: 180.115.x.x:54576 -> 172.x.x.x:5672 (1)
    Channel: 180.115.x.x:54576 -> 172.x.x.x:5672 (2)
    Channel: 180.115.x.x:54576 -> 172.x.x.x:5672 (3)
    ....
posted @ 2020-10-15 15:23  cgl_dong  阅读(117)  评论(0编辑  收藏  举报