Flink实时计算运用(六)Flink 连接器的使用

1. Flink Connectors 介绍

Flink 连接器包含数据源输入与汇聚输出两部分。Flink自身内置了一些基础的连接器,数据源输入包含文件、目录、Socket以及 支持从collections 和 iterators 中读取数据;汇聚输出支持把数据写入文件、标准输出(stdout)、标准错误输出(stderr)和 socket。

官方地址

Flink还可以支持扩展的连接器,能够与第三方系统进行交互。目前支持以下系统:

  • Apache Kafka (source/sink)
  • Apache Cassandra (sink)
  • Amazon Kinesis Streams (source/sink)
  • Elasticsearch (sink)
  • Hadoop FileSystem (sink)
  • RabbitMQ (source/sink)
  • Apache NiFi (source/sink)
  • Twitter Streaming API (source)
  • Google PubSub (source/sink)
  • JDBC (sink)****

常用的是Kafka、ES、HDFS以及JDBC。

2. JDBC 连接方式的读取与写入

  • Flink Connectors JDBC 如何使用?

    功能: 将集合数据写入数据库中

    代码: JdbcConnectorApplication实现类:

    public class JdbcConnectorApplication {
    
        public static void main(String[] args) throws Exception{
            // 配置日志文件
            System.setProperty("log4j.configurationFile", "log4j2.xml");
    
            // 1. 创建运行环境
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            // 2. 创建集合数据
            List arrs = new ArrayList<String>();
            arrs.add("10.10.20.101\t1601297294548\tPOST\taddOrder");
            arrs.add("10.10.20.102\t1601297296549\tGET\tgetOrder");
            // 3. 读取集合数据, 写入数据库
            env.fromCollection(arrs).addSink(JdbcSink.sink(
                    // 配置SQL语句
                    "insert into t_access_log (ip, time, type, api) values (?,?,?,?)",
                    (ps, value) -> {
                        System.out.println("receive ==> " + value);
                        // 解析数据
                        String[] arrValue = String.valueOf(value).split("\t");
                        for(int i=0; i<arrValue.length; i++) {
                            // 新增数据
                            ps.setString(i+1, arrValue[i]);
                        }
                    },
                    // JDBC 连接配置
                    new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
                            .withUrl("jdbc:mysql://192.168.19.150:3306/flink?useSSL=false")
                            .withDriverName("com.mysql.jdbc.Driver")
                            .withUsername("root")
                            .withPassword("654321")
                            .build()));
            // 4. 执行任务
            env.execute("job");
        }
    
    }
    

    数据表:

    DROP TABLE IF EXISTS `t_access_log`;
    CREATE TABLE `t_access_log`  (
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `ip` varchar(32) NOT NULL COMMENT 'IP地址',
    `time` varchar(255)  NULL DEFAULT NULL COMMENT '访问时间',
    `type` varchar(32)   NOT NULL COMMENT '请求类型',
    `api` varchar(32)  NOT NULL COMMENT 'API地址',
    PRIMARY KEY (`id`)
    ) ENGINE = InnoDB AUTO_INCREMENT=1;
    
  • 自定义写入数据源

    功能:读取Socket数据, 采用流方式写入数据库中。

    代码:

    CustomSinkApplication实现类:

    public class CustomSinkApplication {
    
        public static void main(String[] args) throws Exception{
            // 配置日志文件
            System.setProperty("log4j.configurationFile", "log4j2.xml");
    
            // 1. 创建运行环境
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            // 2. 读取Socket数据源
            DataStreamSource<String> socketStr = env.socketTextStream("localhost", 9911, "\n");
            // 3. 转换处理流数据
            SingleOutputStreamOperator<AccessLog> outputStream = socketStr.map(new MapFunction<String, AccessLog>() {
                @Override
                public AccessLog map(String value) throws Exception {
                    System.out.println(value);
                    // 根据分隔符解析数据
                    String[] arrValue = value.split("\t");
                    // 将数据组装为对象
                    AccessLog log = new AccessLog();
                    log.setNum(1);
                    for(int i=0; i<arrValue.length; i++) {
                        if(i == 0) {
                            log.setIp(arrValue[i]);
                        }else if( i== 1) {
                            log.setTime(arrValue[i]);
                        }else if( i== 2) {
                            log.setType(arrValue[i]);
                        }else if( i== 3) {
                            log.setApi(arrValue[i]);
                        }
                    }
    
                    return log;
                }
            });
            // 4. 配置自定义写入数据源
            outputStream.addSink(new MySQLSinkFunction());
            // 5. 执行任务
            env.execute("job");
    
        }
    
    }
    

    AccessLog:

    @Data
    public class AccessLog {
    
        private String ip;
    
        private String time;
    
        private String type;
    
        private String api;
    
        private Integer num;
    }
    

    测试数据:

    10.10.20.11	1603166893313	GET	getOrder
    10.10.20.12	1603166893314	POST	addOrder
    
  • 自定义读取数据源

    功能: 读取数据库中的数据, 并将结果打印出来。

    代码: CustomSourceApplication实现类

    public class CustomSourceApplication {
    
        public static void main(String[] args) throws Exception {
    
            // 1. 创建运行环境
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            // 2. 配置自定义MySQL读取数据源
            DataStreamSource<AccessLog> dataStream = env.addSource(new MySQLSourceFunction());
    
            // 3. 设置并行度
            dataStream.print().setParallelism(1);
    
            // 4. 执行任务
            env.execute("custom jdbc source.");
    
        }
    
    }
    
    

