hazelcast_Hazelcast的MapLoader陷阱

hazelcast

Hazelcast提供的核心数据结构之一是IMap<K, V> ,它扩展了java.util.concurrent.ConcurrentMap ,它基本上是一个分布式地图,通常用作缓存。 您可以将此类地图配置为使用自定义MapLoader<K, V> -每次尝试从该地图(通过键)获取.get()尚不存在的内容时都会被询问的一段Java代码。 当您将IMap用作内存中的分布式缓存时,这特别有用–如果客户端代码要求尚未缓存的内容,Hazelcast将透明地执行MapLoader.load(key)

public interface MapLoader<K, V> {V load(K key);Map<K, V> loadAll(Collection<K> keys);Set<K> loadAllKeys();
}

其余两种方法在启动期间用于通过加载预定义的键集来预热缓存。 您的自定义MapLoader可以连接到(否)SQL数据库,Web服务,文件系统(您命名)。 使用这样的缓存更加方便,因为您不必执行繁琐的“ 如果不在缓存负载中并放入缓存 ”循环。 此外, MapLoader具有一项出色的功能-如果许多客户端在同一时间(从不同的线程,甚至从不同的集群成员,从而从机器)要求相同的密钥,则MapLoader仅执行一次。 这显着减少了外部依赖项的负担,而没有引入任何复杂性。

从本质上说IMapMapLoader类似于LoadingCache中发现的番石榴 -但分布。 但是,强大的功能会带来极大的挫败感,尤其是当您不了解API的特殊性和分布式系统固有的复杂性时。

首先,让我们看看如何配置自定义MapLoader 。 您可以hazelcast.xml使用hazelcast.xml<map-store/>元素),但是您无法控制加载程序的生命周期(例如,不能使用Spring bean)。 一个更好的主意是直接从代码配置Hazelcast并传递MapLoader的实例:

