当前位置: 首页 > 知识库问答 >
问题:

Infinispan缓存和computeIfAbsent的lambda反序列化无效

晋骏喆
2023-03-14

我正在玩弄一个基本的无限泛集群,我遇到了一个令人费解的错误。

我基本上实现了一个共享映射,只包含一个整数

这是我的服务代码

package sandbox.infinispan.test.service;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.infinispan.Cache;

@Named("useThisOne")
@ApplicationScoped
public class CounterService implements ICounterService {

    private static final String KEY = "key";

    @Inject
    private Cache<String, Integer> cache;

    @Override
    public void inc(final int amount) {
        this.cache.put(KEY, Integer.valueOf(this.get() + amount));
    }

    @Override
    public int get() {
        return this.cache.computeIfAbsent(KEY, k -> Integer.valueOf(0)).intValue();
    }
}

缓存是通过以下方式生成的:

package sandbox.infinispan.test.config;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;

import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;

@Dependent
class CacheProvider {

    @Produces
    @ApplicationScoped
    private EmbeddedCacheManager defaultClusteredCacheManager() {
        final GlobalConfiguration g = new GlobalConfigurationBuilder() //
                .clusteredDefault() //
                .transport() //
                .nodeName(this.getNodeName()) //
                .clusterName("infinispanTestCluster") //
                .build();
        final Configuration cfg = new ConfigurationBuilder() //
                .clustering() //
                .cacheMode(CacheMode.REPL_SYNC) ///
                .build();
        return new DefaultCacheManager(g, cfg);
    }
}

当集群中至少有两台服务器时,computeIfAbsen会失败

15:48:50,253 ERROR [org.infinispan.interceptors.impl.InvocationContextInterceptor] (jgroups-7,myhostname-14393) ISPN000136: Error executing command ComputeIfAbsentCommand, writing keys [key]: org.infinispan.remoting.RemoteException: ISPN000217: Received exception from otherhostname-44445, see cause for remote stack trace

这可以归结为:

Caused by: java.lang.NoSuchMethodException: sandbox.infinispan.test.service.CounterService.$deserializeLambda$(java.lang.invoke.SerializedLambda)

最后是:

Caused by: java.lang.IllegalArgumentException: Invalid lambda deserialization
        at sandbox.infinispan.test.service.CounterService.$deserializeLambda$(CounterService.java:10)
            ... 68 more
Caused by: an exception which occurred:
        in object of type java.lang.invoke.SerializedLambda

如果我把我非常好的时髦代码重写成丑陋的代码,它就会起作用。

@Override
public int get() {
    Integer value = this.cache.get(KEY);
    if (value == null) {
        value = Integer.valueOf(0);
        this.cache.put(KEY, value);
    }
    return value.intValue();
}

我怎么能使用漂亮的电脑现在没有做事的方式?

Eclipse 2018-12、WildFly 14、集群dev成员上的java 10、远程集群成员上的CentO 7、OpenJdk 10、WildFly 14。

谢谢你的帮忙

已解决(有点)

由于我在这里得到的帮助,我将lambda转换为内部类:

static class OhWell implements Serializable {

    static Integer zero(final String t) {
        return Integer.valueOf(0);
    }
}

@Override
public int get() {
    return this.cache.computeIfAbsent(KEY, OhWell::zero).intValue();
}

它现在可以用了,但比干净的lambda要差得多。所以我会坚持传统的方式,除非有人能想出更好的方式。

进一步结果:

下面的静态内部类使用静态方法工作

static class StaticOhWell implements Serializable {

    static Integer apply(final String t) {
        return Integer.valueOf(0);
    }
}

@Override
public int get() {
    return this.cache.computeIfAbsent(KEY, StaticOhWell::apply).intValue();
}

以下具有非静态方法的非静态内部类失败:

class NotStaticOhWell implements SerializableFunction<String, Integer> {

    @Override
    public Integer apply(final String t) {
            return Integer.valueOf(0);
    }
}

@Override
public int get() {
    return this.cache.computeIfAbsent(KEY, new NotStaticOhWell()::apply).intValue();
}

它失败了,错误消息NotSerializableException:org.infinispan.cache.impl.EncoderCache:

13:41:29,221 ERROR [org.infinispan.interceptors.impl.InvocationContextInterceptor] (default task-1) ISPN000136: Error executing command ComputeIfAbsentCommand, writing keys [value]: org.infinispan.commons.marshall.NotSerializableException: org.infinispan.cache.impl.EncoderCache
    Caused by: an exception which occurred:
    in field sandbox.infinispan.test.service.CounterService.cache
    in object sandbox.infinispan.test.service.CounterService@4612a6c3
    in field sandbox.infinispan.test.service.CounterService$NotStaticOhWell.this$0
    in object sandbox.infinispan.test.service.CounterService$NotStaticOhWell@4effd362
    in field java.lang.invoke.SerializedLambda.capturedArgs
    in object java.lang.invoke.SerializedLambda@e62f08a
    in object sandbox.infinispan.test.service.CounterService$$Lambda$1195/1060417313@174a143b

