springcloud搭建kafka

该博客介绍了如何在Windows环境下,使用SpringCloud Hoxton2.3.1版本,配合SpringBoot 2.2.5,集成Zookeeper、Kafka以及Eureka来构建分布式系统。文章详细阐述了Kafka启动时的内存问题解决办法,以及Eureka服务注册与发现的配置和使用。此外,还展示了服务提供者、消费者如何通过Kafka进行消息传递,并给出了详细的代码示例。

环境:zk+springcloud+eureka+kafka
系统:windows

springcloud版本:Hoxton 2.3.1
适配版本
springboot版本:2.2.5

各版本对应关系
Release Train/Boot Version
Hoxton /2.2.x
Greenwich /2.1.x
Finchley /2.0.x
Edgware /1.5.x
Dalston /1.5.x

搭建中遇到的问题
1.kafka启动报错如下:
java.io.IOException: Map failed
java.lang.OutOfMemoryError: Map failed
解决:从字面上看是内存不够,尝试更改jvm内存,结果失败。
分析错误日志hs_err_pid10384.log,里面有提到:在64位操作系统上使用64位Java,重新安装64位java解决问题!
在这里插入图片描述
在这里插入图片描述
日志最下方有当前java的信息
在这里插入图片描述

2.Group coordinator promote.localdomain:9092 (id: 2147483647 rack: null) is unavailable or invalid, will attempt rediscovery
解决:消费者没连接上服务,原因是搭建失败。通过分析错误日志hs_err_pid10384.log发现,需要使用64位的java,。详细解决步骤同问题1

步骤:
1.先启动zk,双击zkServer.cmd
在这里插入图片描述
2.启动kafka,bin同级目录命令框中执行一下命令

.inwindowskafka-server-start.bat .configserver.properties

3.搭建springcloud项目
eureka用作Service发现服务
zk用作分布式协同服务,配合kafka使用
txlcn-tm是分布式事务(待完善,不影响整体)
在这里插入图片描述
eureka-service
在这里插入图片描述
导包:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

配置

server:
  port: 8091
eureka:
  instance:
    hostname: localhost #服务注册中心实例的主机名
  server:
    enableSelfPreservation: true #服务端开启自我保护模式。无论什么情况,服务端都会保持一定数量的服务。避免client与server的网络问题,而出现大量的服务被清除。
    renewalPercentThreshold: 0.1 #在设置的时间范围类,期望与client续约的百分比。
  client:
    register-with-eureka: false #实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
    fetch-registry: false #此客户端是否获取eureka服务器注册表上的注册信息,默认为true
service-url: 
  defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #服务地址

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }

}

eureka-provider
在这里插入图片描述
导包

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-kafka</artifactId>
        </dependency>

配置

server:
  port: 8081
spring:
  cloud:
    stream:
      kafka:
        binder:
          brokers: localhost:9092         #Kafka的消息中间件服务器
          zk-nodes: localhost:2181        #Zookeeper的节点,如果集群,后面加,号分隔
          auto-create-topics: true        #如果设置为false,就不会自动创建Topic 有可能你Topic还没创建就直接调用了。
      bindings:
        output:      #这里用stream给我们提供的默认output,后面会讲到自定义output
          destination: stream-demo    #消息发往的目的地
          content-type: text/plain    #消息发送的格式,接收端不用指定格式,但是发送端要
eureka:
  instance:
    appname: eureka-provider
  client:
    service-url:
      defaultZone: http://localhost:8091/eureka/

eureka的服务提供方

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

//rest 风格控制器
@RestController
public class EurekaProviderController {
    @RequestMapping("/getInfo")
    public String getDemoInfo() {
        return "this is a provider service";
    }

    @RequestMapping("/getString")
    public String getDemoInfo1(String userId) {
        return userId + ",this is a provider service";
    }

}

kafka消息发送端

import com.eureka.provider.service.KafkaSendService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class KafkaProviderController {

    @Autowired
    private KafkaSendService kafkaSendService;

    @RequestMapping("/send/{msg}")
    public void send(@PathVariable("msg") String msg) {
        System.out.println("发送消息------->>"+msg);
        kafkaSendService.sendMsg(msg);
    }
}

kafka服务接口

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;

//这个注解给我们绑定消息通道的,Source是Stream给我们提供的,可以点进去看源码,可以看到output和input,这和配置文件中的output,input对应的。
@EnableBinding(Source.class)
public class KafkaSendService {

    @Autowired
    private Source source;

    public void sendMsg(String msg) {
        source.output().send(MessageBuilder.withPayload(msg).build());
    }
}

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaProviderApplication.class, args);
    }

}

eureka-customer
在这里插入图片描述
导包

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-kafka</artifactId>
        </dependency>

配置

server:
  port: 8082
spring:
  cloud:
    stream:
      kafka:
        binder:
          brokers: localhost:9092         #Kafka的消息中间件服务器
          zk-nodes: localhost:2181        #Zookeeper的节点,如果集群,后面加,号分隔
          auto-create-topics: true        #如果设置为false,就不会自动创建Topic 有可能你Topic还没创建就直接调用了。
      bindings:
        input: #input是接收,注意这里不能再像前面一样写output了
          destination: stream-demo #消息接收的目的地
