From 5d656f24c69c6704d31004e1ec6c960dad376488 Mon Sep 17 00:00:00 2001 From: jaysunxiao Date: Tue, 19 Oct 2021 17:28:30 +0800 Subject: [PATCH] =?UTF-8?q?perf[orm]:=20=E5=8E=BB=E6=8E=89IEntityCaches?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/zfoo/orm/manager/OrmManager.java | 37 ++++++++++++------- .../zfoo/orm/model/cache/EntityCaches.java | 16 +------- .../zfoo/orm/model/cache/IEntityCaches.java | 4 -- .../com/zfoo/orm/model/entity/IEntity.java | 8 ++-- .../zfoo/storage/manager/StorageManager.java | 23 +++++++----- 5 files changed, 42 insertions(+), 46 deletions(-) diff --git a/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java b/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java index 5e700718..7b665bf7 100644 --- a/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java +++ b/orm/src/main/java/com/zfoo/orm/manager/OrmManager.java @@ -63,13 +63,18 @@ import java.util.stream.Collectors; */ public class OrmManager implements IOrmManager { - private static final Map>, IEntityCaches> entityCachesMap = new HashMap<>(); - private OrmConfig ormConfig; private MongoClient mongoClient; private MongoDatabase mongodbDatabase; + /** + * 全部的Entity定义,key为对应的class,value为当前的Entity是否在当前项目中以缓存的形式使用 + */ + private final Map, Boolean> allEntityCachesUsableMap = new HashMap<>(); + + private final Map>, IEntityCaches> entityCachesMap = new HashMap<>(); + private final Map>, String> collectionNameMap = new ConcurrentHashMap<>(); public OrmConfig getOrmConfig() { @@ -87,6 +92,7 @@ public class OrmManager implements IOrmManager { for (var entityDef : entityDefMap.values()) { var entityCaches = new EntityCaches(entityDef); entityCachesMap.put(entityDef.getClazz(), entityCaches); + allEntityCachesUsableMap.put(entityDef.getClazz(), false); } CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries( @@ -172,6 +178,7 @@ public class OrmManager implements IOrmManager { public void inject() { var applicationContext = OrmContext.getApplicationContext(); var beanNames = applicationContext.getBeanDefinitionNames(); + for (var beanName : beanNames) { var bean = applicationContext.getBean(beanName); @@ -185,34 +192,37 @@ public class OrmManager implements IOrmManager { } Type[] types = ((ParameterizedType) type).getActualTypeArguments(); - Class> clazz = (Class>) types[1]; - IEntityCaches entityCaches = OrmContext.getOrmManager().getEntityCaches(clazz); + Class> entityClazz = (Class>) types[1]; + IEntityCaches entityCaches = entityCachesMap.get(entityClazz); if (entityCaches == null) { - throw new RuntimeException(StringUtils.format("实体缓存对象[entityCaches:{}]不存在", clazz)); + throw new RuntimeException(StringUtils.format("实体缓存对象[entityCaches:{}]不存在", entityClazz)); } ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, bean, entityCaches); - entityCaches.setUsable(true); + allEntityCachesUsableMap.put(entityClazz, true); }); } } @Override public void initAfter() { - var unusableEntityClassList = entityCachesMap.entrySet().stream() - .filter(it -> !it.getValue().isUsable()) + allEntityCachesUsableMap.entrySet().stream() + .filter(it -> !it.getValue()) .map(it -> it.getKey()) - .collect(Collectors.toList()); - - unusableEntityClassList.forEach(it -> { - entityCachesMap.remove(it); - }); + .forEach(it -> entityCachesMap.remove(it)); } @Override public > IEntityCaches getEntityCaches(Class clazz) { + var usable = allEntityCachesUsableMap.get(clazz); + if (usable == null) { + throw new RunException("没有定义[]的EntityCaches,无法获取", clazz.getCanonicalName()); + } + if (!usable) { + throw new RunException("Orm没有使用[]的EntityCaches,为了节省内存提前释放了它;只有使用EntityCachesInjection注解的Entity才能被动态获取", clazz.getCanonicalName()); + } return (IEntityCaches) entityCachesMap.get(clazz); } @@ -233,7 +243,6 @@ public class OrmManager implements IOrmManager { collectionName = StringUtils.substringBeforeLast(StringUtils.uncapitalize(entityClazz.getSimpleName()), "Entity"); collectionNameMap.put(entityClazz, collectionName); } - return mongodbDatabase.getCollection(collectionName, entityClazz); } diff --git a/orm/src/main/java/com/zfoo/orm/model/cache/EntityCaches.java b/orm/src/main/java/com/zfoo/orm/model/cache/EntityCaches.java index 97411d21..40657d29 100644 --- a/orm/src/main/java/com/zfoo/orm/model/cache/EntityCaches.java +++ b/orm/src/main/java/com/zfoo/orm/model/cache/EntityCaches.java @@ -50,11 +50,9 @@ public class EntityCaches, E extends IEntity> impl private static final int BATCH_SIZE = 512; - private EntityDef entityDef; + private final EntityDef entityDef; - private LoadingCache> cache; - - private boolean usable; + private final LoadingCache> cache; public EntityCaches(EntityDef entityDef) { this.entityDef = entityDef; @@ -304,16 +302,6 @@ public class EntityCaches, E extends IEntity> impl return cache.estimatedSize(); } - @Override - public boolean isUsable() { - return this.usable; - } - - @Override - public void setUsable(boolean usable) { - this.usable = usable; - } - @Override public String recordStatus() { var stats = cache.stats(); diff --git a/orm/src/main/java/com/zfoo/orm/model/cache/IEntityCaches.java b/orm/src/main/java/com/zfoo/orm/model/cache/IEntityCaches.java index 38ed1e1f..8eb5c802 100644 --- a/orm/src/main/java/com/zfoo/orm/model/cache/IEntityCaches.java +++ b/orm/src/main/java/com/zfoo/orm/model/cache/IEntityCaches.java @@ -55,10 +55,6 @@ public interface IEntityCaches, E extends IEntity> long size(); - boolean isUsable(); - - void setUsable(boolean usable); - /** * 统计缓存命中率 */ diff --git a/orm/src/main/java/com/zfoo/orm/model/entity/IEntity.java b/orm/src/main/java/com/zfoo/orm/model/entity/IEntity.java index d714e100..454b08e7 100644 --- a/orm/src/main/java/com/zfoo/orm/model/entity/IEntity.java +++ b/orm/src/main/java/com/zfoo/orm/model/entity/IEntity.java @@ -28,11 +28,11 @@ public interface IEntity> { /** * 一个文档的写入到数据库的version版本,version的get和set方法 *

- * 写入一条数据到数据库的时候会对比以下老的vs和数据库的vs是不是一样,不是一样的话无法写入,一致的话就写入数据并且让vs自增+1. - * 主要是为了防止多个服务器去操作同一条数据,保证分布式环境的数据一致性,就是一个数据版本号的简单实现,版本号一致才能写入数据。 - * 在分布式环境的那种有状态服务器中会遇到,比如网关路由一个玩家的数据到a服务器,这个时候你加了一个b服务器,有可能下一条数据就被路由到b。 + * 写入一条数据到数据库的时候会对比当前entity的vs和数据库的vs是不是一样,不是一样的话无法写入,一致的话就写入数据并且让vs自增+1. + * 主要是为了防止多个服务器去操作同一条数据,保证在分布式环境的数据一致性;简单的说就是一个数据版本号的简单实现,版本号一致才能写入数据。 + * 在分布式环境中,会存在有状态服务器,比如网关路由一个玩家的数据到a服务器,这个时候你加了一个b服务器,有可能下一条数据就被路由到b。 * 虽然用了一致性hash的负载均衡算法,但是一样有概率会让某些消息路由到不同服务器。 - * 这个时候版本号就可以保住只有一台服务器可以对数据库做操作,不用担心多个服务器去操作数据。 + * 这个时候版本号就可以保证只有一台服务器可以对数据库做操作,不用担心多个服务器去操作数据。 * 这是一个容错的操作,真实环境下很少发生。为了高性能必须要把服务器做成有状态的,这个容错操作就是最后一道保证数据一致的方案。 * mongodb更新的时候是原子的,并发更新同一条数据只有一条数据会写入,第二条数据写入的时候版本号已经不一致了,所以老版本号的数据无法写入。 * 写入的时候只要发现版本号不一致,就让缓存失效,重新读取数据库最新的数据。 diff --git a/storage/src/main/java/com/zfoo/storage/manager/StorageManager.java b/storage/src/main/java/com/zfoo/storage/manager/StorageManager.java index b46b73d9..32a82004 100644 --- a/storage/src/main/java/com/zfoo/storage/manager/StorageManager.java +++ b/storage/src/main/java/com/zfoo/storage/manager/StorageManager.java @@ -15,6 +15,7 @@ package com.zfoo.storage.manager; import com.zfoo.protocol.collection.CollectionUtils; import com.zfoo.protocol.exception.ExceptionUtils; +import com.zfoo.protocol.exception.RunException; import com.zfoo.protocol.util.ReflectionUtils; import com.zfoo.protocol.util.StringUtils; import com.zfoo.storage.StorageContext; @@ -34,7 +35,6 @@ import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; -import java.util.stream.Collectors; /** * @author jaysunxiao @@ -56,12 +56,12 @@ public class StorageManager implements IStorageManager { /** * 在当前项目被依赖注入,被使用的Storage */ - private Map, Storage> storageMap = new HashMap<>(); + private final Map, Storage> storageMap = new HashMap<>(); /** * 全部的Storage定义,key为对应的excel配置表,value为当前配置表是否在当前项目中使用 */ - private Map, Boolean> allStorageUsableMap = new HashMap<>(); + private final Map, Boolean> allStorageUsableMap = new HashMap<>(); public StorageConfig getStorageConfig() { return storageConfig; @@ -133,7 +133,7 @@ public class StorageManager implements IStorageManager { Class resourceClazz = (Class) types[1]; - Storage storage = StorageContext.getStorageManager().getStorage(resourceClazz); + Storage storage = storageMap.get(resourceClazz); if (storage == null) { throw new RuntimeException(StringUtils.format("静态类资源[resource:{}]不存在", resourceClazz.getSimpleName())); @@ -158,18 +158,21 @@ public class StorageManager implements IStorageManager { @Override public void initAfter() { - var unusableStorageClassList = allStorageUsableMap.entrySet().stream() + allStorageUsableMap.entrySet().stream() .filter(it -> !it.getValue()) .map(it -> it.getKey()) - .collect(Collectors.toList()); - - unusableStorageClassList.forEach(it -> { - storageMap.remove(it); - }); + .forEach(it -> storageMap.remove(it)); } @Override public Storage getStorage(Class clazz) { + var usable = allStorageUsableMap.get(clazz); + if (usable == null) { + throw new RunException("没有定义[{}]的Storage,无法获取", clazz.getCanonicalName()); + } + if (!usable) { + throw new RunException("Storage没有使用[{}],为了节省内存提前释放了它;只有使用ResInjection注解的Storage才能被动态获取", clazz.getCanonicalName()); + } return storageMap.get(clazz); }