是什么

屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型(可理解为JDBC)

解决问题

每种消息中间件实现方式不同,对接也不同,如一开始对接了RabbitMQ,后期想改Kafka,那对接方面的代码会受到影响,所以,使用Stream可避免类似问题

使用方式

注:下面默认已经安装好RabbitMq环境

创建生产者项目

pom

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-bus-amqp</artifactId>
  4. <version>2.2.3.RELEASE</version>
  5. </dependency>

yml

  1. server:
  2. port: 8000
  3. spring:
  4. application:
  5. name: producer
  6. cloud:
  7. stream:
  8. binders: # 配置使用mq的信息;
  9. myRabbit: #给Binder定义的名称,用于下方bindings -> binder
  10. type: rabbit # 消息组件类型
  11. environment: # mq环境配置
  12. spring:
  13. rabbitmq:
  14. host: 192.168.1.2 #rabbitmq使用非localhost|127.0.0.1以外的帐号登录,需在管理页面创建新的帐号密码
  15. port: 5672
  16. username: admin
  17. password: 123456
  18. bindings: # 服务的整合处理
  19. output: # 这个名字是一个通道的名称
  20. destination: myExchange # Exchange名称(消息队列主题名称),生产者消费者需一致
  21. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  22. binder: myRabbit # 设置要绑定的消息服务的具体设置

生产消息方法

接口

  1. public interface MessageProducer {
  2. boolean send();
  3. }

实现

  1. package com.project.stream_producer.demo1.service.impl;
  2. import com.project.stream_producer.demo1.service.MessageProducer;
  3. import org.springframework.cloud.stream.annotation.EnableBinding;
  4. import org.springframework.cloud.stream.messaging.Source;
  5. import org.springframework.messaging.MessageChannel;
  6. import org.springframework.messaging.support.MessageBuilder;
  7. import javax.annotation.Resource;
  8. import java.util.UUID;
  9. @EnableBinding(Source.class)
  10. public class MessageProducerImpl implements MessageProducer {
  11. @Resource
  12. private MessageChannel output;//消息发送管道
  13. @Override
  14. public boolean send() {
  15. String uuid = UUID.randomUUID().toString();
  16. boolean send = output.send(MessageBuilder.withPayload(uuid).build());
  17. System.out.println(uuid);
  18. return send;
  19. }
  20. }

以上代码见 code 上述代码在demo1包下

创建消费者项目

pom

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
  4. <version>3.0.10.RELEASE</version>
  5. </dependency>

yml

  1. server:
  2. port: 8000
  3. spring:
  4. application:
  5. name: consumer
  6. cloud:
  7. stream:
  8. binders: # 配置使用mq的信息;
  9. myRabbit:
  10. type: rabbit # 消息组件类型
  11. environment: # mq环境配置
  12. spring:
  13. rabbitmq:
  14. host: 192.168.1.2
  15. port: 5672
  16. username: admin
  17. password: 123456
  18. bindings: # 服务的整合处理
  19. input: # 这个名字是一个通道的名称
  20. destination: myExchange # Exchange名称(消息队列主题名称),生产者消费者需一致
  21. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  22. binder: myRabbit

接收消息方法

  1. package com.project.stream_consumer.demo1.controller;
  2. import org.springframework.cloud.stream.annotation.EnableBinding;
  3. import org.springframework.cloud.stream.annotation.StreamListener;
  4. import org.springframework.cloud.stream.messaging.Sink;
  5. import org.springframework.messaging.Message;
  6. import org.springframework.stereotype.Component;
  7. @Component
  8. @EnableBinding(Sink.class)
  9. public class ConsunmerController {
  10. @StreamListener(Sink.INPUT)
  11. public void input(Message<String> message){
  12. System.out.println("消费消息:"+message.getPayload());
  13. }
  14. }

以上代码见 code 上述代码在demo1包下

重复消费

上述配置,默认实现发布订阅,有消息产生则所有消费端都可收到,有些场景不想这样,所以需新增如下配置

消费者yml

新增group: group_a

  1. bindings: # 服务的整合处理
  2. input: # 这个名字是一个通道的名称
  3. destination: myExchange # 表示要使用的Exchange名称定义
  4. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  5. binder: defaultRabbit # 设置要绑定的消息服务的具体设置
  6. group: group_a #分组名称自定义

此时,当消费者再次发送消息,mq只会选择组内一台消费者进行消费

持久化

当消费端加入group配置后,就有了消息持久化的效果,在消费端服务关闭时,生产端产出了消息,消费端启动后会自动拉取

以上代码见 code 上述代码在demo2 YML文件下

消费者负载个性配置(预拉取)

或者理解为,不管你消费速度多慢,只要得到了消费机会,那至少要给我消费指定条数

在众多消费者中,服务器配置参差不齐,消费能力有高有低,为了能让低配的降低负载,高配提高负载,可进行prefetch配置,具体见下方注释

  1. server:
  2. port: 7001
  3. spring:
  4. application:
  5. name: concumer
  6. cloud:
  7. stream:
  8. binders: # 配置使用mq的信息;
  9. myRabbit: #给Binder定义的名称,⽤于后⾯的关联
  10. type: rabbit # 消息组件类型
  11. environment: # mq环境配置
  12. spring:
  13. rabbitmq:
  14. host: localhost
  15. port: 5672
  16. username: consumer
  17. password: 123456
  18. bindings: # 服务的整合处理
  19. input: # 这个名字是一个通道的名称
  20. destination: myExchange # 表示要使用的Exchange名称定义
  21. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  22. binder: myRabbit # 关联MQ服务
  23. group: consunmerA
  24. rabbit:
  25. bindings:
  26. input:
  27. consumer:
  28. prefetch: 3 #当有消息产生时,配置这个的消费端会一次性从MQ中读取三条信息,然后依次消费,消费完成后会再次去MQ中拉取消息,如不配置则有消息就会接受

以上代码见 code 上述代码在demo3 YML文件下

