Spring Cloud Stream初探

最近有一段时间没有学习Spring相关的内容了,上次和一个朋友聊天说到了Spring Cloud Stream,听他说的是这是一个消息队列的技术.之前我了解到的是有Spring Cloud Bus这样一个技术,但是因为精力有限肯定不能什么都去学习,所以今天简单了解了一下Spring Cloud BusSpring Cloud Stream.
文档地址:Spring Cloud

一、简介

Spring Cloud Bus官方的简介:

Spring Cloud Bus links nodes of a distributed system with a lightweight message broker. This can then be used to broadcast state changes (e.g. configuration changes) or other management instructions. AMQP and Kafka broker implementations are included with the project. Alternatively, any Spring Cloud Stream binder found on the classpath will work out of the box as a transport.

Spring Cloud Stream官方简介:

Spring Cloud Stream is a framework for building highly scalable event-driven microservices connected with shared messaging systems.
The framework provides a flexible programming model built on already established and familiar Spring idioms and best practices, including support for persistent pub/sub semantics, consumer groups, and stateful partitions.

根据上面的是简介,我们大概能了解到Spring Cloud BusSpring Cloud Stream的区别:
Spring Cloud Bus定位于通过轻量级的消息代理来链接分布式系统中的节点,其主要用来广播状态(配置)或者其他管理变更。其实现了AMQPkafka。并且它还可以使用Spring Cloud Stream的消息绑定器.感觉Spring Cloud Bus更偏向于用来广播配置方面的变更,而非业务方面的数据。
Spring Cloud Stream则是一个通过共有的消息系统构建高扩展性的,以事件驱动的微服务的框架。这和Spring Cloud Bus定位是不一样的。
之前的项目中已经多次整合了消息队列KafkaRocketmq等等,感兴趣的可以查看相关的文章。但是目前稳定看到的Spring Cloud Stream官方支持的只有RabbitMQKafkaAmazon Kinesis。下面开始搭建项目。

二、创建项目

本次项目Spring Boot版本选择的是:2.3.8.RELEASE;而Spring Cloud版本是:Hoxton.SR9Kafka版本是:2.5.1。在测试中需要注意相关版本问题。
按照需要我创建两个项目,一个消息生产者,一个消息消费者,因为只是测试Spring Cloud Stream,所以我项目依赖比较简单,其中生产者额外添加了数据库的相关依赖,而消费者只有Spring Cloud Stream,为了方便这里只放出生产者项目的pom.xml,如下:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.8.RELEASE
         
    
    com.spring.cloud.stream.kafka
    producer
    0.0.1-SNAPSHOT
    producer
    spring cloud stream use kafka
    
        1.8
        Hoxton.SR9
    
    
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.cloud
            spring-cloud-starter-stream-kafka
        
        
            org.postgresql
            postgresql
            runtime
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
        
            org.springframework.cloud
            spring-cloud-stream
            test
            test-binder
            test-jar
        
    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    


接下来就是配置文件,先看消息生产者项目(下文统一简称为生产者):

server.port=8080

## JPA配置
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect
## 指定列名,不配置指定列名不生效
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
## 数据库配置
spring.datasource.username=postgres
spring.datasource.password=123456
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/pgsql?useSSL=false&characterEncoding=utf8

## kafka基础配置
spring.cloud.stream.default-binder=kafka
spring.cloud.stream.kafka.binder.brokers=localhost:9092
spring.cloud.stream.kafka.binder.auto-create-topics=true
spring.cloud.stream.kafka.binder.health-timeout=60
## 全局配置
#spring.cloud.stream.kafka.default.producer.=

## channel 名称为自定义的out_channel
spring.cloud.stream.bindings.out_channel.binder=kafka
spring.cloud.stream.bindings.out_channel.content-type=application/json
spring.cloud.stream.bindings.out_channel.destination=topic_one
spring.cloud.stream.bindings.out_channel.group=group_one
spring.cloud.stream.bindings.out_channel.producer.auto-startup=true
spring.cloud.stream.bindings.out_channel.producer.partition-count=1

上述的配置文件中关于数据库和JPA的配置可以忽略。相关的配置可以在官方文档找到,而且讲解也比较详细,我们只是简单分析下上面的配置,主要就是自定义消息发送的管道名称out_channel,其对应的topicgroup还有就是发生消息的内容类型,默认就是application/json,支持的其他类型还有text/plainapplication/xmltext/xml等。
接下来就是编写相关的代码,首先我们需要绑定消息通道的接口,用来绑定我们的输入流和输出流,代码如下:

public interface CustomChannel {

    /**
     * 输出channel 名称
     */
    String OUTPUT = "out_channel";

    /**
     * 输入channel 名称
     */
    String INPUT = "in_channel";

    @Output(value = CustomChannel.OUTPUT)
    MessageChannel output();