最后一句话

使用“静态lambda”(实现SerializableFunction接口的静态内部类)也可以

static class StaticSerializableFunction implements SerializableFunction<String, Integer> {

    @Override
    public Integer apply(final String t) {
        return Integer.valueOf(0);
    }
}

@Override
public int get() {
    return this.cache.computeIfAbsent(KEY, new StaticSerializableFunction()::apply).intValue();
}

获胜者是...

通过“转换”缓存使类实际上可序列化,只需使用此类的方法即可。无需创建内部类!

package sandbox.infinispan.test.service;

import java.io.Serializable;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.infinispan.Cache;

@Named("useThisOne")
@ApplicationScoped
public class CounterService implements ICounterService, Serializable {

    private static final String KEY = "value";

    @SuppressWarnings("cdi-ambiguous-dependency")
    @Inject
    private transient Cache<String, Integer> cache;

    @Override
    public void inc(final int amount) {
        this.cache.put(KEY, Integer.valueOf(this.get() + amount));
    }

    @Override
    public int get() {
        return this.cache.computeIfAbsent(KEY, this::zero).intValue();
    }

    private Integer zero(@SuppressWarnings("unused") final String unused) {
        return Integer.valueOf(0);
    }

    @Override
    public void reset() {
        this.cache.clear();
    }
}

谢谢大家!

共有1个答案

姜学海
2023-03-14

根据无法反序列化lambda,反序列化程序需要实际代码才可用。是否确定应用程序已在群集中的所有其他节点上启动(版本完全相同,包括lambda)?

computeIfAbsent()将lambda直接发送到数据(因此使用一个RPC处理操作,而不是先获取值,然后将其写入“丑陋的代码”)。在WF中,您的应用程序位于与Infinispan不同的类加载器(模块)中,这可能会导致问题。

您是否可以尝试将lambda重构为一个类,看看是否会遇到类似的问题?我对WF不是很了解,所以对于lambdas缺少的常规类,可能有一种缓解方法。

 类似资料:
  • Eclipse JDT编译器似乎有一个问题,在某些情况下,Java8个lamda没有正确反序列化,而是抛出一个。我正在使用最近的分布式维护版本,如下所示: 现有的bug/SO条目报告了在中已经(至少部分)解决的类似问题。我亲自验证了以下问题已经得到解决。 无法反序列化访问实例字段和方法的Java 8 lambda https://bugs.eclipse.org/bugs/show_bug.cgi

  • 问题内容: 作为一个小项目,我一直在尝试做一个小事,它可以读取序列化的lambda(从本地或从FTP)并调用它们的运行函数作为测试的一部分,以测试Windows中的文件关联(即打开某些文件类型)使用特定程序打开它们),但不管如何,无论如何,它似乎从未正确地反序列化。 lambda被这样声明 并使用由ObjectOutputStream包装的[n可选] BufferedOutputStream包装的

  • 作为一个小项目,我一直在尝试制作一个小东西来读取序列化的lambda(本地或从FTP)并调用它们的run函数,作为测试的一部分,以在Windows中试验文件关联(即打开某些文件类型会用某个程序打开它们)等等,但无论我尝试什么,它似乎都无法正确地反序列化。 lambda是这样宣布的 并使用由ObjectOutputStream包装的[n可选]BufferedOutputStream包装的FileOu

  • 我在项目中使用Infinispan和hibernate,遇到了一个奇怪的错误日志: ISPN000936:类“[I”被反序列化白名单阻止。请调整配置序列化白名单正则表达式以包含此类 我已经有了这个问题,但是使用了普通的类名,所以我可以通过将类添加到序列化白名单来解决这个问题,如下所示: 但是有了这个奇怪的类名()我不能这么做。我可以通过授权序列化白名单中的所有类来解决这个问题,如下所示: 但是我想

  • 我在redis实现中使用spring缓存,我有以下方法 当我第一次访问该方法时,数据以json格式缓存到redis中。 但是当我第二次访问它时,它会出现一些错误,如下所示: Java语言util。同时发生的执行例外:组织。springframework。数据redis。序列化程序。SerializationException:无法读取JSON:无法构造org的实例。springframework。

  • 我有一个带有lambda表达式的wicket项目。在一个页面上,当用户单击“后退”按钮时,我的应用程序崩溃: 在page类(返回的地方)中,我使用lambda表达式实现此接口: 以及lambda: 当我将lambda更改为匿名类时,一切正常。在这种情况下,我应该如何使用lambda? 环境:Java1.8。0_25,网豆8.0.2,Wicket 6.17.0。 编辑:这是使用lambda的真实(但