测试流程:

  • 创建两个消费端,
  • A消费端配置prefetch:3,消费速度为2秒一条消息 (控制速度我使用TimeUnit.SECONDS.sleep(2);)
  • B消费端配置prefetch:7,消费速度不限制(B消费端可不配置)
  • 生产端生产10条消息 (生产者改动见 code demo2包下
  • 这时A消费端因速度慢(手动sleep),只会消费3条,B消费端会消费7条

消费者数量配置

消费顺序:当启动多个消费端服务器或多个消费线程后,消息消费顺序将被打乱

注:每个消费端开几个消费线程来消费消息

配置见下方 concurrency

  1. server:
  2. port: 7001
  3. spring:
  4. application:
  5. name: concumer
  6. cloud:
  7. stream:
  8. binders: # 配置使用mq的信息;
  9. myRabbit: #给Binder定义的名称,⽤于后⾯的关联
  10. type: rabbit # 消息组件类型
  11. environment: # mq环境配置
  12. spring:
  13. rabbitmq:
  14. host: localhost
  15. port: 5672
  16. username: consumer
  17. password: 123456
  18. bindings: # 服务的整合处理
  19. input: # 这个名字是一个通道的名称
  20. destination: myExchange # 表示要使用的Exchange名称定义
  21. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  22. binder: myRabbit # 关联MQ服务
  23. group: consunmerA
  24. consumer:
  25. concurrency: 2 # 初始/最少/空闲时 消费者数量。默认1
  26. rabbit:
  27. bindings:
  28. input:
  29. consumer:
  30. prefetch: 3

测试流程:

  • 配置concurrency: 2
  • 消费端消费时输出线程名字,便于观察
  • 当生产者发送10个请求后,会看到有2个线程在消费

消费者数量弹性配置

注:类似线程池,核心线程数和动态扩容上限

配置见下方 maxConcurrency

  1. server:
  2. port: 7001
  3. spring:
  4. application:
  5. name: concumer
  6. cloud:
  7. stream:
  8. binders: # 配置使用mq的信息;
  9. myRabbit: #给Binder定义的名称,⽤于后⾯的关联
  10. type: rabbit # 消息组件类型
  11. environment: # mq环境配置
  12. spring:
  13. rabbitmq:
  14. host: localhost
  15. port: 5672
  16. username: consumer
  17. password: 123456
  18. bindings: # 服务的整合处理
  19. input: # 这个名字是一个通道的名称
  20. destination: myExchange # 表示要使用的Exchange名称定义
  21. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  22. binder: myRabbit # 关联MQ服务
  23. group: consunmerA
  24. consumer:
  25. concurrency: 1 # 初始/最少/空闲时 消费者数量。默认1
  26. rabbit:
  27. bindings:
  28. input:
  29. consumer:
  30. maxConcurrency: 5 #最多有五个,当消息积压过多,MQ会自动开放消费者线程数量,使用后会慢慢销毁 至 concurrency数量
  31. prefetch: 3

以上代码见 code 上述代码在demo4 YML文件及demo4包下代码

手动确认消息

消费者yml

  1. #新增配置,开启手动确认
  2. acknowledge-mode: manual

关键代码

  1. channel.basicAck(deliveryTag, false);
  1. package com.project.stream_consumer.controller;
  2. import com.rabbitmq.client.Channel;
  3. import org.springframework.amqp.support.AmqpHeaders;
  4. import org.springframework.cloud.stream.annotation.EnableBinding;
  5. import org.springframework.cloud.stream.annotation.StreamListener;
  6. import org.springframework.cloud.stream.messaging.Sink;
  7. import org.springframework.messaging.Message;
  8. import org.springframework.messaging.handler.annotation.Header;
  9. import org.springframework.stereotype.Component;
  10. import java.io.IOException;
  11. import java.util.concurrent.TimeUnit;
  12. @Component
  13. @EnableBinding(Sink.class)
  14. public class ConsunmerController {
  15. @StreamListener(Sink.INPUT)
  16. public void input(Message<String> message,@Header(AmqpHeaders.CHANNEL) Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag){
  17. try {
  18. TimeUnit.SECONDS.sleep(2);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println("消费者A:"+message.getPayload());
  23. try {
  24. System.out.println("消费者A消费了:" + payload);
  25. channel.basicAck(deliveryTag, true);//消息手动确认,如配置了预读取prefetch=3,在消费完这三条后未确认,将不再去MQ中拉取;
  26. //如果第二个参数配置为true ,生产者一次发送1~10,但2这条消息不去basicAck确认消费,在消费后一条时,会默认消费掉2这条消息;
  27. //配置为false时,basicAck只会确认传入的消息,之前未确认的消息不会消费
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }

以上代码见 code 上述代码在demo5 YML文件及demo5包下代码

MQ优化目标

上述消费者数量配置、预读取配置可提高消费的效率,那设置多少合适?可参考下面的方式

  • 访问http://127.0.0.1:15672,进入RabbitMQ管理页面,
  • 进入Queues Tab页,找到对应的消息,进入

  • 进入对应消息后,会看到下图页面

  • 最终看的就是上图中“消费者利用率”,值为0~100%,数值越高,证明消费者资源利用度越高,或者说数值越高越好,如果这个数值已经很高,但还有很多消息积压,就需要增加消费者了

多Exchange配置

每个项目会用到多个主题,下面是多主题配置方式

生产者项目

  • 参考Source.class,新增配置
  1. import org.springframework.cloud.stream.annotation.Output;
  2. import org.springframework.messaging.MessageChannel;
  3. public interface MyOutPut {
  4. String MYOUTPUT2 = "myoutput2";
  5. @Output(MyOutPut.MYOUTPUT2)
  6. MessageChannel myoutput2();
  7. String MYOUTPUT1 = "myoutput1";
  8. @Output(MyOutPut.MYOUTPUT1)
  9. MessageChannel myoutput1();
  10. }
  • 修改YML文件

    bindings下绑定了多组,改动点见bindings下

    1. server:
    2. port: 8000
    3. spring:
    4. application:
    5. name: producer
    6. cloud:
    7. stream:
    8. binders: # 配置使用mq的信息;
    9. myRabbit: # 自定义的名称,用于下方bindings -> binder
    10. type: rabbit # 消息组件类型
    11. environment: # mq环境配置
    12. spring:
    13. rabbitmq:
    14. host: 192.168.1.2
    15. port: 5672
    16. username: admin
    17. password: 123456
    18. bindings: # 服务的整合处理
    19. myoutput2: # 这个是上面新建配置类MyOutPut中的名字
    20. destination: myoutput2_change # 表示要使用的Exchange名称定义
    21. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    22. binder: myRabbit # 设置要绑定的消息服务的具体设置
    23. myoutput1: # 这个是上面新建配置类的名字
    24. destination: myoutput1_change # 表示要使用的Exchange名称定义
    25. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    26. binder: myRabbit # 设置要绑定的消息服务的具体设置
  • 实现类修改

    1. @EnableBinding(MyOutPut.class) //修改为自定义的配置
    2. public class MessageProducerImpl implements MessageProducer {
    3. @Autowired
    4. private MyOutPut myOutPut;//引入自定义的
    5. @Override
    6. public boolean send(int num,int tag) {
    7. boolean send=true;
    8. if(tag==2)
    9. send=myOutPut.myoutput2().send(MessageBuilder.withPayload("output_send>>"+num).build());//根据业务不同调用不同output
    10. if(tag==1)
    11. send=myOutPut.myoutput1().send(MessageBuilder.withPayload("myoutput_send>>"+num).build());
    12. return send;
    13. }
    14. }

以上代码见 code 上述代码在demo3 YML文件及demo3包下代码

消费者项目

  • 参考Sink.class,新增配置

    1. import org.springframework.cloud.stream.annotation.Input;
    2. import org.springframework.messaging.SubscribableChannel;
    3. public interface MyInPut {
    4. String MYINPUT1 = "myinput1";
    5. @Input(MyInPut.MYINPUT1)
    6. SubscribableChannel myinput1();
    7. String MYINPUT2 = "myinput2";
    8. @Input(MyInPut.MYINPUT2)
    9. SubscribableChannel myinput2();
    10. }
  • 修改YML文件

    bindings下配置了多组

    1. server:
    2. port: 7000
    3. spring:
    4. application:
    5. name: concumer
    6. cloud:
    7. stream:
    8. binders: # 配置使用mq的信息;
    9. myRabbit: #给Binder定义的名称,⽤于后⾯的关联
    10. type: rabbit # 消息组件类型
    11. environment: # mq环境配置
    12. spring:
    13. rabbitmq:
    14. host: localhost
    15. port: 5672
    16. username: consumer
    17. password: 123456
    18. bindings: # 服务的整合处理
    19. myinput1: # 这个是上面新建配置类MyInPut中的名字
    20. destination: myoutput1_change # 表示要使用的Exchange名称定义
    21. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    22. binder: myRabbit # 关联MQ服务
    23. group: consunmerA
    24. myinput2: # 这个名字是一个通道的名称
    25. destination: myoutput2_change # 表示要使用的Exchange名称定义
    26. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    27. binder: myRabbit # 关联MQ服务
    28. rabbit:
    29. bindings:
    30. myinput1:
    31. consumer:
    32. acknowledge-mode: manual
  • 修改消费端接收信息实现类

    1. import com.project.stream_consumer.mq_config.MyInPut;
    2. import com.rabbitmq.client.Channel;
    3. import org.springframework.amqp.support.AmqpHeaders;
    4. import org.springframework.cloud.stream.annotation.EnableBinding;
    5. import org.springframework.cloud.stream.annotation.StreamListener;
    6. import org.springframework.messaging.Message;
    7. import org.springframework.messaging.handler.annotation.Header;
    8. import org.springframework.stereotype.Component;
    9. import java.io.IOException;
    10. @Component
    11. @EnableBinding(MyInPut.class) //引入上面新增的
    12. public class ConsunmerController {
    13. @StreamListener(MyInPut.MYINPUT1) //指定监听类中的哪个通道
    14. public void input1(Message<String> message,@Header(AmqpHeaders.CHANNEL) Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag){
    15. System.out.println("input1:"+message.getPayload());
    16. try {
    17. channel.basicAck(deliveryTag, false);
    18. } catch (IOException e) {
    19. e.printStackTrace();
    20. }
    21. }
    22. @StreamListener(MyInPut.MYINPUT2)
    23. public void input2(Message<String> message){
    24. System.out.println("input2:"+message.getPayload());
    25. }
    26. }
  • 测试

    生产端调用不同通道发送消息后,对应消费端对应通道的监听就会收到消息

    以上代码见 code 上述代码在demo6 YML文件及demo6包下代码

集成Kafka

注:下面默认已经安装好KafKa环境

修改生产者项目

pom

新增

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-stream-kafka</artifactId>
  4. <version>3.0.10.RELEASE</version>
  5. </dependency>

修改yml

标***的为新增配置

  1. server:
  2. port: 8000
  3. spring:
  4. application:
  5. name: producer
  6. cloud:
  7. stream:
  8. default-binder: mykafka #***设置项目启动默认绑定哪个MQ,在同时配置多种MQ必须配置,否则报错
  9. binders: # 配置使用mq的信息;
  10. #***kafka环境配置start
  11. mykafka:
  12. type: kafka
  13. environment:
  14. spring:
  15. cloud:
  16. stream:
  17. kafka:
  18. binder:
  19. brokers: http://192.168.1.5:9092 #kafka地址,多个可用,隔开
  20. auto-add-partitions: true
  21. auto-create-topics: true
  22. min-partition-count: 1
  23. #***kafka环境配置end
  24. myRabbit: # 自定义的名称,用于下方bindings -> binder
  25. type: rabbit # 消息组件类型
  26. environment: # mq环境配置
  27. spring:
  28. rabbitmq:
  29. host: 192.168.1.2
  30. port: 5672
  31. username: admin
  32. password: 123456
  33. bindings: # 服务的整合处理
  34. myoutput2: # 这个名字是一个通道的名称
  35. destination: myoutput2_change # 表示要使用的Exchange名称定义
  36. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  37. binder: myRabbit # 设置要绑定的消息服务的具体设置
  38. myoutput1: # 这个名字是一个通道的名称
  39. destination: myoutput1_change # 表示要使用的Exchange名称定义
  40. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  41. binder: myRabbit # 设置要绑定的消息服务的具体设置
  42. #***kafka配置start
  43. mykafkaoutput: # 这个名字是一个通道的名称
  44. destination: my_kafka_topic # 表示要使用的topic名称定义
  45. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  46. binder: mykafka # 设置要绑定的消息服务的具体设置
  47. #***kafka配置end

修改代码

  • MyOutPut.java

    1. package com.project.stream_producer.mq_config;
    2. import org.springframework.cloud.stream.annotation.Output;
    3. import org.springframework.messaging.MessageChannel;
    4. public interface MyOutPut {
    5. String MYOUTPUT2 = "myoutput2";
    6. @Output(MyOutPut.MYOUTPUT2)
    7. MessageChannel myoutput2();
    8. String MYOUTPUT1 = "myoutput1";
    9. @Output(MyOutPut.MYOUTPUT1)
    10. MessageChannel myoutput1();
    11. //新增下面三行
    12. String MYKAFKAOUTPUT = "mykafkaoutput";
    13. @Output(MyOutPut.MYKAFKAOUTPUT)
    14. MessageChannel mykafkaoutput();
    15. }
  • MessageProducerByMyOutPutImpl.java

    1. import com.project.stream_producer.mq_config.MyOutPut;
    2. import com.project.stream_producer.service.MessageProducer;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.cloud.stream.annotation.EnableBinding;
    5. import org.springframework.messaging.support.MessageBuilder;
    6. import javax.annotation.Resource;
    7. @EnableBinding(MyOutPut.class)
    8. public class MessageProducerByMyOutPutImpl implements MessageProducer {
    9. @Autowired
    10. private MyOutPut myOutPut;//消息发送管道
    11. @Override
    12. public boolean send(int num,int tag) {
    13. boolean send=true;
    14. if(tag==2)
    15. send=myOutPut.myoutput2().send(MessageBuilder.withPayload("output_send>>"+num).build());
    16. if(tag==1)
    17. send=myOutPut.myoutput1().send(MessageBuilder.withPayload("myoutput_send>>"+num).build());
    18. if(tag==3) //新增kafka发送方法
    19. send=myOutPut.mykafkaoutput().send(MessageBuilder.withPayload("mykafka_send>>"+num).build());
    20. return send;
    21. }
    22. }

以上代码见 code 上述代码在demo4 YML文件及demo4包下代码

修改消费者项目

pom

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-stream-kafka</artifactId>
  4. <version>3.0.10.RELEASE</version>
  5. </dependency>

yml

标***的为新增配置

  1. server:
  2. port: 7000
  3. spring:
  4. application:
  5. name: concumer
  6. cloud:
  7. stream:
  8. default-binder: mykafka
  9. binders: # 配置使用mq的信息;
  10. #***kafka环境配置start
  11. mykafka:
  12. type: kafka
  13. environment:
  14. spring:
  15. cloud:
  16. stream:
  17. kafka:
  18. binder:
  19. brokers: http://192.168.1.5:9092 #kafka地址,多个可用,隔开
  20. #***kafka环境配置end
  21. myRabbit: #给Binder定义的名称,⽤于后⾯的关联
  22. type: rabbit # 消息组件类型
  23. environment: # mq环境配置
  24. spring:
  25. rabbitmq:
  26. host: localhost
  27. port: 5672
  28. username: consumer
  29. password: 123456
  30. bindings: # 服务的整合处理
  31. myinput1: # 这个名字是一个通道的名称
  32. destination: myoutput1_change # 表示要使用的Exchange名称定义
  33. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  34. binder: myRabbit # 关联MQ服务
  35. group: consunmerA
  36. consumer:
  37. concurrency: 1 # 初始/最少/空闲时 消费者数量。默认1
  38. myinput2: # 这个名字是一个通道的名称
  39. destination: myoutput2_change # 表示要使用的Exchange名称定义
  40. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  41. binder: myRabbit # 关联MQ服务
  42. group: consunmerA
  43. consumer:
  44. concurrency: 1
  45. #***kafka配置start
  46. mykafkainput: # 这个名字是一个通道的名称
  47. destination: my_kafka_topic # 表示要使用的topic名称定义
  48. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  49. binder: mykafka # 关联MQ服务
  50. #***kafka配置end
  51. rabbit:
  52. bindings:
  53. myinput1:
  54. consumer:
  55. maxConcurrency: 1
  56. prefetch: 3
  57. acknowledge-mode: manual

修改代码

  • MyInPut.java

    1. import org.springframework.cloud.stream.annotation.Input;
    2. import org.springframework.messaging.SubscribableChannel;
    3. public interface MyInPut {
    4. String MYINPUT1 = "myinput1";
    5. @Input(MyInPut.MYINPUT1)
    6. SubscribableChannel myinput1();
    7. String MYINPUT2 = "myinput2";
    8. @Input(MyInPut.MYINPUT2)
    9. SubscribableChannel myinput2();
    10. //新增如下三行
    11. String MYKAFKAINPUT = "mykafkainput";
    12. @Input(MyInPut.MYKAFKAINPUT)
    13. SubscribableChannel mykafkainput();
    14. }
  • ConsunmerController.java

    1. package com.project.stream_consumer.controller;
    2. import com.project.stream_consumer.mq_config.MyInPut;
    3. import com.rabbitmq.client.Channel;
    4. import org.springframework.amqp.support.AmqpHeaders;
    5. import org.springframework.beans.factory.annotation.Value;
    6. import org.springframework.cloud.stream.annotation.EnableBinding;
    7. import org.springframework.cloud.stream.annotation.StreamListener;
    8. import org.springframework.messaging.Message;
    9. import org.springframework.messaging.handler.annotation.Header;
    10. import org.springframework.stereotype.Component;
    11. import java.io.IOException;
    12. @Component
    13. @EnableBinding(MyInPut.class)
    14. public class ConsunmerController {
    15. @Value("${spring.application.name}")
    16. String name;
    17. @StreamListener(MyInPut.MYINPUT1)
    18. public void input1(Message<String> message,@Header(AmqpHeaders.CHANNEL) Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag){
    19. System.out.println("input1:"+message.getPayload());
    20. try {
    21. channel.basicAck(deliveryTag, false);
    22. } catch (IOException e) {
    23. e.printStackTrace();
    24. }
    25. }
    26. @StreamListener(MyInPut.MYINPUT2)
    27. public void input2(Message<String> message){
    28. System.out.println("input2:"+message.getPayload());
    29. }
    30. //新增下面方法
    31. @StreamListener(MyInPut.MYKAFKAINPUT)
    32. public void input3(Message<String> message){
    33. System.out.println(name+">kafka:"+message.getPayload());
    34. }
    35. }

以上代码见 code 上述代码在demo7 YML文件及demo7包下代码

测试

生产者执行send请求,消费者即可接收消息

重复消费

同上方rabbitMq 分组配置,设置效果同上

手动确认消息

  • 修改消费者yml

    标***的为新增配置

    1. server:
    2. port: 7000
    3. spring:
    4. application:
    5. name: concumer1
    6. cloud:
    7. stream:
    8. default-binder: mykafka
    9. binders: # 配置使用mq的信息;
    10. mykafka:
    11. type: kafka
    12. environment:
    13. spring:
    14. cloud:
    15. stream:
    16. kafka:
    17. binder:
    18. brokers: http://192.168.1.5:9092 #kafka地址,多个可用,隔开
    19. auto-add-partitions: true
    20. auto-create-topics: true
    21. min-partition-count: 1
    22. myRabbit: #给Binder定义的名称,⽤于后⾯的关联
    23. type: rabbit # 消息组件类型
    24. environment: # mq环境配置
    25. spring:
    26. rabbitmq:
    27. host: 192.168.1.2
    28. port: 5672
    29. username: consumer
    30. password: 123456
    31. bindings: # 服务的整合处理
    32. myinput1: # 这个名字是一个通道的名称
    33. destination: myoutput1_change # 表示要使用的Exchange名称定义
    34. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    35. binder: myRabbit # 关联MQ服务
    36. group: consunmerA
    37. consumer:
    38. concurrency: 1 # 初始/最少/空闲时 消费者数量。默认1
    39. myinput2: # 这个名字是一个通道的名称
    40. destination: myoutput2_change # 表示要使用的Exchange名称定义
    41. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    42. binder: myRabbit # 关联MQ服务
    43. group: consunmerA
    44. consumer:
    45. concurrency: 1 # 初始/最少/空闲时 消费者数量。默认1
    46. mykafkainput: # 这个名字是一个通道的名称
    47. destination: my_kafka_topic # 表示要使用的Exchange名称定义
    48. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    49. binder: mykafka # 关联MQ服务
    50. group: consunmerA
    51. rabbit:
    52. bindings:
    53. myinput1:
    54. consumer:
    55. maxConcurrency: 1
    56. prefetch: 3
    57. acknowledge-mode: manual
    58. # ***手动确认消息start
    59. kafka:
    60. bindings:
    61. mykafkainput:
    62. consumer:
    63. auto-commit-offset: false
    64. # ***手动确认消息end
  • 修改消费者监听方法

    1. @StreamListener(MyInPut.MYKAFKAINPUT)
    2. public void input3(Message<String> message) throws InterruptedException {
    3. Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
    4. if (acknowledgment != null) {
    5. System.out.println(name+">kafka:"+message.getPayload());//接收到的消息
    6. acknowledgment.acknowledge();//确认收到消息
    7. }
    8. }

    以上代码见 code 上述代码在demo8 YML文件及demo8包下代码

  • 测试

    1. 关闭消费者,生产者发送消息

    2. 通过命令查看topic消费情况

      1. ./kafka-consumer-groups.sh --bootstrap-server 192.168.1.5:9092 --describe --group consunmerA
      2. # kafka地址 显示详细信息 消费组名

      CURRENT-OFFSET:最终消费offset,只有自动消费或手动确认消费后才会更新

      LOG-END-OFFSET:总共消息offset,每插入一条消息,就会更新

    3. 当消费者启动后会收到未消费的消息

      • 手动确认消息模式下:

        未调用acknowledge()方法,当消费者服务重启时,会再次重新消费;

    4. 注:当生产者依次发送1、2、3、4消息,1、2、3未确认。4确认了,那么1、2、3也会被确认

死信队列

上面手动确认消息模式测试中发现会出现1、2、3丢失(消费报错)情况,下面可通过DLQ队列将失败的队列保存起来,或通过代码自行处理

  • 消费者修改yml

    标***的为新增配置

    1. server:
    2. port: 7000
    3. spring:
    4. application:
    5. name: concumer1
    6. cloud:
    7. stream:
    8. default-binder: mykafka
    9. binders: # 配置使用mq的信息;
    10. mykafka:
    11. type: kafka
    12. environment:
    13. spring:
    14. cloud:
    15. stream:
    16. kafka:
    17. binder:
    18. brokers: http://192.168.1.5:9092 #kafka地址,多个可用,隔开
    19. auto-add-partitions: true
    20. auto-create-topics: true
    21. min-partition-count: 1
    22. myRabbit: #给Binder定义的名称,⽤于后⾯的关联
    23. type: rabbit # 消息组件类型
    24. environment: # mq环境配置
    25. spring:
    26. rabbitmq:
    27. host: 192.168.1.2
    28. port: 5672
    29. username: consumer
    30. password: 123456
    31. bindings: # 服务的整合处理
    32. myinput1: # 这个名字是一个通道的名称
    33. destination: myoutput1_change # 表示要使用的Exchange名称定义
    34. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    35. binder: myRabbit # 关联MQ服务
    36. group: consunmerA
    37. consumer:
    38. concurrency: 1 # 初始/最少/空闲时 消费者数量。默认1
    39. myinput2: # 这个名字是一个通道的名称
    40. destination: myoutput2_change # 表示要使用的Exchange名称定义
    41. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    42. binder: myRabbit # 关联MQ服务
    43. group: consunmerA
    44. consumer:
    45. concurrency: 1 # 初始/最少/空闲时 消费者数量。默认1
    46. mykafkainput: # 这个名字是一个通道的名称
    47. destination: my_kafka_topic # 表示要使用的Exchange名称定义
    48. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    49. binder: mykafka # 关联MQ服务
    50. group: consunmerA
    51. #***定义DLQ队列配置start
    52. mydlqinput: # 这个名字是一个通道的名称
    53. destination: mydlq # 表示要使用的Exchange名称定义
    54. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    55. binder: mykafka # 关联MQ服务
    56. group: mydlq
    57. #***定义DLQ队列配置end
    58. rabbit:
    59. bindings:
    60. myinput1:
    61. consumer:
    62. maxConcurrency: 1
    63. prefetch: 3
    64. acknowledge-mode: manual
    65. kafka:
    66. bindings:
    67. mykafkainput:
    68. consumer:
    69. #***dlq开启start
    70. enableDlq: true #开启DLQ队列
    71. dlqName: mydlq #自定义dlq名称
    72. #***dlq开启end
    73. auto-commit-offset: false
    74. #***定义DLQ队列配置start
    75. mydlqinput: # 这个名字是一个通道的名称
    76. destination: mydlq # 表示要使用的Exchange名称定义
    77. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
    78. binder: mykafka # 关联MQ服务
    79. group: mydlq
    80. #***定义DLQ队列配置end
  • MyInPut.java

    1. package com.project.stream_consumer.demo9.conf;
    2. import org.springframework.cloud.stream.annotation.Input;
    3. import org.springframework.messaging.SubscribableChannel;
    4. public interface MyInPut {
    5. String MYINPUT1 = "myinput1";
    6. @Input(MyInPut.MYINPUT1)
    7. SubscribableChannel myinput1();
    8. String MYINPUT2 = "myinput2";
    9. @Input(MyInPut.MYINPUT2)
    10. SubscribableChannel myinput2();
    11. String MYKAFKAINPUT = "mykafkainput";
    12. @Input(MyInPut.MYKAFKAINPUT)
    13. SubscribableChannel mykafkainput();
    14. //新增如下三行
    15. String MYDLQINPUT = "mydlqinput";
    16. @Input(MyInPut.MYDLQINPUT)
    17. SubscribableChannel mydlqinput();
    18. }
  • 修改消费者监听方法

    1. @StreamListener(MyInPut.MYKAFKAINPUT)
    2. public void input3(Message<String> message) throws Exception {
    3. Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
    4. if (acknowledgment != null) {
    5. String payload = message.getPayload();
    6. System.out.println(name+">kafka:"+payload);
    7. if("mykafka_send>>5".equals(payload)) //当消息满足条件时,故意抛出异常,kafka会重试三次,三次后会推送到DLQ队列
    8. throw new Exception("errorabcd");
    9. acknowledgment.acknowledge();
    10. }
    11. }
    12. @StreamListener(MyInPut.MYDLQINPUT)
    13. public void input4(Message<String> message) throws Exception {//DLQ队列监听
    14. String payload = message.getPayload();
    15. System.out.println(name+">MYDLQINPUT:"+payload);
    16. }

以上代码见 code 上述代码在demo9 YML文件及demo9包下代码

并发

每个消费者开几个线程来消费,开多线程消费会导致消费顺序与发送顺序不一致

消费者修改yml

标***的为新增配置

  1. server:
  2. port: 7000
  3. spring:
  4. application:
  5. name: concumer1
  6. cloud:
  7. stream:
  8. default-binder: mykafka
  9. binders: # 配置使用mq的信息;
  10. mykafka:
  11. type: kafka
  12. environment:
  13. spring:
  14. cloud:
  15. stream:
  16. kafka:
  17. binder:
  18. brokers: http://192.168.1.5:9092 #kafka地址,多个可用,隔开
  19. auto-add-partitions: true
  20. auto-create-topics: true
  21. min-partition-count: 1
  22. myRabbit: #给Binder定义的名称,⽤于后⾯的关联
  23. type: rabbit # 消息组件类型
  24. environment: # mq环境配置
  25. spring:
  26. rabbitmq:
  27. host: 192.168.1.2
  28. port: 5672
  29. username: consumer
  30. password: 123456
  31. bindings: # 服务的整合处理
  32. myinput1: # 这个名字是一个通道的名称
  33. destination: myoutput1_change # 表示要使用的Exchange名称定义
  34. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  35. binder: myRabbit # 关联MQ服务
  36. group: consunmerA
  37. consumer:
  38. concurrency: 1 # 初始/最少/空闲时 消费者数量。默认1
  39. myinput2: # 这个名字是一个通道的名称
  40. destination: myoutput2_change # 表示要使用的Exchange名称定义
  41. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  42. binder: myRabbit # 关联MQ服务
  43. group: consunmerA
  44. consumer:
  45. concurrency: 1 # 初始/最少/空闲时 消费者数量。默认1
  46. mykafkainput: # 这个名字是一个通道的名称
  47. destination: my_kafka_topic # 表示要使用的Exchange名称定义
  48. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  49. binder: mykafka # 关联MQ服务
  50. group: consunmerA
  51. #***并发配置start
  52. consumer:
  53. concurrency: 1
  54. #***并发配置end
  55. mydlqinput: # 这个名字是一个通道的名称
  56. destination: mydlq # 表示要使用的Exchange名称定义
  57. content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
  58. binder: mykafka # 关联MQ服务
  59. group: mydlq
  60. rabbit:
  61. bindings:
  62. myinput1:
  63. consumer:
  64. maxConcurrency: 1
  65. prefetch: 3
  66. acknowledge-mode: manual
  67. kafka:
  68. bindings:
  69. mykafkainput:
  70. consumer:
  71. enableDlq: true
  72. dlqName: mydlq
  73. auto-commit-offset: false

以上代码见 code 上述代码在demo10 YML文件及demo10包下代码

预拉取

不知道为什么,死活就是找不到用SpringCloudStream kafka如何实现想RabbitMq一样预拉取消费,没办法,只能找了kafka另外的一种方式来实现。

修改生产者项目

新增如下配置

  1. package com.project.stream_producer.demo5.conf;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.beans.factory.annotation.Qualifier;
  4. import org.springframework.kafka.core.KafkaTemplate;
  5. import org.springframework.stereotype.Component;
  6. import java.util.concurrent.ExecutionException;
  7. @Component
  8. public class StringProducer {
  9. @Autowired
  10. @Qualifier("stringKafkaTemplate")
  11. private KafkaTemplate<String, String> kafkaTemplate;
  12. public void send(String message) throws ExecutionException, InterruptedException {
  13. kafkaTemplate.send("mykafkainput", message).get();
  14. }
  15. }
  1. package com.project.stream_producer.demo5.conf;
  2. import org.apache.kafka.clients.producer.ProducerConfig;
  3. import org.apache.kafka.common.serialization.StringSerializer;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.kafka.core.DefaultKafkaProducerFactory;
  7. import org.springframework.kafka.core.KafkaTemplate;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. @Configuration
  11. public class StringProducerConfig {
  12. /**
  13. * 配置可以写到配置文件中,此处省略
  14. * @return
  15. */
  16. @Bean
  17. public KafkaTemplate<String, String> stringKafkaTemplate() {
  18. Map<String, Object> configProps = new HashMap<>();
  19. configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "http://10.66.202.189:9092");
  20. configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
  21. configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
  22. // -----------------------------额外配置,可选--------------------------
  23. //重试,0为不启用重试机制
  24. configProps.put(ProducerConfig.RETRIES_CONFIG, 1);
  25. //控制批处理大小,单位为字节
  26. configProps.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
  27. //批量发送,延迟为1毫秒,启用该功能能有效减少生产者发送消息次数,从而提高并发量
  28. configProps.put(ProducerConfig.LINGER_MS_CONFIG, 1);
  29. //生产者可以使用的总内存字节来缓冲等待发送到服务器的记录
  30. configProps.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 1024000);
  31. return new KafkaTemplate<>(new DefaultKafkaProducerFactory<>(configProps));
  32. }
  33. /**
  34. * ----可选参数----
  35. *
  36. * configProps.put(ProducerConfig.ACKS_CONFIG, "1");
  37. * 确认模式, 默认1
  38. *
  39. * acks=0那么生产者将根本不会等待来自服务器的任何确认。
  40. * 记录将立即被添加到套接字缓冲区,并被认为已发送。在这种情况下,不能保证服务器已经收到了记录,
  41. * 并且<code>重试</code>配置不会生效(因为客户端通常不会知道任何故障)。每个记录返回的偏移量总是设置为-1。
  42. *
  43. * acks=1这将意味着领导者将记录写入其本地日志,但不会等待所有追随者的全部确认。
  44. * 在这种情况下,如果领导者在确认记录后立即失败,但在追随者复制之前,记录将会丢失。
  45. *
  46. * acks=all这些意味着leader将等待所有同步的副本确认记录。这保证了只要至少有一个同步副本仍然存在,
  47. * 记录就不会丢失。这是最有力的保证。这相当于acks=-1的设置。
  48. *
  49. *
  50. *
  51. * configProps.put(ProducerConfig.RETRIES_CONFIG, "3");
  52. * 设置一个大于零的值将导致客户端重新发送任何发送失败的记录,并可能出现暂时错误。
  53. * 请注意,此重试与客户机在收到错误后重新发送记录没有什么不同。
  54. * 如果不将max.in.flight.requests.per.connection 设置为1,则允许重试可能会更改记录的顺序,
  55. * 因为如果将两个批发送到单个分区,而第一个批失败并重试,但第二个批成功,则第二批中的记录可能会首先出现。
  56. * 注意:另外,如果delivery.timeout.ms 配置的超时在成功确认之前先过期,则在重试次数用完之前,生成请求将失败。
  57. *
  58. *
  59. * 其他参数:参考:http://www.shixinke.com/java/kafka-configuration
  60. * https://blog.csdn.net/xiaozhu_you/article/details/91493258
  61. */
  62. }
  1. package com.project.stream_producer.demo5.controller;
  2. import com.project.stream_producer.demo5.conf.StringProducer;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.bind.annotation.GetMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import java.util.concurrent.ExecutionException;
  7. @RestController
  8. public class ProducerController {
  9. @Autowired
  10. private StringProducer producer;
  11. @GetMapping("/string")
  12. public String string() throws ExecutionException, InterruptedException {
  13. for (int i = 0; i < 10; i++) {
  14. producer.send("test"+i);
  15. System.out.println(i);
  16. }
  17. return "success";
  18. }
  19. }

以上代码见 code 上述代码在demo5包下代码

修改消费者项目

新增如下代码

  1. package com.project.stream_consumer.demo11.controller;
  2. import com.project.stream_consumer.demo7.conf.MyInPut;
  3. import com.rabbitmq.client.Channel;
  4. import org.apache.kafka.clients.consumer.ConsumerRecord;
  5. import org.springframework.amqp.support.AmqpHeaders;
  6. import org.springframework.beans.factory.annotation.Value;
  7. import org.springframework.cloud.stream.annotation.EnableBinding;
  8. import org.springframework.cloud.stream.annotation.StreamListener;
  9. import org.springframework.kafka.annotation.KafkaListener;
  10. import org.springframework.kafka.support.Acknowledgment;
  11. import org.springframework.kafka.support.KafkaHeaders;
  12. import org.springframework.messaging.Message;
  13. import org.springframework.messaging.handler.annotation.Header;
  14. import org.springframework.stereotype.Component;
  15. import java.io.IOException;
  16. import java.util.List;
  17. import java.util.Optional;
  18. import java.util.concurrent.TimeUnit;
  19. @Component
  20. @EnableBinding(MyInPut.class)
  21. public class ConsunmerController {
  22. @KafkaListener(topics = MyInPut.MYKAFKAINPUT, containerFactory="batchFactory")
  23. public void consumerBatch(List<ConsumerRecord<?, ?>> records, Acknowledgment ack) throws InterruptedException {
  24. System.out.println("接收到消息数量,通过BatchConsumerConfig-》configProps.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 3)配置:"+records.size());
  25. // TimeUnit.SECONDS.sleep(5);
  26. for(ConsumerRecord record: records) {
  27. Optional<?> kafkaMessage = Optional.ofNullable(record.value());
  28. //System.out.println("Received: " + record);
  29. if (kafkaMessage.isPresent()) {
  30. Object message = record.value();
  31. String topic = record.topic();
  32. System.out.println("接收到消息:" + message);
  33. }
  34. }
  35. ack.acknowledge();//确认消费
  36. }
  37. }
  1. package com.project.stream_consumer.demo11.conf;
  2. import org.apache.kafka.clients.consumer.ConsumerConfig;
  3. import org.apache.kafka.common.serialization.StringDeserializer;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.kafka.annotation.EnableKafka;
  7. import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
  8. import org.springframework.kafka.config.KafkaListenerContainerFactory;
  9. import org.springframework.kafka.core.ConsumerFactory;
  10. import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
  11. import org.springframework.kafka.listener.ContainerProperties;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. @Configuration
  15. @EnableKafka
  16. public class BatchConsumerConfig {
  17. /**
  18. * 多线程-批量消费
  19. * @return
  20. */
  21. @Bean
  22. public KafkaListenerContainerFactory<?> batchFactory(){
  23. ConcurrentKafkaListenerContainerFactory<String, String> factory =
  24. new ConcurrentKafkaListenerContainerFactory<>();
  25. factory.setConsumerFactory(consumerFactory());
  26. // 控制多线程消费
  27. // 并发数(如果topic有3各分区。设置成3,并发数就是3个线程,加快消费)
  28. // 不设置setConcurrency就会变成单线程配置, MAX_POLL_RECORDS_CONFIG也会失效,
  29. // 接收的消息列表也不会是ConsumerRecord
  30. //factory.setConcurrency(10);
  31. // poll超时时间
  32. factory.getContainerProperties().setPollTimeout(1500);
  33. factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
  34. // 控制批量消费
  35. // 设置为批量消费,每个批次数量在Kafka配置参数中设置(max.poll.records)
  36. factory.setBatchListener(true);
  37. return factory;
  38. }
  39. public ConsumerFactory<String, String> consumerFactory() {
  40. return new DefaultKafkaConsumerFactory<>(consumerConfigs());
  41. }
  42. /**
  43. * 消费者配置
  44. * @return
  45. */
  46. public Map<String, Object> consumerConfigs() {
  47. Map<String, Object> configProps = new HashMap<>();
  48. // 不用指定全部的broker,它将自动发现集群中的其余的borker, 最好指定多个,万一有服务器故障
  49. configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "http://10.66.202.189:9092");
  50. // key序列化方式
  51. configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
  52. // value序列化方式
  53. configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
  54. // GroupID
  55. configProps.put(ConsumerConfig.GROUP_ID_CONFIG, "consunmerA");
  56. // 批量消费消息数量
  57. configProps.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 3);
  58. // -----------------------------额外配置,可选--------------------------
  59. // 自动提交偏移量
  60. // 如果设置成true,偏移量由auto.commit.interval.ms控制自动提交的频率
  61. // 如果设置成false,不需要定时的提交offset,可以自己控制offset,当消息认为已消费过了,这个时候再去提交它们的偏移量。
  62. // 这个很有用的,当消费的消息结合了一些处理逻辑,这个消息就不应该认为是已经消费的,直到它完成了整个处理。
  63. //configProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
  64. // 自动提交的频率
  65. configProps.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
  66. // Session超时设置
  67. configProps.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "15000");
  68. // 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
  69. // latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录)
  70. // earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录
  71. configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
  72. return configProps;
  73. }
  74. }