    @Input(value = CustomChannel.INPUT)
    SubscribableChannel input();
}

上面的@Output@Input分别表示的输出管道和输入管道,这两个注解标,而它们的名称就是由框架创建的bean的名称,也就是配置文件我们配置的输出管道和输入管道的名称。对于生产者我们只需要配置输出管道即可,接下来我们创建一个消息生产者

@Component
public class MessageProducer {

    private CustomChannel source;

    public MessageProducer(CustomChannel source) {
        this.source = source;
    }

    public CustomChannel getSource() {
        return source;
    }
}

这个代码其实没什么特别的意义,不创建也可以,因为消息最终的发送还是由绑定的输出管道发送的。
编写一个测试的Controller用来发送消息。

@RestController
@RequestMapping("/kafka")
public class SendMessageController {

    private TestService testService;

    public SendMessageController(TestService testService) {
        this.testService = testService;
    }

    @PostMapping("/send/message")
    public ResponseEntity sendMessage() {
        return ResponseEntity.ok(testService.sendMessage());
    }
}

具体的逻辑代码,如下:

@Service
public class TestServiceImpl implements TestService {

    private MessageProducer messageProducer;

    private UserRepository userRepository;

    public TestServiceImpl(MessageProducer messageProducer, UserRepository userRepository) {
        this.messageProducer = messageProducer;
        this.userRepository = userRepository;
    }

    @Override
    public Boolean sendMessage() {
        List userEntityList = userRepository.findAll();
        MessageBuilder> messageBuilder = MessageBuilder.withPayload(userEntityList);
        boolean success = messageProducer.getSource().output().send(messageBuilder.build());
        return success;
    }
}

上面的代码中我从数据库查询了一些数据,然后由消息生产者,准确的说是绑定的消息管道进行发送,这里只是简单的发送了一个消息体。
还有一个重要的事情没有做,就是开启消息输出管道和输入管道和broker的绑定关系。这点一定一定别忘了,所以在启动类上添加@EnableBinding(CustomChannel.class)。这个注解是支持多个绑定关系的,如果你自定义了多个输出管道和输入管道都可以添加上。
这里基本上生产者的相关代码已经完成了。接下来我们开始消费者项目(下文统称消费者)。
消费者主要就是绑定输入管道,项目配置文件如下:

server.port=9090

spring.cloud.stream.default-binder=kafka
spring.cloud.stream.kafka.binder.brokers=localhost:9092

spring.cloud.stream.bindings.in_channel.binder=kafka
spring.cloud.stream.bindings.in_channel.destination=topic_one
#spring.cloud.stream.bindings.input.group=group_one
spring.cloud.stream.bindings.in_channel.content-type=application/json

配置非常简单,就是指定了Kafka以及输入管道的topic和消息类型,其实这两点只需要和生产者的配置保持一致即可。
消费者我们则需要创建消息的监听器来订阅输入管道的消息,代码如下:

@Slf4j
@EnableBinding(CustomChannel.class)
public class TestListener {

    @StreamListener(target = CustomChannel.INPUT)
    public void consume(Message message) {
        String body = message.getPayload();
        log.info(">>>> message={} <<<<",body);
    }
}

将生产者项目中绑定消息通道的接口复制一份到消费者,同样将其和broker进行绑定。这里需要注意一点,@EnableBinding不能直接添加到启动类上(除非你在启动类内添加监听器),而应该添加到具体的消息监听器所在的类上。@StreamListener表明这个方法是一个输入通道的消息的监听方法,也就是真正的消费消息的方法(方法名称无所谓),该注解的名称就是输入管道的名称。
接下来我们就测试以下消息的发送和接收,分别启动生产者和消费者。并调用测试发送消息的接口,成功收到了生产者发送的消息列表。当然有的人会问,你在生产者里面使用了消息的泛型List,而在消费者里面消息的泛型则为String,我的感觉这里因为发送消息的内容是json,因此可以直接使用String,当然和生产者一样使用相应的泛型也可以,并不会影响消息的接收。

三、总结

因为上面只是做了一个简单的例子,要说有什么很大的收货确实是没有,Spring Cloud Stream就是在KafkaRabbitMQ进行了进一步的抽象,它不关心你具体的使用那个类型的消息队列,只需要在配置中具体指定使用的类型即可,相当于做了一个统一性的标准化的接口,不需要额外的去配置具体的消息的监听器等等。但是就本次学习的quick start来讲,个人感觉有点鸡肋,当然这个见仁见智,大家有兴趣都可以交流讨论。因为时间关系本次学习就到这里,如果有什么疑问欢迎大家交流、讨论,最后还是希望大家能多多关注我的VX个人号超超学堂,非常感谢。

你可能感兴趣的:(Spring Cloud Stream初探)