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;
}
}