大数据入门案例1——kafka数据推送和消费

426 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情

前言

昨天,我们完成了使用taro框架进行跨端开发的系列文章。今天本来想在前端找几个方向写小Demo的,但是没有发现比较有创意和亮点的项目。
最终,我决定在4月份的最后5天给大家介绍一下我在3月份参与到新公司大数据项目的一些落地实践,以及最终实现超多数据量的数据落盘工作(粗略估计5W/秒)

现状和需要解决的问题

我现在的公司所属的行业比较传统,是煤炭行业,对于数据的收集需求较大。在较大的矿场当中,产生的数据很多,对数据的实时性和准确性有着较高的要求。因此我需要实现数据超大的吞吐量,一开始定的是最少支持2W/秒。

初始的解决方案

因为我们的业务数据较为统一,且吞吐量需求较高。我们一开始定下的落地方案就是基于kafka来实现的,相信参与大数据开发的读者都比较清楚kafka的超高吞吐量。
我们一开始的实现方案为:将一个topic中的数据分发给不同的消费者进行消费,A消费者定义自己的group.id只用来生成心跳信息。同理B消费者只用来记录数据至持久化存储

第一阶段的实现方案

我们一开始其实有一个写好的解决方案了,但是效率有点跟不上,所以我们团队就从0开始死磕kafka的数据生产和消费功能。我们第一版的实现方案很简单:试一下kafka官方的案例的瓶颈是多少

基本环境准备

  1. 我们第一步就是准备一个自己的单机kafka,大家可以参考这篇文章搭建:win 10 kafka搭建\
  2. 下一步因为我们的技术栈是Spring Boot所以我们就基于Spring Boot来整合kafka,我简单的说一下整合步骤
  • 初始化一个spring boot项目。含有starter和starter-web即可
  • 添加kafka依赖,例如下面所示
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>
  1. 自己初始化而不是使用kafka初始化 我们都知道kafka有autoconfig,但是我们有时候会针对于特殊的数据生产者和消费者有特殊的配置。所以我们直接就把producer放在InitializingBean之后进行自动初始化。
    步骤为\
  2. 取消kafka的自动装配,在application启动类中,将kafka的自动配置config去除即可
@SpringBootApplication(exclude = KafkaAutoConfiguration.class)
  1. 初始化代码,我只贴出初始化的位置了,大家可以按照自己的kafka进行初始化
@Data
@Component
public class KafkaProduceInit implements InitializingBean {
    private KafkaProducer producer;

    @Override
    public void afterPropertiesSet() throws Exception {
        this.setProducer(.....);
    }
}
  1. 如何1秒发送2W+数据 我们想要实现2W/S的数据落盘,首先要实现2W+/S的发送效率,这里我们使用多线程来发送数据。多线程发送数据完全支撑起了我们的从2W/S到5W/S的数据生产工作,示例代码如下
int threadNumber = 10;
int sendNum = 5000;
// 初始化countDown参数为线程个数
final CountDownLatch cdl = new CountDownLatch(threadNumber);
// 使用线程池
//获取系统处理器个数,作为线程池数量
int nThreads = Runtime.getRuntime().availableProcessors();
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNamePrefix("demo-pool-").build();
long start = System.currentTimeMillis();

ExecutorService service = new ThreadPoolExecutor(nThreads, 200, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
//遍历启动10个线程发送数据
for (int index = 0; index < threadNumber; index++) {
    int startIndex = index;
    //执行线程
    service.submit(() -> {
        for (int sendIndex = startIndex * sendNum; sendIndex < (startIndex + 1) * sendNum; sendIndex++) {
            kafkaService.sendDemoMsg(sendIndex, startIndex);
        }
        cdl.countDown();
    });
}
//线程启动后调用countDownLatch方法
try {
    cdl.await();//需要捕获异常,当其中线程数为0时这里才会继续运行
} catch (InterruptedException e) {
    e.printStackTrace();
    Thread.currentThread().interrupt();
}
long end = System.currentTimeMillis();
log.info("end - start = " + (end - start) / (double) 1000);
  1. 我们今天先试一下单个消费者单个分区的消费效率
@KafkaListener(topics = "testTopic")
public void listenTestTopic(String message) {
    log.info("testTopic" + message);
}

我们可以试了几次之后就会发现,效率其实非常低,明天我们优化kafka listener的消费逻辑,试一下分片消费的效率

结语

今天写的代码比较少,主要是给大家一个概念,如果需要大数据大吞吐量的话,大家可以考虑kafka,明天我们更新kafka的分片消费,欢迎各位多多点赞关注!