59、Flink 的异步 IO 算子使用线程池查询 MySQL

1、概述

-----------Test1-----------

非静态 dataSource 和 executorService【一个并行度(Task 线程)一个实例】

分区1

dataSource=>915342614

executorService=>2120731873

分区2

dataSource=>1271767714

executorService=>844411403

并行度=2,固定线程池数量=2,即查询并发度 2线程2实例=4

静态 dataSource 和 executorService【一个TM(JVM 进程)一个实例】 分区1 dataSource=>1153938359 dataSource=>1153938359 分区2 executorService=>1974212788 executorService=>1974212788 并行度=2,固定线程池数量=2,即查询并发度 2线程1实例=2

-----------Test2-----------

timeout() 方法中设置 resultFuture.completeExceptionally(new Exception(input + "=获取数据超时"));

Caused by: java.lang.Exception: 1=获取数据超时

timeout() 方法中设置 resultFuture.complete(Collections.singleton(Tuple2.of(input, null))); 1> (1,null)

timeout() 方法中设置 resultFuture.complete(Collections.EMPTY_LIST); 无结果发出

-----------Test3-----------

AsyncDataStream.unorderedWait 模式下,并行度=2,固定线程池数量=2

设置 if(id1 || id2){TimeUnit.SECONDS.sleep(5);}

发送数据 1,2,3,4

输出结果

1> (3,c)

2> (4,d)

1> (1,a)

2> (2,b)

AsyncDataStream.orderedWait 模式下,并行度=2,固定线程池数量=2 设置 if(id==1 || id==2){TimeUnit.SECONDS.sleep(5);} 发送数据 1,2,3,4 输出结果 1> (1,a) 1> (3,c) 2> (2,b) 2> (4,d)

-----------Test4-----------

capacity=1,并行度=2,固定线程池数量=2,即查询并发度 2线程2实例=4

设置 TimeUnit.SECONDS.sleep(5);

发送数据 1,2,3,4,

间隔5秒输出

1> (1,a)

2> (2,b)

间隔5秒输出

1> (3,c)

2> (4,d)

capacity=2,并行度=2,固定线程池数量=2,即查询并发度 2线程2实例=4 设置 TimeUnit.SECONDS.sleep(5); 间隔5秒输出 2> (1,a) 1> (2,b) 2> (3,c) 1> (4,d)

结论:capacity=等待响应队列的容量=吞吐量

-----------Test5-----------

模拟查询超时异常=TimeUnit.SECONDS.sleep(10);,AsyncRetryStrategy=FixedDelayRetryStrategy

没有增加重试策略=>Caused by: java.lang.Exception: 1=获取数据超时 增加重试策略=>正常产出 1> (1,a) 2> (2,b)

2、代码示例

bash 复制代码
package com.xu.flink.datastream.day11;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.AsyncDataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.async.ResultFuture;
import org.apache.flink.streaming.api.functions.async.RichAsyncFunction;
import org.apache.flink.streaming.util.retryable.AsyncRetryStrategies;
import org.apache.flink.streaming.util.retryable.RetryPredicates;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Collections;
import java.util.concurrent.*;
import java.util.function.Supplier;

