Spring Boot 2.x 快速集成整合消息中間件 Kafka

歡迎關(guān)注個(gè)人微信公眾號(hào): 小哈學(xué)Java, 每日推送 Java 領(lǐng)域干貨文章,關(guān)注即免費(fèi)無(wú)套路附送 100G 海量學(xué)習(xí)、面試資源喲!!

個(gè)人網(wǎng)站: https://www.exception.site/springboot/spring-boot2-kafka

springbootkafka.png

什么是 Kafka?

Kafka 是 Apache 基金會(huì)開(kāi)源的一個(gè)分布式發(fā)布 - 訂閱消息中間件,流處理平臺(tái)。 它起源于 LinkedIn,由 Scala 和 Java兩種語(yǔ)言編寫(xiě)而成。于 2011 年成為 Apache 項(xiàng)目,2012 成為 Apache 基金會(huì)下頂級(jí)項(xiàng)目。

Kafka 專(zhuān)為分布式高吞吐系統(tǒng)而設(shè)計(jì)。相比較其他消息中間件,如 RabbitMq 等,Kafka 具有更好的吞吐量,內(nèi)置分區(qū),復(fù)制和固有的容錯(cuò)能力,使得它非常適合應(yīng)用在大數(shù)據(jù)領(lǐng)域。另外,Kafka 還支持離線、在線消費(fèi)消息。

為什么要用 Kafka

  • 低延遲 - Kafka 支持低延遲消息傳遞,速度極快,能達(dá)到 200w 寫(xiě)/秒;
  • 高性能 - Kafka對(duì)于消息的發(fā)布、訂閱都具有高吞吐量。即使存儲(chǔ)了 TB 級(jí)的消息,依然能夠保證穩(wěn)定的性能;
  • 可靠性 - Kafka 是分布式,分區(qū),復(fù)制和容錯(cuò)的,保證零停機(jī)和零數(shù)據(jù)丟失。
  • 可拓展性 - Kafka 支持集群水平拓展。
  • 耐用性 - Kafka 使用"分布式提交日志",消息能夠快速的持久化的磁盤(pán)上。
image

Kafka 環(huán)境安裝

接下來(lái),小哈為大家演示一下,在 Linux 系統(tǒng)中,采用最簡(jiǎn)單的單機(jī)安裝方式, 因?yàn)楸疚闹攸c(diǎn)還是介紹 Spring Boot 2.x 快速集成整合 Kafka.

下載 Kafka

訪問(wèn) Kafka 官網(wǎng) http://kafka.apache.org/downloads,下載 tgz 包, 這里演示版本為最新的 2.3.0 版本。

image

解壓,進(jìn)入目錄

下載下來(lái)過(guò)后,放置到指定位置,執(zhí)行命令解壓:

tar -zxvf kafka_2.11-2.3.0.tgz 

解壓完成后,進(jìn)入 Kafka 目錄下:

cd kafka_2.11-2.3.0

啟動(dòng) zookeeper

通過(guò) bin 目錄下的 zookeeper-server-start.sh 啟動(dòng)腳本,來(lái)啟動(dòng) zk 單節(jié)點(diǎn)實(shí)例:

bin/zookeeper-server-start.sh -daemon config/zookeeper.properties

啟動(dòng) Kafka

通過(guò) bin 目錄下的 kafka-server-start.sh 來(lái)啟動(dòng) :

bin/kafka-server-start.sh  config/server.properties

注意:Kafka 默認(rèn)使用 9092 端口,注意關(guān)閉防火墻,阿里云服務(wù)器的話(huà),記得添加安全組。

Spring Boot 2.x 開(kāi)始整合

新建一個(gè) Spring Boot 2.x Web 工程。

項(xiàng)目結(jié)構(gòu)

image

添加 maven 依賴(lài)

小哈這里完整的 maven 依賴(lài)如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>site.exception</groupId>
    <artifactId>spring-boot-kafka</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-kafka</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Kafka -->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>
      
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 阿里巴巴 fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

添加 kafka 配置

修改 application.yml 文件,添加 kafka 相關(guān)配置:

spring:
  kafka:
    # 指定 kafka 地址,我這里在本地,直接就 localhost, 若外網(wǎng)地址,注意修改【PS: 可以指定多個(gè)】
    bootstrap-servers: localhost:9092
    consumer:
      # 指定 group_id
      group-id: group_id
      auto-offset-reset: earliest
      # 指定消息key和消息體的編解碼方式
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
    producer:
      # 指定消息key和消息體的編解碼方式
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