3. HDFS 连接方式的读取与写入

  • 通过Sink写入HDFS数据

    功能: 将Socket接收到的数据, 写入至HDFS文件中。

    代码:HdfsSinkApplication实现类

    public class HdfsSinkApplication {
    
        public static void main(String[] args) throws Exception{
            // 配置日志文件
            System.setProperty("log4j.configurationFile", "log4j2.xml");
    
            // 1. 创建运行环境
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            // 2. 读取Socket数据源
            DataStreamSource<String> socketStr = env.socketTextStream("localhost", 9911, "\n");
            BucketingSink<String> sink = new BucketingSink<String>("d:/tmp/hdfs");
            sink.setBucketer(new DateTimeBucketer<>("yyyy-MM-dd--HHmm"));
            sink.setWriter(new StringWriter())
                .setBatchSize(5*1024) // 设置每个文件的大小
                .setBatchRolloverInterval(5*1000) // 设置滚动写入新文件的时间
                .setInactiveBucketCheckInterval(30*1000) // 30秒检查一次不写入的文件
                .setInactiveBucketThreshold(60*1000); // 60秒不写入,就滚动写入新的文件
            socketStr.addSink(sink).setParallelism(1);
    
            // 5. 执行任务
            env.execute("job");
    
        }
    
    }
    

    POM依赖:

    <dependency>
        <groupId>org.apache.flinkgroupId>
        <artifactId>flink-connector-filesystem_2.11artifactId>
        <version>1.11.2version>
    dependency>
    
    <dependency>
        <groupId>org.apache.hadoopgroupId>
        <artifactId>hadoop-clientartifactId>
        <version>2.8.1version>
    dependency>
    

    数据源模拟实现, SocketSourceApplication实现类:

    public class SocketSourceApplication {    
        /**
         * 服务端的端口
         */
        private int port;
    
        /**
         * 初始化构造方法
         * @param port
         */
        public SocketSourceApplication(int port) {
            this.port = port;
        }
      /**
         * IP 访问列表
         */
        private static String[] accessIps = new String[]{"10.10.20.101", "10.10.20.102", "10.10.20.103"};
    
        /**
         * 请求访问类型
         */
        private static String[] accessTypes = new String[] {"GET", "POST", "PUT"};
    
        /**
         * 请求接口信息
         */
        private static String[] accessApis = new String[] {"addOrder", "getAccount", "getOrder"};    
    
        /**
         * Netty通讯服务启动方法
         * @throws Exception
         */
        public void runServer() throws Exception  {
            // 1. 创建Netty服务
            // 2. 定义事件Boss监听组
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            // 3. 定义用来处理已经被接收的连接
            EventLoopGroup workerGourp = new NioEventLoopGroup();
            try {
                // 4. 定义NIO的服务启动类
                ServerBootstrap sbs = new ServerBootstrap();
                // 5. 配置NIO服务启动的相关参数
                sbs.group(bossGroup, workerGourp)
                        .channel(NioServerSocketChannel.class)
                        // tcp最大缓存链接个数,它是tcp的参数, tcp_max_syn_backlog(半连接上限数量, CENTOS6.5默认是128)
                        .option(ChannelOption.SO_BACKLOG, 128)
                        //保持连接的正常状态
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        // 根据日志级别打印输出
                        .handler(new LoggingHandler(LogLevel.INFO))
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                //管道注册handler
                                ChannelPipeline pipeline = socketChannel.pipeline();
                                //编码通道处理
                                pipeline.addLast("decode", new StringDecoder());
                                //转码通道处理
                                pipeline.addLast("encode", new StringEncoder());
                                // 处理接收到的请求
                                pipeline.addLast(new NettyServerHandler());
                            }
                        });
    
                System.err.println("-------server 启动------");
                // 6. 监听控制台的输入, 并将输入信息, 广播发送给客户端
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            while(true) {
                                String accessLog = getAccessLog();
                                System.out.println("broadcast (" + NettyServerHandler.channelList.size() + ") ==> " + accessLog);
                                if(NettyServerHandler.channelList.size()  > 0 ){
                                    for(Channel channel : NettyServerHandler.channelList) {
                                        channel.writeAndFlush(accessLog);
                                    }
                                }
                                Thread.sleep(1000);
                            }
                        }catch(Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
    
                // 7. 启动netty服务
                ChannelFuture cf = sbs.bind(port).sync();
                cf.channel().closeFuture().sync();
    
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取访问日志
         * @return
         */
        private String getAccessLog() {
            StringBuilder strBuilder = new StringBuilder();
            strBuilder.append(accessIps[new Random().nextInt(accessIps.length )]).append("\t")
                    .append(System.currentTimeMillis()).append("\t")
                    .append(accessTypes[new Random().nextInt(accessTypes.length)]).append("\t")
                    .append(accessApis[new Random().nextInt(accessApis.length)]).append("\t\n");
            return strBuilder.toString();
        }
    
        /**
         * netty服务端的启动
         * @param args
         * @throws Exception
         */
        public static void main(String[] args) throws Exception{
            new SocketSourceApplication(9911).runServer();
        }
    
    }
    

    NettyServerHandler实现类:

    public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    
        // 客户端通道记录集合
        public static List<Channel> channelList = new ArrayList<>();
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("Server---连接已建立: " + ctx);
            super.channelActive(ctx);
            // 将成功建立的连接通道, 加入到集合当中
            channelList.add(ctx.channel());
        }
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("Server---收到的消息: " + msg);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            System.out.println("server--读取数据出现异常");
            cause.printStackTrace();
            ctx.close();
        }
    
        @Override
        public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
            super.channelUnregistered(ctx);
            // 移除无效的连接通道
            channelList.remove(ctx.channel());
        }
    
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            super.channelInactive(ctx);
            // 移除无效的连接通道
            channelList.remove(ctx.channel());
        }
    }
    
    

    POM依赖:

    <dependencies>
            
            <dependency>
                <groupId>io.nettygroupId>
                <artifactId>netty-allartifactId>
                <version>4.1.16.Finalversion>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
                <version>${spring.boot.version}version>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-jpaartifactId>
                <version>${spring.boot.version}version>
            dependency>
    
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>${mysql.jdbc.version}version>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-redisartifactId>
                <version>2.1.1.RELEASEversion>
            dependency>
    
        dependencies>
    
  • 读取HDFS文件数据

    HdfsSourceApplication实现类:

    public class HdfsSourceApplication {
    
        public static void main(String[] args) throws Exception{
            // 配置日志文件
            System.setProperty("log4j.configurationFile", "log4j2.xml");
    
            // 1. 创建运行环境
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            // 2. 读取HDFS数据源
            DataStreamSource<String> socketStr = env.readTextFile("hdfs://10.10.20.132:9090/hadoop-env.sh");
    
            // 3. 打印文件内容
            socketStr.print().setParallelism(1);
    
            // 4. 执行任务
            env.execute("job");
    
        }
    
    }
    
    
  • Hadoop环境安装

    1. 配置免密码登录

      生成秘钥:

      [root@flink1 hadoop-2.6.0-cdh5.15.2]# ssh-keygen -t rsa -P ''
      Generating public/private rsa key pair.
      

      将秘钥写入认证文件:

      [root@flink1 .ssh]# cat id_rsa.pub >> ~/.ssh/authorized_keys
      

      修改认证文件权限:

      [root@flink1 .ssh]# chmod 600 ~/.ssh/authorized_keys
      
    2. 配置环境变量

      将Hadoop安装包解压, 将Hadoop加入环境变量/etc/profile:

      export HADOOP_HOME=/usr/local/hadoop-2.6.0-cdh5.15.2
      export PATH=$HADOOP_HOME/bin:$PATH
      

      执行生效:

      source /etc/profile
      
    3. 修改Hadoop配置文件

      1) 修改hadoop-env.sh文件

      vi /usr/local/hadoop-2.6.0-cdh5.15.2/etc/hadoop
      

      修改JAVA_HOME:

      export JAVA_HOME=/usr/local/jdk1.8.0_181
      

      2)修改core-site.xml文件

      <configuration>
              <property>
                      <name>fs.defaultFSname>
                      <value>hdfs://flink1:9090value>
              property>
      configuration>
      
      

      这里的主机名称是flink1。

      3)修改hdfs-site.xml文件

      <configuration>
               <property>
                      <name>dfs.replicationname>
                      <value>1value>
              property>
      
               <property>
                      <name>hadoop.tmp.dirname>
                      <value>/usr/local/hadoop-2.6.0-cdh5.15.2/tmpvalue>
              property>
      configuration>
      
      

      4)修改mapred-site.xml文件

      
              
                      yarn.nodemanager.aux-services
                      mapreduce_shuffle
              
      
      
      

      5)修改slaves文件

      flink1
      

      这里配置的是单节点, 指向本机主机名称。

      6)修改yarn-site.xml

      <configuration>
      
      
      
              <property>
                      <name>mapreduce.framework.namename>
                      <value>yarnvalue>
              property>
      
      configuration>
      
      
    4. 启动Hadoop服务

      [root@flink1 sbin]# ./start-all.sh
      This script is Deprecated. Instead use start-dfs.sh and start-yarn.sh
      20/09/27 19:22:37 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
      Starting namenodes on [flink1]
      flink1: setterm: $TERM is not defined.
      flink1: starting namenode, logging to /usr/local/hadoop-2.6.0-cdh5.15.2/logs/hadoop-root-namenode-flink1.out
      flink1: setterm: $TERM is not defined.
      flink1: starting datanode, logging to /usr/local/hadoop-2.6.0-cdh5.15.2/logs/hadoop-root-datanode-flink1.out
      Starting secondary namenodes [0.0.0.0]
      0.0.0.0: setterm: $TERM is not defined.
      0.0.0.0: starting secondarynamenode, logging to /usr/local/hadoop-2.6.0-cdh5.15.2/logs/hadoop-root-secondarynamenode-flink1.out
      20/09/27 19:22:53 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
      starting yarn daemons
      starting resourcemanager, logging to /usr/local/hadoop-2.6.0-cdh5.15.2/logs/yarn-root-resourcemanager-flink1.out
      flink1: setterm: $TERM is not defined.
      flink1: starting nodemanager, logging to /usr/local/hadoop-2.6.0-cdh5.15.2/logs/yarn-root-nodemanager-flink1.out
      
      

      上传一个文件, 用于测试:

       hdfs dfs -put /usr/local/hadoop-2.6.0-cdh5.15.2/etc/hadoop/hadoop-env.sh /
      
    5. 访问验证

      Flink实时计算运用(六)Flink 连接器的使用_第1张图片

      Flink实时计算运用(六)Flink 连接器的使用_第2张图片

