springboot kafka发送消息

  • 场景:kafka发送消息,并且根据消息发送的不同渠道和消息类型(例如发送到WX,DingDing,邮箱),采取不同的线程池处理

1.引入依赖

		<dependency>
			<groupId>org.springframework.kafka</groupId>
			<artifactId>spring-kafka</artifactId>
			<version>2.7.8</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.78</version>
		</dependency>

2.kafka配置信息

sobev.kafka.ip=xxx.x.x.x
sobev.kafka.port=9092
sobev.business.topic.name=sobevBusiness

spring.kafka.bootstrap-servers=${sobev.kafka.ip}:${sobev.kafka.port}
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.auto.offset.reset=earliest
spring.kafka.consumer.auto-commit-interval=1000
spring.kafka.consumer.enable-auto-commit=true

3.发送消息工具类

@Component
@Slf4j
public class KafkaUtils {
    @Autowired
     private KafkaTemplate kafkaTemplate;
    /**
     * 发送kafka消息
     */
    public void send(String topicName, String jsonMessage) {
        kafkaTemplate.send(topicName, jsonMessage);
    }
}

4.注册kafka消息监听器接收消息
由于存在多种消息发送渠道和消息类型,因此需要多个监听器监听不同渠道的不同类型,但是不着急,不用急着把所有类型都写一个Listener

//不同的渠道不同的消息类型都设为单独的类型,由不同consumer消费
public class GroupIdMappingUtils {
    /**
     * (不同的渠道不同的消息类型拥有自己的groupId)
     */
    public static List<String> getAllGroupIds() {
        List<String> groupIds = new ArrayList<>();
        for (ChannelType channelType : ChannelType.values()) {
            for (MessageType messageType : MessageType.values()) {
                groupIds.add(channelType.getCodeEn() + "." + messageType.getCodeEn());
            }
        }
        return groupIds;
    }
}

定义一个kafka消息监听器,
注意注解@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
prototype类型 意味着每次请求类都将返回一个新的类,利用这个特性,我们根据消息的发送渠道,类型返回多个KafkaReceiver类,并用GROUP_ID区分他们

@Component
//prototype类型 意味着每次请求类都将返回一个新的类
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Slf4j
public class KafkaReceiver {

    @Autowired
    private TaskPendingHolder taskPendingHolder;

    @KafkaListener(topics = "#{'${sobev.business.topic.name}'}")
    public void consumer(ConsumerRecord<?, String> consumerRecord, @Header(KafkaHeaders.GROUP_ID) String topicGroupId){
        Optional<String> kafkaMessage = Optional.of(consumerRecord.value());
        if(kafkaMessage.isPresent()){
            String s = kafkaMessage.get();
            //具体消息类
            TaskInfo taskInfo = JSON.parseObject(s, TaskInfo.class);
            //如果消息的groupId和KafkaHeaders.GROUP_ID一样,则获取特定线程池发送执行发送消息任务
            if((taskInfo.getSendChannel() + "." + taskInfo.getMsgType()).equals(topicGroupId)){
                System.out.println(topicGroupId + " :" + s);
                //根据当前 topicGroupId 路由到不同的线程池处理
              taskPendingHolder.route(topicGroupId).execute(具体任务...)
            }
        }
    }
}

根据prototype的特性,注册多个监听器

@Service
public class KafkaReceiverStarter {

    @Autowired
    ApplicationContext applicationContext;
    
    @Autowired
    private TaskPendingHolder taskPendingHolder;

    private static List<String> groupIds = GroupIdMappingUtils.getAllGroupIds();

    private static Integer idx = 0;

    /**
     * @Receiver是Prototype类型,每次请求创建新的
     */
    @PostConstruct
    public void init(){
        for (int i = 0; i < groupIds.size(); i++) {
            applicationContext.getBean(KafkaReceiver.class);
        }
    }
    /**
     * 在执行@KafkaListener解析之前都会调用增强器
     */
    @Bean
    public static KafkaListenerAnnotationBeanPostProcessor.AnnotationEnhancer groupIdEnhancer(){
        return (attrs, element) -> {
        //设置@KafkaListener内的groupId属性
            attrs.put("groupId", groupIds.get(idx++));
            return attrs;
        };
    }
}

定义线程池持有容器

@Component
public class TaskPendingHolder {
    private Map<String, ExecutorService> taskPendingHolder = new HashMap<>(32);

    /**
     * 获取得到所有的groupId
     */
    private static List<String> groupIds = GroupIdMappingUtils.getAllGroupIds();

    /**
     * 给每个渠道,每种消息类型初始化一个线程池
     */
    @PostConstruct
    public void init() {
        for (String groupId : groupIds) {
        //自定义线程池生成器 根据groupId生成不同的线程池
            MyExecutor executor = MyThreadPoolConfig.getExecutor(groupId);
            
            taskPendingHolder.put(groupId, executor);
        }
    }
    /**
     * 得到对应的线程池
     */
    public ExecutorService route(String groupId) {
        return taskPendingHolder.get(groupId);
    }


}

未完。。。

参考于github austin 消息推送平台

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中使用Kafka进行消息传递时,可以通过压缩消息来减少网络流量和磁盘空间的使用。Kafka提供了多种压缩算法,包括gzip,snappy和lz4。下面是使用gzip压缩消息的示例: 1. 添加依赖 在项目的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>2.7.2</version> </dependency> ``` 2. 配置Kafka生产者 在Spring Boot应用程序中,可以使用Spring Kafka提供的KafkaTemplate来发送消息。在KafkaTemplate中可以配置压缩类型和压缩等级。以下是一个示例配置: ```java @Configuration @EnableKafka public class KafkaProducerConfig { @Bean public ProducerFactory<String, String> producerFactory() { Map<String, Object> props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "gzip"); // 压缩类型 props.put(ProducerConfig.COMPRESSION_LEVEL_CONFIG, "9"); // 压缩等级 return new DefaultKafkaProducerFactory<>(props); } @Bean public KafkaTemplate<String, String> kafkaTemplate() { return new KafkaTemplate<>(producerFactory()); } } ``` 在上面的示例中,我们使用gzip压缩类型和压缩等级9来配置Kafka生产者。 3. 发送压缩消息 可以使用KafkaTemplate发送压缩消息。以下是一个示例: ```java @Service public class KafkaProducerService { @Autowired private KafkaTemplate<String, String> kafkaTemplate; public void sendMessage(String message) { kafkaTemplate.send("test-topic", message); } } ``` 在上面的示例中,我们使用KafkaTemplate发送了一个消息。如果配置了压缩类型和压缩等级,那么发送消息会被自动压缩。 4. 配置Kafka消费者 在消费者端,需要配置解压缩器来解压缩消息。以下是一个示例配置: ```java @Configuration @EnableKafka public class KafkaConsumerConfig { @Bean public ConsumerFactory<String, String> consumerFactory() { Map<String, Object> props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group"); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000"); props.put(ConsumerConfig.COMPRESSION_TYPE_CONFIG, "gzip"); // 解压缩器 return new DefaultKafkaConsumerFactory<>(props); } @Bean public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(consumerFactory()); return factory; } } ``` 在上面的示例中,我们使用gzip解压缩器来配置Kafka消费者。 5. 接收压缩消息 可以使用@KafkaListener注解来接收压缩消息。以下是一个示例: ```java @Service public class KafkaConsumerService { @KafkaListener(topics = "test-topic", groupId = "test-group") public void receiveMessage(String message) { System.out.println("Received message: " + message); } } ``` 在上面的示例中,我们使用@KafkaListener注解来接收消息。如果消息被压缩了,那么Spring Kafka会自动解压缩消息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值