關(guān)于 auto-offset-reset

auto.offset.reset 配置有3個(gè)值可以設(shè)置,分別如下:

  • earliest:當(dāng)各分區(qū)下有已提交的 offset 時(shí),從提交的 offset 開(kāi)始消費(fèi);無(wú)提交的 offset時(shí),從頭開(kāi)始消費(fèi);
  • latest:當(dāng)各分區(qū)下有已提交的 offset 時(shí),從提交的 offset 開(kāi)始消費(fèi);無(wú)提交的 offset 時(shí),消費(fèi)新產(chǎn)生的該分區(qū)下的數(shù)據(jù);
  • none: topic各分區(qū)都存在已提交的 offset 時(shí),從 offset 后開(kāi)始消費(fèi);只要有一個(gè)分區(qū)不存在已提交的 offset,則拋出異常;

默認(rèn)建議用 earliest, 設(shè)置該參數(shù)后 kafka出錯(cuò)后重啟,找到未消費(fèi)的offset可以繼續(xù)消費(fèi)。

而 latest 這個(gè)設(shè)置容易丟失消息,假如 kafka 出現(xiàn)問(wèn)題,還有數(shù)據(jù)往topic中寫(xiě),這個(gè)時(shí)候重啟kafka,這個(gè)設(shè)置會(huì)從最新的offset開(kāi)始消費(fèi), 中間出問(wèn)題的哪些就不管了。

none 這個(gè)設(shè)置沒(méi)有用過(guò),兼容性太差,經(jīng)常出問(wèn)題。

新增一個(gè)訂單類(lèi)

模擬業(yè)務(wù)系統(tǒng)中,用戶(hù)每下一筆訂單,就發(fā)送一個(gè)消息,供其他服務(wù)消費(fèi):

/**
 * @author 犬小哈(公眾號(hào):小哈學(xué)Java)
 * @date 2019/4/12
 * @time 下午3:05
 * @discription 訂單實(shí)體類(lèi)
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    /**
     * 訂單id
     */
    private long orderId;
    /**
     * 訂單號(hào)
     */
    private String orderNum;
    /**
     * 訂單創(chuàng)建時(shí)間
     */
    private LocalDateTime createTime;
}

添加一個(gè)消息發(fā)布者

新建一個(gè) KafkaProvider 消息提供者類(lèi),源碼如下:

/**
 * @author 犬小哈(公眾號(hào):小哈學(xué)Java)
 * @date 2019/4/12
 * @time 下午3:05
 * @discription 消息提供者
 **/
@Component
@Slf4j
public class KafkaProvider {

    /**
     * 消息 TOPIC
     */
    private static final String TOPIC = "xiaoha";

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    public void sendMessage(long orderId, String orderNum, LocalDateTime createTime) {
        // 構(gòu)建一個(gè)訂單類(lèi)
        Order order = Order.builder()
                .orderId(orderId)
                .orderNum(orderNum)
                .createTime(createTime)
                .build();

        // 發(fā)送消息,訂單類(lèi)的 json 作為消息體
        ListenableFuture<SendResult<String, String>> future =
                kafkaTemplate.send(TOPIC, JSONObject.toJSONString(order));

        // 監(jiān)聽(tīng)回調(diào)
        future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
            @Override
            public void onFailure(Throwable throwable) {
                log.info("## Send message fail ...");
            }

            @Override
            public void onSuccess(SendResult<String, String> result) {
                log.info("## Send message success ...");
            }
        });
    }
}

添加一個(gè)消息消費(fèi)者

消息發(fā)送出去了,當(dāng)然就需要一個(gè)消費(fèi)者,消費(fèi)者拿到消息后,再做相關(guān)的業(yè)務(wù)處理,這里,小哈僅僅是打印消息體。

添加 KafkaConsumer 消費(fèi)者類(lèi):

/**
 * @author 犬小哈(公眾號(hào):小哈學(xué)Java)
 * @date 2019/4/12
 * @time 下午3:05
 * @discription 消息消費(fèi)者
 **/
@Component
@Slf4j
public class KafkaConsumer {

    @KafkaListener(topics = "xiaoha", groupId = "group_id")
    public void consume(String message) {
        log.info("## consume message: {}", message);
    }
}

通過(guò) @KafkaListener注解,我們可以指定需要監(jiān)聽(tīng)的 topic 以及 groupId, 注意,這里的 topics 是個(gè)數(shù)組,意味著我們可以指定多個(gè) topic,如:@KafkaListener(topics = {"xiaoha", "xiaoha2"}, groupId = "group_id")