4. ES 连接方式的写入

  • ES服务安装

    1. 到官网下载地址下载6.8.1版本的gz压缩包, 不要下载最新版本, Spring Boot等项目可能未及时更新支持。

    2. 解压安装包

      tar -xvf elasticsearch-6.8.1-linux-x86_64.tar.gz
      
    3. ElasticSearch不能以Root身份运行, 需要单独创建一个用户

      1. groupadd elsearch
      2. useradd elsearch -g elsearch -p elasticsearch
      3. chown -R elsearch:elsearch  /usr/local/elasticsearch-6.8.1
      

      执行以上命令,创建一个名为elsearch用户, 并赋予目录权限。

    4. 修改配置文件

      vi config/elasticsearch.yml, 只需修改以下设置:

      #集群名称
      cluster.name: my-application
      #节点名称 
      node.name: node-1
      #数据存储路径
      path.data: /usr/local/elasticsearch-6.8.1/data
      #日志存储路径
      path.logs: /usr/local/elasticsearch-6.8.1/logs
      # 绑定IP地址
      network.host: 10.10.20.28
      # 指定服务访问端口
      http.port: 9200
      # 指定API端户端调用端口
      transport.tcp.port: 9300
      
      
    5. 指定JDK版本

      • 最新版的ElasticSearch需要JDK11版本, 下载JDK11压缩包, 并进行解压。

      • 修改环境配置文件

        vi bin/elasticsearch-env

        参照以下位置, 追加一行, 设置JAVA_HOME, 指定JDK11路径。

        JAVA_HOME=/usr/local/jdk_11
        
        # now set the path to java
        if [ ! -z "$JAVA_HOME" ]; then
          JAVA="$JAVA_HOME/bin/java"
        else
          if [ "$(uname -s)" = "Darwin" ]; then
            # OSX has a different structure
            JAVA="$ES_HOME/jdk/Contents/Home/bin/java"
          else
            JAVA="$ES_HOME/jdk/bin/java"
          fi
        fi
        
      • 关闭ConcMarkSweepGC

        JDK9版本以后不建议使用ConcMarkSweepGC, 如果不想出现提示, 可以将其关闭

        vi config/jvm.options

        将UseConcMarkSweepGC注释:

        ## GC configuration
        #-XX:+UseConcMarkSweepGC
        ...
        ## G1GC Configuration
        # NOTE: G1GC is only supported on JDK version 10 or later.
        # To use G1GC uncomment the lines below.
        #-XX:-UseConcMarkSweepGC
        ...
        
    6. 启动ElasticSearch

      • 切换用户

        su elsearch

      • 以后台常驻方式启动

        bin/elasticsearch -d

    7. 问题处理

      出现max virtual memory areas vm.max_map_count [65530] is too low, increase to at least 错误信息

      修改系统配置:

      • vi /etc/sysctl.conf

        添加

        vm.max_map_count=655360

        执行生效

        sysctl -p

      • vi /etc/security/limits.conf

        在文件末尾添加

        * soft nofile 65536

        * hard nofile 131072

        * soft nproc 2048

        * hard nproc 4096

        elsearch soft nproc 125535

        elsearch hard nproc 125535

        重新切换用户即可:

        su - elsearch

  • FLINK ES写入功能实现

    功能: 将Socket流数据, 写入至ES服务。

    代码:ElasticSinkApplication实现类:

    public class ElasticSinkApplication {
    
        public static void main(String[] args) throws Exception{
            // 配置日志文件
            System.setProperty("log4j.configurationFile", "log4j2.xml");
    
            // 1. 创建运行环境
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            // 2. 读取Socket数据源
            DataStreamSource<String> socketStr = env.socketTextStream("localhost", 9911, "\n");
    
            //3. 配置ES服务信息
            List<HttpHost> httpHosts = new ArrayList<>();
            httpHosts.add(new HttpHost("10.10.20.132", 9200, "http"));
            //4. 数据解析处理
            ElasticsearchSink.Builder<String> esSinkBuilder = new ElasticsearchSink.Builder<>(
                    httpHosts,
                    new ElasticsearchSinkFunction<String>() {
                        public IndexRequest createIndexRequest(String element) {
                            Map<String, String> json = new HashMap<>();
                            // 解析数据
                            String[] arrValue = String.valueOf(element).split("\t");
                            for(int i=0; i<arrValue.length; i++) {
                                if(i == 0) {
                                    json.put("ip", arrValue[i]);
                                }else if( i== 1) {
                                    json.put("time", arrValue[i]);
                                }else if( i== 2) {
                                    json.put("type", arrValue[i]);
                                }else if( i== 3) {
                                    json.put("api", arrValue[i]);
                                }
                            }
                            return Requests.indexRequest()
                                    .index("flink-es")
                                    .type("access-log")
                                    .source(json);
                        }
    
                        @Override
                        public void process(String element, RuntimeContext ctx, RequestIndexer indexer) {
                            indexer.add(createIndexRequest(element));
                        }
                    }
            );
    
            // 5. ES的写入配置
            esSinkBuilder.setBulkFlushMaxActions(1);
            esSinkBuilder.setRestClientFactory(
                    restClientBuilder -> {
                        restClientBuilder.setMaxRetryTimeoutMillis(5000);
                    }
            );
    
            // 6. 添加ES的写入器
            socketStr.addSink(esSinkBuilder.build());
            socketStr.print().setParallelism(1);
    
            // 7. 执行任务
            env.execute("job");
    
        }
    
    }
    
    

    查看index信息:

    http://10.10.20.132:9200/_cat/indices?v

    查看具体数据:

    http://10.10.20.132:9200/flink-es/_search?pretty