class HazelcastTest extends Specification {public static final int ANY_KEY = 42public static final String ANY_VALUE = "Forty two"def 'should use custom loader'() {given:MapLoader loaderMock = Mock()loaderMock.load(ANY_KEY) >> ANY_VALUEdef hz = build(loaderMock)IMap<Integer, String> emptyCache = hz.getMap("cache")when:def value = emptyCache.get(ANY_KEY)then:value == ANY_VALUEcleanup:hz?.shutdown()}

请注意,我们如何获得一个空的地图,但是当要求输入ANY_KEY ,我们得到ANY_VALUE作为回报。 这不足为奇,这正是我们的loaderMock所期望的。 我离开了Hazelcast配置:

def HazelcastInstance build(MapLoader<Integer, String> loader) {final Config config = new Config("Cluster")final MapConfig mapConfig = config.getMapConfig("default")final MapStoreConfig mapStoreConfig = new MapStoreConfig()mapStoreConfig.factoryImplementation = {name, props -> loader } as MapStoreFactorymapConfig.mapStoreConfig = mapStoreConfigreturn Hazelcast.getOrCreateHazelcastInstance(config)
}

任何IMap (按名称标识)都可以具有不同的配置。 但是,特殊的"default"映射为所有映射指定默认配置。 让我们玩一下自定义加载器,看看当MapLoader返回null或引发异常时它们的行为:

def 'should return null when custom loader returns it'() {given:MapLoader loaderMock = Mock()def hz = build(loaderMock)IMap<Integer, String> cache = hz.getMap("cache")when:def value = cache.get(ANY_KEY)then:value == null!cache.containsKey(ANY_KEY)cleanup:hz?.shutdown()
}public static final String SOME_ERR_MSG = "Don't panic!"def 'should propagate exceptions from loader'() {given:MapLoader loaderMock = Mock()loaderMock.load(ANY_KEY) >> {throw new UnsupportedOperationException(SOME_ERR_MSG)}def hz = build(loaderMock)IMap<Integer, String> cache = hz.getMap("cache")when:cache.get(ANY_KEY)then:UnsupportedOperationException e = thrown()e.message.contains(SOME_ERR_MSG)cleanup:hz?.shutdown()
}

到目前为止,不足为奇。 您可能遇到的第一个陷阱是线程在这里如何交互。 永远不会从客户端线程执行MapLoader ,而总是从单独的线程池执行:

def 'loader works in a different thread'() {given:MapLoader loader = Mock()loader.load(ANY_KEY) >> {key -> "$key: ${Thread.currentThread().name}"}def hz = build(loader)IMap<Integer, String> cache = hz.getMap("cache")when:def value = cache.get(ANY_KEY)then:value != "$ANY_KEY: ${Thread.currentThread().name}"cleanup:hz?.shutdown()
}

该测试通过是因为当前线程是"main"线程,而加载是从"hz.Cluster.partition-operation.thread-10" 。 这是一个重要的观察结果,如果您记得当许多线程尝试访问相同的缺席密钥时,加载程序仅被调用一次,则这实际上很明显。 但是,这里需要进一步说明。 IMap上的几乎每个操作都封装到一个操作对象中 (另请参见: 命令模式 )。 此操作随后分派给一个或所有群集成员,并在单独的线程池中甚至在另一台计算机上远程执行。 因此,不要期望加载发生在同一线程,甚至同一JVM /服务器(!)中。

这会导致一种有趣的情况,您在一台计算机上请求给定密钥,而另一台计算机上实际加载。 甚至更史诗般的–机器A,B和C请求给定密钥,而机器D实际加载该密钥的值。 基于一致的哈希算法确定哪个机器负责加载。

最后一句话–当然,您可以自定义运行这些操作的线程池的大小,请参阅“ 高级配置属性” 。

考虑到这一点,这是完全令人惊讶的,绝对可以预期的:

def 'IMap.remove() on non-existing key still calls loader (!)'() {given:MapLoader loaderMock = Mock()def hz = build(loaderMock)IMap<Integer, String> emptyCache = hz.getMap("cache")when:emptyCache.remove(ANY_KEY)then:1 * loaderMock.load(ANY_KEY)cleanup:hz?.shutdown()
}

仔细地看! 我们要做的就是从地图上删除缺少的密钥。 没有其他的。 但是, loaderMock.load()已执行。 这是一个问题,特别是当您的自定义加载程序特别慢或昂贵时。 为什么在这里执行? 查找`java.util.Map#remove()的API:

V remove(Object key)

[…]

返回此映射先前与该键关联的值;如果该映射不包含该键的映射,则返回null。

也许这是有争议的,但有人可能会认为Hazelcast做得正确。 如果您认为附有MapLoader的地图就像外部存储的视图一样,那是有道理的。 当删除缺少的密钥时,Hazelcast实际上会询问我们的MapLoader :以前的值是什么? 它假装地图好像包含从MapLoader返回的每个单个值,但延迟加载。 这不是错误,因为有一个特殊的方法IMap.delete()就像remove()一样工作,但是不会加载“以前的”值:

@Issue("https://github.com/hazelcast/hazelcast/issues/3178")
def "IMap.delete() doesn't call loader"() {given:MapLoader loaderMock = Mock()def hz = build(loaderMock)IMap<Integer, String> cache = hz.getMap("cache")when:cache.delete(ANY_KEY)then:0 * loaderMock.load(ANY_KEY)cleanup:hz?.shutdown()
}

实际上,存在一个错误: IMap.delete()不应调用 3.2.6和3.3中修复的MapLoader.load() 。 如果尚未升级,则即使IMap.delete()也将转到MapLoader 。 如果您认为IMap.remove()令人惊讶,请查看put()工作原理!

如果您认为remove()加载值是可疑的,那么显式put()首先为给定键加载值怎么办? 毕竟,我们明确地通过密钥将某些内容放入地图中,为什么Hazelcast首先通过MapLoader加载此值?

def 'IMap.put() on non-existing key still calls loader (!)'() {given:MapLoader loaderMock = Mock()def hz = build(loaderMock)IMap<Integer, String> emptyCache = hz.getMap("cache")when:emptyCache.put(ANY_KEY, ANY_VALUE)then:1 * loaderMock.load(ANY_KEY)cleanup:hz?.shutdown()
}

再次,让我们还原到java.util.Map.put() JavaDoc:

V put(K键,V值)

[…]

返回值:

与key关联的先前值;如果没有key映射,则为null。

Hazelcast假设IMap只是对某些外部源的懒惰视图,因此当我们put()某些内容放到以前没有的IMap中时,它会首先加载“ previous”值,以便它可以返回它。 同样,当MapLoader速度慢或价格昂贵时,这又是一个大问题–如果我们可以明确地将某些内容放入地图中,为什么要先加载它? 幸运的是,有一个简单的解决方法putTransient()

def "IMap.putTransient() doesn't call loader"() {given:MapLoader loaderMock = Mock()def hz = build(loaderMock)IMap<Integer, String> cache = hz.getMap("cache")when:cache.putTransient(ANY_KEY, ANY_VALUE, 1, TimeUnit.HOURS)then:0 * loaderMock.load(ANY_KEY)cleanup:hz?.shutdown()
}

一个警告是,您必须显式提供TTL,而不是依赖于已配置的IMap默认值。 但这也意味着您可以为每个映射条目分配任意TTL,不仅可以全局分配给整个映射-很有用。

记住我们的比喻: IMap与后盾MapLoader行为就像在数据的外部源视图。 这就是为什么在空地图上的containsKey()会调用MapLoader并不令人惊讶的原因:

def "IMap.containsKey() calls loader"() {given:MapLoader loaderMock = Mock()def hz = build(loaderMock)IMap<Integer, String> emptyMap = hz.getMap("cache")when:emptyMap.containsKey(ANY_KEY)then:1 * loaderMock.load(ANY_KEY)cleanup:hz?.shutdown()
}

每当我们请求地图中不存在的键时,Hazelcast都会询问MapLoader 。 同样,只要装载机速度快,无副作用且可靠,这不是问题。 如果不是这种情况,将会杀死您:

def "IMap.get() after IMap.containsKey() calls loader twice"() {given:MapLoader loaderMock = Mock()def hz = build(loaderMock)IMap<Integer, String> cache = hz.getMap("cache")when:cache.containsKey(ANY_KEY)cache.get(ANY_KEY)then:2 * loaderMock.load(ANY_KEY)cleanup:hz?.shutdown()
}

尽管containsKey()调用MapLoader ,它不会“缓存”加载的值以供以后使用。 这就是为什么containsKey()后跟get()两次调用MapLoader ,这非常浪费。 幸运的是,如果您在现有密钥上调用containsKey() ,则它几乎立即运行,尽管很可能需要网络跳转。 不幸的是,Hazelcast 3.3版之前的keySet()values()entrySet()和其他一些方法的行为。 如果一次加载任何密钥,这些将全部阻止。 因此,如果您有一个包含数千个键的映射,并且要求提供keySet() ,则缓慢的MapLoader.load()调用将阻塞整个群集。 幸运的是,此问题已在3.3中修复,因此即使当前正在计算某些键,也不会阻塞IMap.keySet()IMap.values()等。


如您所见, IMap + MapLoader组合功能强大,但也充满陷阱。 其中一些由API规定,osme由Hazelcast的分布式特性决定,最后一些是特定于实现的。 在实施加载缓存功能之前,请确保您了解它们。

翻译自: https://www.javacodegeeks.com/2014/09/hazelcasts-maploader-pitfalls.html

hazelcast

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/339912.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html ul左侧浮动,UL里的LI元素左浮动层一行显示时使其居中的方法

在制作页面是&#xff0c;li浮动是很常用的&#xff0c;一般情况也不用其居中&#xff0c;但有时&#xff0c;其特殊原因需要居中&#xff0c;这是就有点犯难了&#xff0c;这里有了一个很好的解决方法&#xff0c;主要是用了相对定位的原理。在ul外报一层&#xff0c;使其相对…

aws s3 獲取所有文件_AWS SA associate 证书考试学习记录-EBS,S3,EFS比较

我们的目标&#xff0c;就是花最少的时间&#xff0c;学到最多的东西&#xff1a;-&#xff09;在AWS中&#xff0c;可以选择的存储服务很多&#xff0c;纷繁复杂&#xff0c;新手根本弄不清楚选择哪个。因为做为一个云架构师&#xff0c;你并不是让你的产品能用就够了&#xf…

ios调用restful接口_做iOS上最好的REST API测试App

对于Web开发者和移动应用开发者来说&#xff0c;少不了和REST API打交道。何为REST API&#xff0c;维基百科是这么解释的(https://zh.wikipedia.org/wiki/REST)REST(英文&#xff1a;Representational State Transfer)是Roy Thomas Fielding博士于2000年在他的博士论文中提出来…

Java 14:记录

Java 14是在几周前问世的&#xff0c;它引入了Record类型&#xff0c;它是一个不变的数据载体类&#xff0c;旨在容纳一组固定的字段。 请注意&#xff0c;这是预览语言功能 &#xff0c;这意味着必须使用--enable-preview标志在Java编译器和运行时中显式启用它。 我将直接介绍…

html设置顶部对齐,HTML / CSS文本从div顶部对齐

首先&#xff0c;你需要修复你的CSS选择器 .你可以通过这种方式t write all those id .#content #main #services只需选择1个元素和他的孩子 .例如&#xff0c;如果你测试它&#xff0c;它将适用于你&#xff1a;#services .langelis .txt {width: 440px;height: auto;float: l…

java与java ee_计划Java EE 7批处理作业

java与java eeJava EE 7添加了使用JSR 352以标准方式执行批处理作业的功能。 <job id"myJob" xmlns"http://xmlns.jcp.org/xml/ns/javaee" version"1.0"><step id"myStep"><chunk item-count"3"><rea…

html5 video 播放状态,10分钟了解HTML5的Video标签属性、方法和事件

标签的属性src &#xff1a;视频的属性poster&#xff1a;视频封面&#xff0c;没有播放时显示的图片preload&#xff1a;预加载autoplay&#xff1a;自动播放loop&#xff1a;循环播放controls&#xff1a;浏览器自带的控制条width&#xff1a;视频宽度height&#xff1a;视频…

hot编码 字符one_One Hot编码是什么?为什么要用它,什么时候用它?

作者&#xff1a;Rakshith Vasudev编译&#xff1a;ronghuaiyang导读当你在玩ML模型的时候&#xff0c;你会在任何地方遇到这个“One hot encoding”的术语。当你在玩ML模型的时候&#xff0c;你会在任何地方遇到这个“One hot encoding”术语。你可以看到一个one hot编码器的s…

CUBA 7.2 –有什么新功能?

CUBA平台的第七版向前迈出了一大步。 内部体系结构的改进和新的IDE为进一步改进奠定了良好的基础。 我们将继续添加新功能&#xff0c;以使开发人员的生活更轻松&#xff0c;并使他们的工作更加高效。 在7.2版中&#xff0c;我们引入了许多可能看起来像是主要更新的更改&#…

postgresql 分区视图_PostgreSQL架构集中式到分布式主流架构总结

文章目录一、PG未来主流架构为什么是分布式二、PostgreSQL集中式到分布式架构总结一、PG未来主流架构为什么是分布式如果说5年前DB的分布式还只是一种趋势&#xff0c;如今分布式数据库正逐渐从趋势变成主流。说到分布式&#xff0c;我想我们不能不提一下集中式和分库分表。01集…

html5 上传图片模板,HTML5实现图片文件异步上传

&#xff0c;过现前个能文使近记接的端问对字用近记接  利用HTML5的新特点做文件异步上传非常简单方便&#xff0c;本文主要展示JS部分&#xff0c;html结构。下面的代码并未使用第三发库&#xff0c;如果有参照&#xff0c;请注意一些未展现出来的代码片段。我这边的效果预览…

dot2谜团png_一个类加载的谜团解决了

dot2谜团png面对一个好老问题 我在应用程序服务器上遇到一些类加载问题。 这些库被定义为Maven依赖项&#xff0c;因此被打包到WAR和EAR文件中。 不幸的是&#xff0c;其中一些还安装在应用程序服务器中&#xff0c;但版本不同。 启动应用程序时&#xff0c;我们遇到了与这些类…

python log文件如何不写入syslog_Centos下python 对syslog重写进行日志记录

在Linux 环境下&#xff0c;python自带一个syslog的模块可以进行日志记录。python可以利用logging模块来重写syslog&#xff0c;这样就可以自定义写入文件的文件名。如果不做配置则直接写入到/var/log/message文件里。首先先写log.py,代码如下&#xff1a;importosimportsysimp…

html中可以有两个h1,在一个HTML中h1标签能出现几次?h1标签和标题标签

首页 > web前端 > html教程 > 正文 在一个HTML中h1标签能出现几次&#xff1f;h1标签和标题标签的差别是什么&#xff1f; 2018-08-29 10:57:28本篇文章主要介绍了关于HTML h1标签的一些解释&#xff0c;有html h1标签和html title标签的区别&#xff0c;还有网页中h1…

Java中的记录类型

2020年3月发布的JDK 14引入了记录 &#xff08;预览语言功能&#xff09;&#xff0c;这些记录提供了一种紧凑的语法来声明主要用于保存数据的类。 在记录中 &#xff0c;所有低级&#xff0c;重复且容易出错的代码都类似于构造函数&#xff0c;访问器和通用方法&#xff0c;例…

angular 注入器配置_Angular依赖注入介绍

依赖注入(DI -- Dependency Injection)是一种重要的应用设计模式。Angular里面也有自己的DI框架&#xff0c;在设计应用时经常会用到它&#xff0c;它可以我们的开发效率和模块化程度。依赖&#xff0c;是当类需要执行其功能时&#xff0c;所需要的服务或对象。DI是一种编码模式…

山东省102021年普通高考成绩查询,山东高考成绩今日发布!成绩查询看这里!

原标题&#xff1a;山东高考成绩今日发布&#xff01;成绩查询看这里&#xff01;山东高考生注意啦~今天16:20举行山东2020年夏季高考第二次新闻发布会届时将会公布高考录取政策、分数线情况等今天17:00公布2020夏季高考与等级考成绩发布会怎么看&#xff1f;高考成绩怎样查&am…

可变对象 不可变对象区别_对象应该是不可变的

可变对象 不可变对象区别在面向对象的编程中&#xff0c;如果对象的状态在创建后无法修改&#xff0c;则它是不可变的 。 在Java中&#xff0c;不可变对象的一个​​很好的例子是String 。 创建后&#xff0c;我们无法修改其状态。 我们可以要求它创建新的字符串&#xff0c;但…

判别分析分为r型和q型吗_SPSS聚类和判别分析参考.ppt

SPSS聚类和判别分析参考10.1主成分分析和因子分析简介 3 常用术语 (1)因子载荷 (2)变量共同度 (3)公共因子的方差贡献 10.1主成分分析和因子分析简介 10.1.2主成分和公因子数量的确定 (1) 确定时遵循几个原则 主成分的累积贡献率&#xff1a;一般来说&#xff0c;提取主成分的累…

计算机应用基础人才培养方案,1. 培养方案(计算机应用基础课程).doc

人才培养方案一、课程定位和课程目标1、课程性质和任务  《计算机应用基础》是高职高专教育中的一门理论与实践相结合的基础必修课&#xff0c;是培养大学生信息素养的必修课程。但与普通的素质教育课程不同&#xff0c;由于计算机应用的普及性和广泛性&#xff0c;学生的计算…