以上代码见 code 上述代码在demo11包下代码

说明

当消费者获取消息时,如果未消费消息条数≥配置的预拉取的条数,则拉取指定数量,否则就是剩余未消费记录条数。

一次性拉取消费数量配置

  1. configProps.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 3);//一次性最多拉取三条
  2. factory.setBatchListener(true);//开启

其他

手动消费配置

  1. factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
  2. ack.acknowledge();
  3. //具体含义见ContainerProperties.AckMode源码

按上述配置如果不在yml中配置,SpringCloudStreamKafka会默认去连接127.0.0.1,项目中确定不用SpringCloudStreamKafka,可引入spring的pom,删除SpringCloudStreamKafka pom,然后对上述代码进行引用包、代码进行微调

  1. # 引入下面pom
  2. <dependency>
  3. <groupId>org.springframework.kafka</groupId>
  4. <artifactId>spring-kafka</artifactId>
  5. <version>2.5.5.RELEASE</version>
  6. </dependency>
  7. #删除下面pom
  8. <dependency>
  9. <groupId>org.springframework.cloud</groupId>
  10. <artifactId>spring-cloud-starter-stream-kafka</artifactId>
  11. <version>3.0.10.RELEASE</version>
  12. </dependency>

参考代码

SpringCloudStream(RabbitMQ&Kafka)&Spring-Kafka配置使用的更多相关文章

  1. RabbitMQ与spring集成,配置完整的生产者和消费者

    RabbitMQ与AMQP协议详解可以看看这个 http://www.cnblogs.com/frankyou/p/5283539.html 下面是rabbitMQ和spring集成的配置,我配置了二 ...

  2. RabbitMQ与Spring集成配置

    1.引入相关jar包 //RabbitMQ compile group: 'org.springframework.amqp', name: 'spring-rabbit', version: '1. ...

  3. Spring Kafka中关于Kafka的配置参数

    #################consumer的配置参数(开始)################# #如果'enable.auto.commit'为true,则消费者偏移自动提交给Kafka的频率 ...

  4. 在 Spring Boot 配置 Kafka 安全认证

    spring: kafka: bootstrap-servers: IP:端口 listener: missing-topics-fatal: false properties: sasl: mech ...

  5. 【转载】Understanding When to use RabbitMQ or Apache Kafka

    https://content.pivotal.io/rabbitmq/understanding-when-to-use-rabbitmq-or-apache-kafka RabbitMQ: Erl ...

  6. Spring Kafka和Spring Boot整合实现消息发送与消费简单案例

    本文主要分享下Spring Boot和Spring Kafka如何配置整合,实现发送和接收来自Spring Kafka的消息. 先前我已经分享了Kafka的基本介绍与集群环境搭建方法.关于Kafka的 ...

  7. Spring Kafka整合Spring Boot创建生产者客户端案例

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 创建一个kafka-producer-master的maven工程.整个项目结构如下: ...

  8. Understanding When to use RabbitMQ or Apache Kafka

    https://content.pivotal.io/rabbitmq/understanding-when-to-use-rabbitmq-or-apache-kafka How do humans ...

  9. java架构之路(MQ专题)kafka集群配置和简单使用

    前面我们说了RabbitMQ和RocketMQ的安装和简单的使用,这次我们说一下Kafka的安装配置,后面我会用几个真实案例来说一下MQ的真实使用场景.天冷了,不愿意伸手,最近没怎么写博客了,还请见谅 ...

  10. Docker部署Kafka以及Spring Kafka操作

    从https://hub.docker.com/ 查找kafka 第三个活跃并stars数量多 进去看看使用 我们使用docker-compose来构建镜像 查看使用文档中的docker-compos ...