public class _05_AsyncMySqlQueryWithThreadPool {
    public static void main(String[] args) throws Exception {
        // 创建 Flink 执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        env.setParallelism(2);

        // 模拟source输出数据
        SingleOutputStreamOperator<Integer> line = env.socketTextStream("localhost", 8888)
                .map(Integer::parseInt);

        // 创建异步算子-无重试
        // 无序
//        AsyncDataStream.unorderedWait(line,
//                        new MySqlAsyncFunction(2),
//                        8,
//                        TimeUnit.SECONDS,
//                        2)
//                .print();

        // 有序
//        AsyncDataStream.orderedWait(line,
//                        new MySqlAsyncFunction(2),
//                        8,
//                        TimeUnit.SECONDS,
//                        20)
//                .print();

        // 创建异步算子-重试
        // 创建重试策略
        AsyncRetryStrategies.FixedDelayRetryStrategy fixedDelayRetryStrategy = new AsyncRetryStrategies.FixedDelayRetryStrategyBuilder(3, 10000L)
                .ifResult(RetryPredicates.EMPTY_RESULT_PREDICATE)
                .ifException(RetryPredicates.HAS_EXCEPTION_PREDICATE)
                .build();

//        AsyncRetryStrategies.ExponentialBackoffDelayRetryStrategy backoffDelayRetryStrategy = new AsyncRetryStrategies.ExponentialBackoffDelayRetryStrategyBuilder<>(3, 2000L, 10, 2000L)
//                // 基于执行异常触发重试
//                .ifException(RetryPredicates.HAS_EXCEPTION_PREDICATE)
//                // 基于返回结果为空触发重试
//                .ifResult(RetryPredicates.EMPTY_RESULT_PREDICATE)
//                .build();

        // 无序
        AsyncDataStream.unorderedWaitWithRetry(line,
                        new MySqlAsyncFunction(2),
                        8,
                        TimeUnit.SECONDS,
                        2,
                        fixedDelayRetryStrategy)
                .print();

        // 有序
//        AsyncDataStream.orderedWaitWithRetry(line,
//                        new MySqlAsyncFunction(2),
//                        8,
//                        TimeUnit.SECONDS,
//                        2,
//                        backoffDelayRetryStrategy)
//                .print();

        // 触发执行
        env.execute();
    }
}

/**
 * -----------Test1-----------
 * 非静态 dataSource 和 executorService【一个并行度(Task 线程)一个实例】
 * 分区1
 * dataSource=>915342614
 * executorService=>2120731873
 * 分区2
 * dataSource=>1271767714
 * executorService=>844411403
 * 并行度=2,固定线程池数量=2,即查询并发度 2线程*2实例=4
 * <p>
 * 静态 dataSource 和 executorService【一个TM(JVM 进程)一个实例】
 * 分区1
 * dataSource=>1153938359
 * dataSource=>1153938359
 * 分区2
 * executorService=>1974212788
 * executorService=>1974212788
 * 并行度=2,固定线程池数量=2,即查询并发度 2线程*1实例=2
 * -----------Test2-----------
 * timeout() 方法中设置 resultFuture.completeExceptionally(new Exception(input + "=获取数据超时"));
 * Caused by: java.lang.Exception: 1=获取数据超时
 * <p>
 * timeout() 方法中设置 resultFuture.complete(Collections.singleton(Tuple2.of(input, null)));
 * 1> (1,null)
 * <p>
 * timeout() 方法中设置 resultFuture.complete(Collections.EMPTY_LIST);
 * 无结果发出
 * -----------Test3-----------
 * AsyncDataStream.unorderedWait 模式下,并行度=2,固定线程池数量=2
 * 设置 if(id==1 || id==2){TimeUnit.SECONDS.sleep(5);}
 * 发送数据 1,2,3,4
 * 输出结果
 * 1> (3,c)
 * 2> (4,d)
 * 1> (1,a)
 * 2> (2,b)
 * <p>
 * AsyncDataStream.orderedWait 模式下,并行度=2,固定线程池数量=2
 * 设置 if(id==1 || id==2){TimeUnit.SECONDS.sleep(5);}
 * 发送数据 1,2,3,4
 * 输出结果
 * 1> (1,a)
 * 1> (3,c)
 * 2> (2,b)
 * 2> (4,d)
 * -----------Test4-----------
 * capacity=1,并行度=2,固定线程池数量=2,即查询并发度 2线程*2实例=4
 * 设置 TimeUnit.SECONDS.sleep(5);
 * 发送数据 1,2,3,4,
 * 间隔5秒输出
 * 1> (1,a)
 * 2> (2,b)
 * 间隔5秒输出
 * 1> (3,c)
 * 2> (4,d)
 * <p>
 * capacity=2,并行度=2,固定线程池数量=2,即查询并发度 2线程*2实例=4
 * 设置 TimeUnit.SECONDS.sleep(5);
 * 间隔5秒输出
 * 2> (1,a)
 * 1> (2,b)
 * 2> (3,c)
 * 1> (4,d)
 * <p>
 * 结论:capacity=等待响应队列的容量=吞吐量
 * -----------Test5-----------
 * 模拟查询超时异常=TimeUnit.SECONDS.sleep(10);,AsyncRetryStrategy=FixedDelayRetryStrategy
 * <p>
 * 没有增加重试策略=>Caused by: java.lang.Exception: 1=获取数据超时
 * 增加重试策略=>正常产出 1> (1,a) 2> (2,b)
 */
