
كيفية استخدام ذاكرة التخزين المؤقت للمنطقة (Region Cache) في SAP Commerce (Hybris)
تُستخدم عملية التخزين المؤقت (cacheing) لتخزين البيانات المطلوبة بشكل متكرر واستردادها بسرعة عند الحاجة.
يوفر التخزين المؤقت الإقليمي في SAP Commerce (Region Cache) إمكانية تقسيم المحتوى إلى ما يسمى بالمناطق (regions). يتيح لنا هذا النهج التحكم في الحجم المخصص لكائنات معينة في الذاكرة المؤقتة واستراتيجية الإخلاء الخاصة بها.
كما نرى في الوثائق الرسمية، يستخدم SAP Commerce أنواع الإخلاء التالية:
الأقل استخداماً مؤخراً (LRU): يتم تحديث الطابع الزمني لآخر استخدام عند وضع عنصر في الذاكرة المؤقتة أو عند استرداد عنصر منها عبر استدعاء GET.
الأقل استخداماً تكراراً (LFU): مع كل استدعاء GET للعنصر، يتم تحديث عدد مرات الوصول. وعند إجراء استدعاء PUT لعنصر جديد، وبافتراض الوصول إلى الحد الأقصى لمساحة التخزين، يتم إخلاء العنصر الذي يحتوي على أقل عدد من مرات الوصول (العنصر الأقل استخداماً تكراراً).
الوارد أولاً، يصدر أولاً (FIFO): يتم إخلاء العناصر بنفس الترتيب الذي دخلت به. وعند إجراء استدعاء PUT لعنصر جديد، وبافتراض الوصول إلى الحد الأقصى لمساحة التخزين، فإن العنصر الذي تم وضعه أولاً في المخزن هو المرشح للإخلاء أولاً.
مزيد من المعلومات هنا
في السطور التالية سوف نستكشف مثالاً واقعياً لاستخدام ذاكرة تخزين مؤقت إقليمية مخصصة في Hybris (Hybris Region Cache).
قم بتوسيع
EHCacheRegionوتنفيذ الطرق (methods) التي تحتاجها.
public class CacheAccessImpl extends EHCacheRegion implements CacheAccess{ private static final Logger LOG = LoggerFactory.getLogger(CacheAccessImpl.class); // Insert Constructors @Override public Object get(Object key) { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); try { return super.get(genericCacheKey); } catch (final IllegalStateException e) { LOG.error("IllegalStateException occured", e); return null; } } @Override public void put(Object key, Object object) throws CacheAccessException { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); final DefaultCacheValueLoaderImpl<Object> loader = new DefaultCacheValueLoaderImpl<>(); loader.setValue(object); remove(genericCacheKey); LOG.debug("Object with following key(s) {} put into cache region {} ", genericCacheKey.toString(), this.getName()); super.getWithLoader(genericCacheKey, loader); } @Override public void putIfAbsent(final Object key, final Object object) throws CacheAccessException { if (isAbsent(key)) { put(key, object); } } @Override public void remove(Object key) throws CacheAccessException { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); LOG.debug("Object with following key(s) {} removed from cache region {} ", genericCacheKey.toString(), this.getName()); super.remove(genericCacheKey, false); } /** * Generates a {@link GenericCacheKey} from key object. * * @param key to be converted * @return {@link GenericCacheKey} */ private GenericCacheKey generateGenericCacheKey(final Object key) { if (key instanceof GenericCacheKey) { return (GenericCacheKey) key; } else { return new GenericCacheKey(key, GenericCacheKey.DEFAULT_CACHE_TYPECODE); } } /** * Checks if the key is in cache. * * @param key the key for which the absence is checked * @return true if key is not in cache, false if it is in cache */ private boolean isAbsent(final Object key) { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); return !super.containsKey(genericCacheKey); } /** * Inner implementation of the {@link CacheValueLoader} which is used for method <code>put</code>. * * @param <V> */ private static class DefaultCacheValueLoaderImpl<V> implements CacheValueLoader<V> { private V obj; /** * Standard constructor. */ public DefaultCacheValueLoaderImpl() { super(); } @Override public V load(final CacheKey arg0) { return this.obj; } /** * Set value to be loaded from {@link CacheValueLoader}. * * @param obj value to be loaded. */ @SuppressWarnings("unchecked") public void setValue(final Object obj) { this.obj = (V) obj; } } }
public class CacheAccessImpl extends EHCacheRegion implements CacheAccess{ private static final Logger LOG = LoggerFactory.getLogger(CacheAccessImpl.class); // Insert Constructors @Override public Object get(Object key) { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); try { return super.get(genericCacheKey); } catch (final IllegalStateException e) { LOG.error("IllegalStateException occured", e); return null; } } @Override public void put(Object key, Object object) throws CacheAccessException { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); final DefaultCacheValueLoaderImpl<Object> loader = new DefaultCacheValueLoaderImpl<>(); loader.setValue(object); remove(genericCacheKey); LOG.debug("Object with following key(s) {} put into cache region {} ", genericCacheKey.toString(), this.getName()); super.getWithLoader(genericCacheKey, loader); } @Override public void putIfAbsent(final Object key, final Object object) throws CacheAccessException { if (isAbsent(key)) { put(key, object); } } @Override public void remove(Object key) throws CacheAccessException { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); LOG.debug("Object with following key(s) {} removed from cache region {} ", genericCacheKey.toString(), this.getName()); super.remove(genericCacheKey, false); } /** * Generates a {@link GenericCacheKey} from key object. * * @param key to be converted * @return {@link GenericCacheKey} */ private GenericCacheKey generateGenericCacheKey(final Object key) { if (key instanceof GenericCacheKey) { return (GenericCacheKey) key; } else { return new GenericCacheKey(key, GenericCacheKey.DEFAULT_CACHE_TYPECODE); } } /** * Checks if the key is in cache. * * @param key the key for which the absence is checked * @return true if key is not in cache, false if it is in cache */ private boolean isAbsent(final Object key) { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); return !super.containsKey(genericCacheKey); } /** * Inner implementation of the {@link CacheValueLoader} which is used for method <code>put</code>. * * @param <V> */ private static class DefaultCacheValueLoaderImpl<V> implements CacheValueLoader<V> { private V obj; /** * Standard constructor. */ public DefaultCacheValueLoaderImpl() { super(); } @Override public V load(final CacheKey arg0) { return this.obj; } /** * Set value to be loaded from {@link CacheValueLoader}. * * @param obj value to be loaded. */ @SuppressWarnings("unchecked") public void setValue(final Object obj) { this.obj = (V) obj; } } }
public class CacheAccessImpl extends EHCacheRegion implements CacheAccess{ private static final Logger LOG = LoggerFactory.getLogger(CacheAccessImpl.class); // Insert Constructors @Override public Object get(Object key) { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); try { return super.get(genericCacheKey); } catch (final IllegalStateException e) { LOG.error("IllegalStateException occured", e); return null; } } @Override public void put(Object key, Object object) throws CacheAccessException { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); final DefaultCacheValueLoaderImpl<Object> loader = new DefaultCacheValueLoaderImpl<>(); loader.setValue(object); remove(genericCacheKey); LOG.debug("Object with following key(s) {} put into cache region {} ", genericCacheKey.toString(), this.getName()); super.getWithLoader(genericCacheKey, loader); } @Override public void putIfAbsent(final Object key, final Object object) throws CacheAccessException { if (isAbsent(key)) { put(key, object); } } @Override public void remove(Object key) throws CacheAccessException { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); LOG.debug("Object with following key(s) {} removed from cache region {} ", genericCacheKey.toString(), this.getName()); super.remove(genericCacheKey, false); } /** * Generates a {@link GenericCacheKey} from key object. * * @param key to be converted * @return {@link GenericCacheKey} */ private GenericCacheKey generateGenericCacheKey(final Object key) { if (key instanceof GenericCacheKey) { return (GenericCacheKey) key; } else { return new GenericCacheKey(key, GenericCacheKey.DEFAULT_CACHE_TYPECODE); } } /** * Checks if the key is in cache. * * @param key the key for which the absence is checked * @return true if key is not in cache, false if it is in cache */ private boolean isAbsent(final Object key) { final GenericCacheKey genericCacheKey = generateGenericCacheKey(key); return !super.containsKey(genericCacheKey); } /** * Inner implementation of the {@link CacheValueLoader} which is used for method <code>put</code>. * * @param <V> */ private static class DefaultCacheValueLoaderImpl<V> implements CacheValueLoader<V> { private V obj; /** * Standard constructor. */ public DefaultCacheValueLoaderImpl() { super(); } @Override public V load(final CacheKey arg0) { return this.obj; } /** * Set value to be loaded from {@link CacheValueLoader}. * * @param obj value to be loaded. */ @SuppressWarnings("unchecked") public void setValue(final Object obj) { this.obj = (V) obj; } } }
قم بتهيئة منطقة التخزين المؤقت الجديدة لديك. قم بإنشاء ملف تهيئة جديد مخصص لـ Spring cache. (مثال: devistacore/resources/devistacore-spring-cache.xml)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <alias alias="devistaPriceCacheRegion" name="defaultDevistaPriceCacheRegion"/> <bean id="defaultDevistaPriceCacheRegion" parent="priceCacheRegion"> <constructor-arg name="name" value="devistaPriceCacheRegion"/> <constructor-arg name="maxEntries" value="1000"/> <constructor-arg name="evictionPolicy" value="FIFO"/> <constructor-arg name="statsEnabled" value="true"/> <constructor-arg name="exclusiveComputation" value="false"/> <property name="handledTypes"> <array> <value></value> </array> </property> </bean> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="cacheRegionsList"/> <property name="targetMethod" value="add"/> <property name="singleton" value="true"/> <property name="arguments"> <ref bean="devistaPriceCacheRegion"/> </property> </bean> <alias alias="priceCacheRegion" name="priceDefaultCacheRegion"/> <bean abstract="true" class="com.devista.core.cache.CacheAccessImpl" id="priceDefaultCacheRegion"> <constructor-arg name="evictionPolicy" value="LRU"/> <constructor-arg name="statsEnabled" value="true"/> <constructor-arg name="exclusiveComputation" value="false"/> </bean> </beans>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <alias alias="devistaPriceCacheRegion" name="defaultDevistaPriceCacheRegion"/> <bean id="defaultDevistaPriceCacheRegion" parent="priceCacheRegion"> <constructor-arg name="name" value="devistaPriceCacheRegion"/> <constructor-arg name="maxEntries" value="1000"/> <constructor-arg name="evictionPolicy" value="FIFO"/> <constructor-arg name="statsEnabled" value="true"/> <constructor-arg name="exclusiveComputation" value="false"/> <property name="handledTypes"> <array> <value></value> </array> </property> </bean> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="cacheRegionsList"/> <property name="targetMethod" value="add"/> <property name="singleton" value="true"/> <property name="arguments"> <ref bean="devistaPriceCacheRegion"/> </property> </bean> <alias alias="priceCacheRegion" name="priceDefaultCacheRegion"/> <bean abstract="true" class="com.devista.core.cache.CacheAccessImpl" id="priceDefaultCacheRegion"> <constructor-arg name="evictionPolicy" value="LRU"/> <constructor-arg name="statsEnabled" value="true"/> <constructor-arg name="exclusiveComputation" value="false"/> </bean> </beans>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <alias alias="devistaPriceCacheRegion" name="defaultDevistaPriceCacheRegion"/> <bean id="defaultDevistaPriceCacheRegion" parent="priceCacheRegion"> <constructor-arg name="name" value="devistaPriceCacheRegion"/> <constructor-arg name="maxEntries" value="1000"/> <constructor-arg name="evictionPolicy" value="FIFO"/> <constructor-arg name="statsEnabled" value="true"/> <constructor-arg name="exclusiveComputation" value="false"/> <property name="handledTypes"> <array> <value></value> </array> </property> </bean> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="cacheRegionsList"/> <property name="targetMethod" value="add"/> <property name="singleton" value="true"/> <property name="arguments"> <ref bean="devistaPriceCacheRegion"/> </property> </bean> <alias alias="priceCacheRegion" name="priceDefaultCacheRegion"/> <bean abstract="true" class="com.devista.core.cache.CacheAccessImpl" id="priceDefaultCacheRegion"> <constructor-arg name="evictionPolicy" value="LRU"/> <constructor-arg name="statsEnabled" value="true"/> <constructor-arg name="exclusiveComputation" value="false"/> </bean> </beans>
بعد هذه الخطوة، يجب أن تكون قادراً على رؤية ملف منطقة التخزين المؤقت الذي تم إنشاؤه حديثاً في وحدة تحكم HAC.
*لا تنسَ إضافة ملف التهيئة الجديد الخاص بك إلى سياق التطبيق العام (global application context):
devistacore.global-context=devistacore-spring-cache.xml
أنشئ خدمة التخزين المؤقت الخاصة بك (cache service)
public class DevistaPriceCacheServiceImpl implements DevistaPriceCacheService { private CacheAccess cacheAccess; @Override public void add(String key, PriceInformation object) { try { cacheAccess.putIfAbsent(key, object); } catch (CacheAccessException e) { LOGGER.error("Unable to cache the price information", e); } } @Override public PriceInformation get(String key) { if (Objects.isNull(key)) { return null; } else { PriceInformation chachedInfo = (PriceInformation) cacheAccess.get(key); return chachedInfo; } } public void setCacheAccess(CacheAccess cacheAccess) { this.cacheAccess = cacheAccess; } }
public class DevistaPriceCacheServiceImpl implements DevistaPriceCacheService { private CacheAccess cacheAccess; @Override public void add(String key, PriceInformation object) { try { cacheAccess.putIfAbsent(key, object); } catch (CacheAccessException e) { LOGGER.error("Unable to cache the price information", e); } } @Override public PriceInformation get(String key) { if (Objects.isNull(key)) { return null; } else { PriceInformation chachedInfo = (PriceInformation) cacheAccess.get(key); return chachedInfo; } } public void setCacheAccess(CacheAccess cacheAccess) { this.cacheAccess = cacheAccess; } }
public class DevistaPriceCacheServiceImpl implements DevistaPriceCacheService { private CacheAccess cacheAccess; @Override public void add(String key, PriceInformation object) { try { cacheAccess.putIfAbsent(key, object); } catch (CacheAccessException e) { LOGGER.error("Unable to cache the price information", e); } } @Override public PriceInformation get(String key) { if (Objects.isNull(key)) { return null; } else { PriceInformation chachedInfo = (PriceInformation) cacheAccess.get(key); return chachedInfo; } } public void setCacheAccess(CacheAccess cacheAccess) { this.cacheAccess = cacheAccess; } }
استخدم الخدمة عند الحاجة
public PriceInformation getWebPriceForProduct(ProductModel product) { validateParameterNotNull(product, "Product model cannot be null"); PriceInformation priceInformation = devistaPriceCacheService.get(product.getCode()); if (Objects.isNull(priceInformation)) { // retrieve PriceInformation return null; // TODO replace with real retrieval result } else { return priceInformation; } }
public PriceInformation getWebPriceForProduct(ProductModel product) { validateParameterNotNull(product, "Product model cannot be null"); PriceInformation priceInformation = devistaPriceCacheService.get(product.getCode()); if (Objects.isNull(priceInformation)) { // retrieve PriceInformation return null; // TODO replace with real retrieval result } else { return priceInformation; } }
public PriceInformation getWebPriceForProduct(ProductModel product) { validateParameterNotNull(product, "Product model cannot be null"); PriceInformation priceInformation = devistaPriceCacheService.get(product.getCode()); if (Objects.isNull(priceInformation)) { // retrieve PriceInformation return null; // TODO replace with real retrieval result } else { return priceInformation; } }
هذا مثال موجز على استخدام Hybris Region Cache.

هل تحتاج إلى مساعدة في تحويل الأفكار إلى حلول قابلة للتطوير؟
سواء كنت تستكشف منتجًا جديدًا، أو تعمل على تحسين منصة حالية، أو تواجه تعقيدات تقنية، فنحن هنا لمساعدتك.

هل تحتاج إلى مساعدة في تحويل الأفكار إلى حلول قابلة للتطوير؟
سواء كنت تستكشف منتجًا جديدًا، أو تعمل على تحسين منصة حالية، أو تواجه تعقيدات تقنية، فنحن هنا لمساعدتك.