eureka:
  instance:
    appname: eureka-customer
  client:
    service-url:
      defaultZone: http://localhost:8091/eureka/

eureka服务消费方

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class EurekaCustomerController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/test1")
    public String getEurekaServiceInfo() {
        /**
         * exchange(url,type,paras,resutType)
         * url:请求地址
         * type:请求类型 get,post
         * paras:参数
         * resutType:返回值类型
         */
        String url = "http://localhost:8081/getInfo";
        HttpMethod type = HttpMethod.GET;
        RequestEntity<String> paras = null;
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, type, paras, String.class);
        return responseEntity.getBody();
    }

    @RequestMapping("/test2")
    public String getString1() {
        //getForObject 调用无参方法,返回结果为String的方法
        String url = "http://localhost:8081/getInfo";
        String res = restTemplate.getForObject(url, String.class);
        return res;
    }

    @RequestMapping("/test3")
    public String getString2() {
        //getForObject 调用有参方法,路径添加参数。返回结果为String的方法
        String url = "http://localhost:8081/getString?userId=sn001";
        String res = restTemplate.getForObject(url, String.class);
        return res;
    }

}

kafka消息接收端

import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.Message;

//消息接收端,stream给我们提供了Sink,Sink源码里面是绑定input的,要跟我们配置文件的input关联的。
@EnableBinding(Sink.class)
public class KafkaRecieveService {