class MySqlAsyncFunction extends RichAsyncFunction<Integer, Tuple2<Integer, String>> {
    private int maxConnTotal;
    private static ExecutorService executorService;
    private static DruidDataSource dataSource;

    // 传入最大链接数
    public MySqlAsyncFunction(int maxConnTotal) {
        this.maxConnTotal = maxConnTotal;
    }

    @Override
    public void open(Configuration parameters) throws Exception {
        System.out.println("open 方法被调用");
        //创建一个线程池,实现并发提交请求
        executorService = Executors.newFixedThreadPool(maxConnTotal);

        //创建链接池(异步IO 一个请求对应一个线程,一个请求对应一个链接)
        dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setUrl("jdbc:mysql://localhost:3306/xlink?characterEncoding=UTF-8&useSSL=false");
        dataSource.setMaxActive(maxConnTotal);

        System.out.println("dataSource=>" + dataSource.hashCode());
        System.out.println("executorService=>" + executorService.hashCode());
    }

    @Override
    public void close() throws Exception {
        System.out.println("close 方法被调用");
        executorService.shutdown();
        dataSource.close();
    }

    @Override
    public void asyncInvoke(Integer input, ResultFuture<Tuple2<Integer, String>> resultFuture) throws Exception {
        //使用线程池提交请求
        Future<String> future = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return queryFromMySql(input);
            }
        });

        //获取请求结果
        CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    return future.get();
                } catch (Exception e) {
                    return null;
                }
            }
        }).thenAccept((String result) -> {
            resultFuture.complete(Collections.singleton(Tuple2.of(input, result)));
        });
    }

    @Override
    public void timeout(Integer input, ResultFuture<Tuple2<Integer, String>> resultFuture) throws Exception {
        // 超时-发出异常
        resultFuture.completeExceptionally(new Exception(input + "=获取数据超时"));

        // 超时-发出空的 Join 结果
//        resultFuture.complete(Collections.singleton(Tuple2.of(input, null)));

        // 超时-不发出元素
//        resultFuture.complete(Collections.EMPTY_LIST);
    }

    /**
     * SQL 查询代码实现
     */
    private String queryFromMySql(Integer id) throws Exception {
        String sql = "SELECT deptno,db_source FROM dept WHERE deptno = ?";

        String result = null;
        Connection connection = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;

        try {
            connection = dataSource.getConnection();
            stmt = connection.prepareStatement(sql);
            stmt.setInt(1, id);

            rs = stmt.executeQuery();

            // 模拟超时异常
            TimeUnit.SECONDS.sleep(10);

            while (rs.next()) {
                result = rs.getString("db_source");
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
            if (stmt != null) {
                stmt.close();
            }
            if (connection != null) {
                connection.close();
            }
        }

        return result;
    }
}
相关推荐
W Y41 分钟前
【架构-37】Spark和Flink
架构·flink·spark
ycsdn1044 分钟前
Caused by: org.apache.flink.api.common.io.ParseException: Row too short:
大数据·flink
DolphinScheduler社区2 小时前
Apache DolphinScheduler + OceanBase,搭建分布式大数据调度平台的实践
大数据
时差9533 小时前
MapReduce 的 Shuffle 过程
大数据·mapreduce
kakwooi4 小时前
Hadoop---MapReduce(3)
大数据·hadoop·mapreduce
数新网络4 小时前
《深入浅出Apache Spark》系列②:Spark SQL原理精髓全解析
大数据·sql·spark
昨天今天明天好多天10 小时前
【数据仓库】
大数据
油头少年_w10 小时前
大数据导论及分布式存储HadoopHDFS入门
大数据·hadoop·hdfs
Elastic 中国社区官方博客11 小时前
释放专利力量:Patently 如何利用向量搜索和 NLP 简化协作
大数据·数据库·人工智能·elasticsearch·搜索引擎·自然语言处理
力姆泰克11 小时前
看电动缸是如何提高农机的自动化水平
大数据·运维·服务器·数据库·人工智能·自动化·1024程序员节