5. KAFKA 连接方式的读取与写入

  • Kafka安装

    1. 下载Kafka_2.12-1.1.1安装包

    2. 将安装包解压

      tar -xvf kafka_2.12-1.1.1.tgz
      
    3. 修改kafka配置

      只修改绑定IP, 因为是单节点, 其他按默认配置来。

      listeners=PLAINTEXT://10.10.20.132:9092
      advertised.listeners=PLAINTEXT://10.10.20.132:9092
      

      如有多个IP地址, 绑定为对外访问的IP。

    4. 启动zookeeper服务

      kafka安装包内置了zookeeper,可以直接启动。

      bin/zookeeper-server-start.sh -daemon config/zookeeper.properties
      
    5. 启动kafka服务

      bin/kafka-server-start.sh -daemon config/server.properties
      
  • Flink Kafka 读取功能

    功能: 通过flink读取kafka消息队列数据, 并打印显示。

    代码:KafkaSourceApplication实现类

    public class KafkaSourceApplication {
    
        public static void main(String[] args) throws Exception {
    
            // 1. 创建运行环境
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            // 2. 设置kafka服务连接信息
            Properties properties = new Properties();
            properties.setProperty("bootstrap.servers", "10.10.20.132:9092");
            properties.setProperty("group.id", "fink_group");
    
            // 3. 创建Kafka消费端
            FlinkKafkaConsumer kafkaProducer = new FlinkKafkaConsumer(
                    "flink-source",                  // 目标 topic
                    new SimpleStringSchema(),   // 序列化 配置
                    properties);
    
    //        kafkaProducer.setStartFromEarliest();     // 尽可能从最早的记录开始
    //        kafkaProducer.setStartFromLatest();       // 从最新的记录开始
    //        kafkaProducer.setStartFromTimestamp(...); // 从指定的时间开始(毫秒)
    //        kafkaProducer.setStartFromGroupOffsets(); // 默认的方法
    
            // 4. 读取Kafka数据源
            DataStreamSource<String> socketStr = env.addSource(kafkaProducer);
    
            socketStr.print().setParallelism(1);
    
            // 5. 执行任务
            env.execute("job");
        }
    
    }
    

    通过kafka生产者命令测试验证:

    bin/kafka-console-producer.sh --broker-list 10.10.20.132:9092 --topic flink-source
    
    

    扩展点:kafka消息的消费处理策略:

    //        kafkaProducer.setStartFromEarliest();     // 尽可能从最早的记录开始
    //        kafkaProducer.setStartFromLatest();       // 从最新的记录开始
    //        kafkaProducer.setStartFromTimestamp(...); // 从指定的时间开始(毫秒)
    //        kafkaProducer.setStartFromGroupOffsets(); // 默认的方法
    
  • Flink Kafka 写入功能

    功能: 将Socket的流数据,通过flink 写入kafka 消息队列。

    代码: KafkaSinkApplication实现类

    public class KafkaSinkApplication {
    
        public static void main(String[] args) throws Exception {
    
            // 1. 创建运行环境
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            // 2. 读取Socket数据源
            DataStreamSource<String> socketStr = env.socketTextStream("localhost", 9911, "\n");
    
            // 3. Kakfa的生产者配置
            FlinkKafkaProducer kafkaProducer = new FlinkKafkaProducer(
                    "10.10.20.132:9092",            // broker 列表
                    "flink-topic",                  // 目标 topic
                    new SimpleStringSchema());   // 序列化 方式
    
            // 4. 添加kafka的写入器
            socketStr.addSink(kafkaProducer);
            socketStr.print().setParallelism(1);
    
            // 5. 执行任务
            env.execute("job");
        }
    
    }
    

    通过kafka消费者命令测试验证:

    bin/kafka-console-consumer.sh --bootstrap-server  10.10.20.132:9092 --topic flink-topic
    
    

    控制消息的发送处理模式:

    		// 控制消息的操作模式
            Properties properties = new Properties();
            properties.setProperty("bootstrap.servers", "10.10.20.132:9092");
            FlinkKafkaProducer kafkaProducer = new FlinkKafkaProducer(
                    "flink-topic",                  // 目标 topic
                    new KeyedSerializationSchemaWrapper<>(new SimpleStringSchema()),
                    properties,
                    FlinkKafkaProducer.Semantic.EXACTLY_ONCE
            );   // 序列化 schema
    

    提供了三种消息处理模式:

    • Semantic.NONE:Flink 不会有任何语义的保证,产生的记录可能会丢失或重复。
    • Semantic.AT_LEAST_ONCE(默认设置):类似 FlinkKafkaProducer010 版本中的 setFlushOnCheckpoint(true),这可以保证不会丢失任何记录(虽然记录可能会重复)。
    • Semantic.EXACTLY_ONCE:使用 Kafka 事务提供精准一次的语义。无论何时,在使用事务写入 Kafka 时,都要记得为所有消费 Kafka 消息的应用程序设置所需的 isolation.levelread_committedread_uncommitted - 后者是默认值)。

    Kafka 的消息可以携带时间戳,指示事件发生的时间或消息写入 Kafka broker 的时间。

    kafkaProducer.setWriteTimestampToKafka(true);
    

本文由mirson创作分享,如需进一步交流,请加QQ群:19310171或访问www.softart.cn

你可能感兴趣的:(大数据,架构设计,flink,kafka,rabbitmq)