    @StreamListener(Sink.INPUT)
    public void process(Message<?> message) {
        System.out.println("接收到消息----------->>>"+message.getPayload());
        Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
        if (acknowledgment != null) {
            System.out.println("Acknowledgment provided");
            acknowledgment.acknowledge();
        }
    }
}

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaServer
public class EurekaCustomerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaCustomerApplication.class, args);
    }

    //向spring里注入一个RestTemplate对象
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
<think>我们正在搭建Spring CloudKafka集成的微服务。首先需要明确,Spring Cloud本身并不直接提供与Kafka集成的模块,但我们可以通过Spring Cloud Stream或Spring Kafka来实现。Spring Cloud Stream是一个用于构建消息驱动微服务的框架,它支持多种消息中间件,包括Kafka。而Spring Kafka则是SpringKafka的直接封装。 下面将分别介绍两种方式,并给出示例代码。 方式一:使用Spring Cloud Stream集成Kafka 步骤: 1. 创建Spring Boot项目,添加依赖:Spring Cloud Stream和Kafka的binder。 2. 配置输入输出通道。 3. 编写消息生产者和消费者。 方式二:使用Spring Kafka集成 步骤: 1. 创建Spring Boot项目,添加Spring Kafka依赖。 2. 配置Kafka服务器地址和消费者组等。 3. 使用KafkaTemplate发送消息,使用@KafkaListener消费消息。 由于问题中提到了Spring Cloud,我们更倾向于使用Spring Cloud Stream,因为它提供了与消息中间件无关的统一编程模型,便于切换消息中间件。 下面以Spring Cloud Stream为例,详细说明: 步骤1:创建项目并添加依赖 使用Spring Initializr(https://start.spring.io/)创建一个Spring Boot项目,选择以下依赖: - Spring Cloud Stream - 对于Kafka绑定器,我们选择`spring-cloud-starter-stream-kafka` 或者,在pom.xml中添加: ```xml <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-kafka</artifactId> </dependency> </dependencies> ``` 步骤2:配置application.yml ```yaml spring: cloud: stream: bindings: output: # 生产者通道 destination: test-topic content-type: application/json input: # 消费者通道 destination: test-topic group: myGroup content-type: application/json kafka: binder: brokers: localhost:9092 # Kafka服务器地址 ``` 步骤3:定义通道接口 ```java import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; public interface KafkaChannels { String OUTPUT = "output"; String INPUT = "input"; @Output(OUTPUT) MessageChannel output(); @Input(INPUT) SubscribableChannel input(); } ``` 步骤4:编写消息生产者 在需要发送消息的地方注入MessageChannel,并发送消息。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.messaging.support.MessageBuilder; @EnableBinding(KafkaChannels.class) public class KafkaProducer { @Autowired private MessageChannel output; public void sendMessage(String message) { output.send(MessageBuilder.withPayload(message).build()); } } ``` 步骤5:编写消息消费者 ```java import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.StreamListener; @EnableBinding(KafkaChannels.class) public class KafkaConsumer { @StreamListener(KafkaChannels.INPUT) public void handleMessage(String message) { System.out.println("Received message: " + message); } } ``` 步骤6:在Spring Cloud微服务中使用 在Spring Cloud微服务中,你可以将上述生产者和消费者作为Bean注入,然后在需要的地方调用。 注意:以上示例使用的是Spring Cloud Stream的旧版注解(如@EnableBinding, @StreamListener),在Spring Cloud Stream 3.x之后,推荐使用函数式编程模型。下面给出函数式编程模型的示例: 使用函数式编程模型(推荐): 步骤1:依赖不变 步骤2:配置application.yml,同上,但可以简化通道定义(使用函数式接口) 步骤3:编写一个生产者Bean和一个消费者Bean 生产者: ```java import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; import java.util.function.Supplier; @Service public class KafkaProducer { @Bean public Supplier<String> output() { return () -> "Hello, Kafka!"; // 实际中可能需要动态消息,这里仅示例 } } ``` 消费者: ```java import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; import java.util.function.Consumer; @Service public class KafkaConsumer { @Bean public Consumer<String> input() { return message -> { System.out.println("Received: " + message); }; } } ``` 然后在配置文件中绑定函数: ```yaml spring: cloud: stream: function: definition: output; input # 定义函数名,这里output和input对应上面两个Bean的方法名 bindings: output-out-0: # 函数名output,输出通道(out),索引0(因为函数可能有多个输出,这里只有一个) destination: test-topic input-in-0: # 函数名input,输入通道(in),索引0 destination: test-topic group: myGroup kafka: binder: brokers: localhost:9092 ``` 注意:函数式编程模型中,函数名(如output, input)对应绑定通道,规则为:函数名+方向(in/out)+索引。默认情况下,输出通道是函数名+“-out-” + 索引,输入通道是函数名+“-in-” + 索引。 以上两种方式都可以实现Spring CloudKafka的集成。根据实际需求选择。 另外,关于Kafka配置,如序列化、反序列化、错误处理等,可以在配置文件中进一步设置。 最后,确保Kafka服务已经启动。 相关问题: 1. Spring Cloud Stream与Spring Kafka有什么区别? 2. 在Spring Cloud Stream中如何处理消息序列化? 3. 如何保证Kafka消息的可靠传递? 4. 在微服务架构中,如何利用Kafka实现事件驱动? 5. Spring Cloud Stream函数式编程模型有哪些优势?</think>### Spring CloudKafka集成微服务搭建指南 #### 一、核心组件与依赖 1. **Spring Cloud Stream** 提供统一的消息编程模型,支持Kafka作为消息中间件 2. **Spring Kafka** 直接操作Kafka的底层API 3. **依赖配置** (pom.xml): ```xml <!-- Spring Cloud Stream + Kafka Binder --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-kafka</artifactId> <version>3.2.4</version> </dependency> <!-- Spring Kafka (可选) --> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> ``` #### 二、配置步骤 1. **配置文件** (application.yml) ```yaml spring: cloud: stream: bindings: output: # 生产者通道 destination: orders-topic content-type: application/json input: # 消费者通道 destination: orders-topic group: payment-service content-type: application/json kafka: binder: brokers: localhost:9092 # Kafka集群地址 auto-create-topics: true ``` 2. **消息生产者** ```java @EnableBinding(Source.class) public class OrderProducer { @Autowired private Source source; public void sendOrder(Order order) { source.output().send(MessageBuilder .withPayload(order) .setHeader("orderType", "VIP") .build() ); } } ``` 3. **消息消费者** ```java @EnableBinding(Sink.class) public class PaymentConsumer { @StreamListener(Sink.INPUT) public void handle(Order order, @Header("orderType") String type) { System.out.println("Received order: " + order.getId()); // 处理支付逻辑 } } ``` 4. **实体类示例** ```java public class Order implements Serializable { private Long id; private BigDecimal amount; // getters/setters } ``` #### 三、高级配置 1. **消息分区** ```yaml bindings: output: producer: partition-key-expression: headers['orderId'] # 按订单ID分区 partition-count: 3 # 分区数量 ``` 2. **消息重试机制** ```yaml spring: cloud: stream: bindings: input: consumer: max-attempts: 3 # 最大重试次数 back-off-initial-interval: 1000 # 重试间隔(ms) ``` 3. **死信队列配置** ```yaml kafka: binder: dead-letter-topic-encoding: utf-8 dead-letter-topic-name: orders-topic.DLT ``` #### 四、部署验证 1. **启动Kafka** ```bash # 单节点快速启动 docker run -d --name kafka \ -p 9092:9092 \ -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \ bitnami/kafka ``` 2. **测试消息流** - 发送测试消息:`POST /orders` ```json {"id": 1001, "amount": 99.99} ``` - 查看消费日志: ``` 2023-10-05 INFO: Received order: 1001 ``` #### 五、微服务集成方案 ```mermaid graph LR A[订单服务] -->|Kafka消息| B(订单主题) B --> C[支付服务] B --> D[库存服务] B --> E[日志服务] ``` #### 六、常见问题解决 1. **消息序列化错误** 确保所有服务使用相同的序列化格式: ```yaml spring: cloud: stream: default: contentType: application/json ``` 2. **消费者组冲突** 每个微服务使用独立消费者组: ```yaml spring: cloud: stream: bindings: input: group: ${spring.application.name} # 使用服务名作为组ID ``` 3. **性能优化** ```yaml spring: kafka: listener: concurrency: 3 # 并发消费者数 batch-listener: true # 启用批量消费 ``` > 最佳实践:生产环境建议使用Kubernetes部署Kafka集群,通过Spring Cloud Config统一管理配置[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值