ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor(
new ThreadFactoryBuilder().setNameFormat("request-data-collector-%d").build());
Runnable requestDataCollector = new RequestDataCollector(sessionIdToRequestData, sessionTimeoutMillis, filePath);
pool.scheduleAtFixedRate(requestDataCollector, 0, 7, TimeUnit.SECONDS);
genericKafkaConsumer.startConsumerServer(new SessionBasedAnalyticsConsumerHelper(sessionIdToRequestData, sessionTimeoutMillis, filePath));
这是我Kafka Consumer
计划的主要部分。
在startConsumerServer()
内,它使用ExecutorService
提交SessionBasedAnalyticsConsumerHelper
:
try {
while (true) {
for (final KafkaStream<?, ?> stream : streams) {
runnableClass.setStream(stream);
executor.submit(runnableClass); //here
}
if (!this.executor.isTerminated()) {
Thread.sleep(100);
}
}
} catch (Exception e) {
if (this.executor != null) {
this.executor.shutdown();
}
...
我没有写这部分,但我想这是运行Kafka Consumer
的标准习语。
它通过在不同的线程上运行两个程序来处理来自Apache Kafka
的消息流,这两个程序都访问HashMap
个缓冲区sessionIdToRequestData
。
该地图的定义如下:
private static ConcurrentHashMap<String, ConcurrentSkipListSet<RequestData>> sessionIdToRequestData = new ConcurrentHashMap<>();
这被定义为全局,并在构建两个程序RequestDataCollector
和SessionBasedAnalyticsConsumerHelper
时传递。
他们都是Runnable
。简要描述一下他们做了什么:
SessionBasedAnalyticsConsumerHelper
为用户首次发出请求之前的一系列请求数据创建会话,直到用户在一段时间内处于非活动状态TimeoutMillis
。
将一系列数据存储在HashMap
的缓冲区中,其中userId
为关键字,直到超时后发出同一用户的下一个请求,此时刷新缓冲区并且数据被写入文件。
RequestDataCollector
定期检查缓冲区(通过迭代HashMap
),如果缓冲区中最后添加的数据超时,则刷新缓冲区并将其数据写入文件
我的问题是两个程序在单独运行时工作正常,似乎在等待。
正如您在下面的屏幕截图中看到的,运行kafka-consumer-group
的线程池中的所有SessionBasedAnalyticsConsumerHelper
个线程和运行request-data-collector
的{{1}}线程都是棕色,表示它们是RequestDataCollector
(java.lang.Thread.State:WAITING(parking))。
查看parking
输出,我猜测println
线程占主导地位并且没有释放锁,因为当我运行程序时,只有我放在{{1的开头和结尾的字符串正在打印。然而,令人惊讶的是,即使RequestDataCollector
线程正在等待(停放)(为什么它会从RequestDataCollector
打印字符串?它仍然让我感到困惑。)
我在多线程方面经验不多,而且我读过有关多线程的调试,并尝试自己调试,但我觉得这不是我在没有一些帮助的情况下能在短时间内完成的事情。
任何人都可以帮我弄清楚发生了什么,或者至少指引我走向正确的方向吗?
如果需要,我附上我的程序的源代码。
https://www.dropbox.com/sh/wq2tn1stfimgqq0/AADFAZA9N65RSWme0c8aTA0va?dl=0
这里是一个线程转储(请注意,线程转储中的输出是不确定的和变化的。我已经看到2~3个变体,这只是其中之一):
RequestDataCollector