随机推荐

  1. JAVA多线程学习五:线程范围内共享变量&ThreadLocal

    一.概念 可以将每个线程用到的数据与对应的线程号存放到一个map集合中,使用数据时从这个集合中根据线程号获取对应线程的数据,就可以实现线程范围内共享相同的变量. 二.代码 Runnable中的run( ...

  2. VMware14安装windows7的详细过程

    感谢大佬:https://blog.csdn.net/u012230668/article/details/81701893 一.安装VMware虚拟机,以及下载一份ghost win7系统 下载地址 ...

  3. Java中静态变量与非静态变量的区别

    感谢大佬:https://www.cnblogs.com/liuhuijie/p/9175167.html ①java类的成员变量有俩种: 一种是被static关键字修饰的变量,叫类变量或者静态变量 ...

  4. Java GUI 简单台球游戏模型

    完成效果: 1 package com.neuedu.test; 2 3 import java.awt.Frame; 4 import java.awt.Graphics; 5 import jav ...

  5. Java和Js编码(encodeUrl)解码(decodeUrl)对空格的差异问题

    今天解决一个问题的时候遇到了一个编码解码问题,记录一下. 1. Js用的是encodeURIComponent()方法编码,后面的都以该编码方式处理出来的数据为准. 2. Java用的是URLDeco ...

  6. HTTPStatus(状态码返回)详情

    1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码. 代码 说明 100 (继续) 请求者应当继续提出请求. 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分. 101 (切 ...

  7. LeetCode随缘刷题之无重复字符的最长子串

    欢迎评论区交流. package leetcode.day_12_04; /** * 给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度. * <p> * 示例1: * &l ...

  8. 蟒蛇书学习笔记——Chapter 09 Section 01 创建和使用类

    9.1 创建和使用类 9.1.1 创建Dog类   根据Dog类创建的每个实例都将存储名字和年龄,我们赋予了每条小狗蹲下(sit( ))和打滚(roll_over( ))的能力: class Dog: ...

  9. 通过安装HomeBrew来安装Python3

    首先说什么是HomeBrew? 下面引用简书上一个博客的解释:(博客链接:http://www.jianshu.com/p/d229ac7fe77d) 为什么要使用Homebrew Mac OS X是 ...

  10. 05网络并发 ( GIL+进程池与线程池+协程+IO模型 )

    目录 05 网络并发 05 网络并发