Java 在context()期间发生异常。从kafka streams标点调用修改headers()时发生异常
我有一个使用处理器API的kafka streams应用程序。我有一个基于挂钟的标点符号,用于检查本地statestore中的过时条目,删除它们,并在其他服务正在侦听的kafa主题上发布消息。比方说,如果在一次标点调用期间,statestore中的100个条目中有10个被识别为过时条目,那么这10个条目中的每一个都会被删除,并在卡夫卡主题上发布。应用程序的num.stream.threads设置为3。有4个输入主题,每个主题有100个分区——因此我的localstatestore也有100个分区。我有2.1.1客户和2.1+经纪人(不确定确切数字)。这一切都很好 最近,kafka消息的一个消费应用程序要求添加一个特定于每个被删除条目的标题,这样,如果消费应用程序对标题不感兴趣,它们就不需要打开有效负载。因为标题是特定于每个条目的;我正在做以下工作:Java 在context()期间发生异常。从kafka streams标点调用修改headers()时发生异常,java,apache-kafka,apache-kafka-streams,Java,Apache Kafka,Apache Kafka Streams,我有一个使用处理器API的kafka streams应用程序。我有一个基于挂钟的标点符号,用于检查本地statestore中的过时条目,删除它们,并在其他服务正在侦听的kafa主题上发布消息。比方说,如果在一次标点调用期间,statestore中的100个条目中有10个被识别为过时条目,那么这10个条目中的每一个都会被删除,并在卡夫卡主题上发布。应用程序的num.stream.threads设置为3。有4个输入主题,每个主题有100个分区——因此我的localstatestore也有100个分区
this.context().schedule(Duration.ofMinutes(EXPIRED_MINUTES), PunctuationType.WALL_CLOCK_TIME, new Punctuator() {
@Override
public void punctuate(long timestamp) {
try {
expireEntries(myStore);
} catch (Exception e ) {
LOG.error("Exception: ", e);
}
}
});
private void expireEntries(KeyValueStore<String, byte[]> store) {
try (KeyValueIterator<String, byte[]> range = store.all()) {
while (range.hasNext()) {
KeyValue<String, byte[]> next = range.next();
if (store.isExpired(next.key, expiredMs)) {
addPublishEvent(next.key, next.value);
store.delete(next.key);
}
}
void addPublishEvent(String key, Message message, String topic) {
if (message.hasInterestingProperty()) {
for (Iterator<Header> iterator = context().headers().iterator(); iterator.hasNext();)
{
Header h = iterator.next();
if (h.key().equals("header-key")) {
iterator.remove();
}
}
context().headers().add("header-key", getHeaderValue(message).getBytes());
}
context().forward(key, message.toByteArray(), To.child(App.SINK_PREFIX + topic));
String getHeaderValue(Message m) {
// return m's property of interest to app;
}
this.context().schedule(持续时间.分钟(过期分钟),标点类型.WALL\u CLOCK\u时间,新标点器(){
@凌驾
公共空标点(长时间戳){
试一试{
(myStore);
}捕获(例外e){
日志错误(“异常:”,e);
}
}
});
私有无效过期次数(KeyValueStore存储){
try(KeyValueIterator范围=store.all()){
while(range.hasNext()){
KeyValue next=范围。next();
if(store.isExpired(next.key,expiredMs)){
addPublisheEvent(next.key,next.value);
store.delete(next.key);
}
}
void addPublishEvent(字符串键、消息消息、字符串主题){
if(message.hasInterestingProperty()){
for(Iterator Iterator=context().headers().Iterator();Iterator.hasNext();)
{
Header h=迭代器.next();
如果(h.key().equals(“头键”)){
iterator.remove();
}
}
context().headers().add(“header键”,getHeaderValue(message.getBytes());
}
context().forward(key,message.toByteArray(),To.child(App.SINK_PREFIX+topic));
字符串getHeaderValue(消息m){
//将m的权益财产返还给app;
}
我有以下例外
为什么我会得到ConcurrentModificationException?如果我将num.stream.threads设置为1,它会停止吗?我无法将threads永久更改为1;因此,如何避免出现此异常,并为发布到接收器主题/应用程序的每条消息添加具有相同键但不同值的标题?A
ConcurrentModificationException
与此无关多线程,但是如果修改了迭代器的基础集合,就会抛出它。坦率地说,我不知道为什么会出现异常,因为您似乎使用迭代器。remove()
通常可以避免异常
但是,与其迭代标题
,不如直接通过标题#remove()
,用相应的键删除所有标题:
Cf:我实际上是从context().headers().remove(“header key”);
开始的,然后我更改了粘贴在这里的代码,希望不会遇到此异常。我不确定您是否注意到,第一个异常出现在我执行store.delete(next.key)时
也许这与头文件没有被深度复制有关,但卡夫卡流只在处理器之间传递引用?这是我在应用程序中需要注意的事情(欣赏如何处理的指针)还是需要在库中修复(如果是,在库中解决之前的解决方法是什么)?@MatthiasJ.Sax我们在5.5的处理器API中遇到了完全相同的问题。当我们在context.forward之后立即从状态存储中删除时,我们遇到了异常。这只发生在标点调用中。这表明在标点调用中,上下文引用了不同的记录,而状态存储删除可能是不同的记录。不确定状态存储如何与标题相关?还要注意,在标点回调中,context.headers()
无法调用,因为当前没有处理过的记录,因此也没有标题。--也许这实际上是拼图中缺少的部分。只需r
context().headers().remove("header-key");