注意:消息發(fā)布者的 TOPIC 需要保持與消費(fèi)者監(jiān)聽(tīng)的 TOPIC 一致,否者消費(fèi)不到消息。

單元測(cè)試

新建單元測(cè)試,功能測(cè)試消息發(fā)布,以及消費(fèi)。

/**
 * @author 犬小哈(公眾號(hào):小哈學(xué)Java)
 * @date 2019/4/12
 * @time 下午3:05
 * @discription
 **/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootKafkaApplicationTests {

    @Autowired
    private KafkaProvider kafkaProvider;

    @Test
    public void sendMessage() throws InterruptedException {
        // 發(fā)送 1000 個(gè)消息
        for (int i = 0; i < 1000; i++) {
            long orderId = i+1;
            String orderNum = UUID.randomUUID().toString();
            kafkaProvider.sendMessage(orderId, orderNum, LocalDateTime.now());
        }

        TimeUnit.MINUTES.sleep(1);
    }
}

發(fā)送 1000 個(gè)消息,看消息是否能夠被正常發(fā)布與消費(fèi),控制臺(tái)日志如下:

image

可以發(fā)現(xiàn),1000 個(gè)消息被成功發(fā)送,且被正常消費(fèi)。

我們?cè)衮?yàn)證下 Kafka 的 topic 列表,看 xiaoha 這個(gè)topic 是否正常被創(chuàng)建, 執(zhí)行 bin 目錄下查看 topic 列表的 kafka-topics.sh 腳本:

bin/kafka-topics.sh --list --zookeeper localhost:2181
image

好了,大功告成!

總結(jié)

小哈今天主要和大家分享了,如何安裝單機(jī)版的 kafka 環(huán)境、如何在 Spring Boot 2.x 中快速集成消息中間件 Kafka,以及演示了相關(guān)示例代碼來(lái)發(fā)布消息、消費(fèi)消息,希望大家看完過(guò)后有所收獲,下期見(jiàn)!

GitHub 源碼地址

https://github.com/weiwosuoai/spring-boot-tutorial/tree/master/spring-boot-kafka

參考資料

https://zh.wikipedia.org/wiki/Kafka

https://www.w3cschool.cn/apache_kafka/

https://juejin.im/post/5d406a925188255d352ab24e

http://www.lxweimin.com/p/e1df7d18bb8f

免費(fèi)分享 | 面試&學(xué)習(xí)福利資源

最近在網(wǎng)上發(fā)現(xiàn)一個(gè)不錯(cuò)的 PDF 資源《Java 核心知識(shí)&面試.pdf》分享給大家,不光是面試,學(xué)習(xí),你都值得擁有!!!

獲取方式: 關(guān)注公眾號(hào): 小哈學(xué)Java, 后臺(tái)回復(fù)資源,既可免費(fèi)無(wú)套路獲取資源鏈接,下面是目錄以及部分截圖:

關(guān)注微信公眾號(hào)【小哈學(xué)Java】,回復(fù)【資源】,即可免費(fèi)無(wú)套路領(lǐng)取資源鏈接哦
關(guān)注微信公眾號(hào)【小哈學(xué)Java】,回復(fù)【資源】,即可免費(fèi)無(wú)套路領(lǐng)取資源鏈接哦
關(guān)注微信公眾號(hào)【小哈學(xué)Java】,回復(fù)【資源】,即可免費(fèi)無(wú)套路領(lǐng)取資源鏈接哦
關(guān)注微信公眾號(hào)【小哈學(xué)Java】,回復(fù)【資源】,即可免費(fèi)無(wú)套路領(lǐng)取資源鏈接哦
關(guān)注微信公眾號(hào)【小哈學(xué)Java】,回復(fù)【資源】,即可免費(fèi)無(wú)套路領(lǐng)取資源鏈接哦
關(guān)注微信公眾號(hào)【小哈學(xué)Java】,回復(fù)【資源】,即可免費(fèi)無(wú)套路領(lǐng)取資源鏈接哦

重要的事情說(shuō)兩遍,關(guān)注公眾號(hào): 小哈學(xué)Java, 后臺(tái)回復(fù)資源,既可免費(fèi)無(wú)套路獲取資源鏈接 !!!

歡迎關(guān)注微信公眾號(hào): 小哈學(xué)Java

關(guān)注微信公眾號(hào)【小哈學(xué)Java】,回復(fù)【資源】,即可免費(fèi)無(wú)套路領(lǐng)取資源鏈接哦
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容