Синхронизирован ли EJB Singleton? - PullRequest
0 голосов
/ 04 февраля 2019

Я реализовал своего рода компонент кэширования для кэширования объектов данных как EJB Singleton.Интересно, это правильный путь в EJB:

@Singleton
public class MyCache {

    int DEFAULT_CACHE_SIZE = 30;
    int DEFAULT_EXPIRES_TIME = 60000;
    long expiresTime = 0;
    long lastReset = 0;
    Cache cache = null; 

    ....
    @PostConstruct
    void init() {
        resetCache();
    }

    public void resetCache() {
        cache = new Cache(DEFAULT_CACHE_SIZE);
        lastReset = System.currentTimeMillis();
    }

    public void put(String key, Object value) {
        cache.put(key, value);
    }

    public Object get(String key) {
        // test if cache is expired
        if (expiresTime > 0) {
            Long now = System.currentTimeMillis();
            if ((now - lastReset) > expiresTime) {
                logger.finest("...... Cache expired!");
                resetCache();
            }
        }
        return cache.get(key);
    }



    class Cache extends LinkedHashMap<String, Object> implements Serializable {
        private static final long serialVersionUID = 1L;
        private final int capacity;

        public Cache(int capacity) {
            super(capacity + 1, 1.1f, true);
            this.capacity = capacity;
        }

        protected boolean removeEldestEntry(Entry<String, Object> eldest) {
            return size() > capacity;
        }
    }
}

Мой вопрос: правильный ли это способ реализации механизма кэширования всего приложения?

У меня сложилось впечатление, что содержимое кэша неожиданно меняется.Может ли это случиться?Например, если EJB пассивирован?Я работаю на сервере Payara41.

Или я должен использовать:

cache = Collections.synchronizedMap(new Cache(DEFAULT_CACHE_SIZE));

вместо:

cache = new Cache(DEFAULT_CACHE_SIZE);

Ответы [ 3 ]

0 голосов
/ 06 февраля 2019

У решения с управляемой блокировкой контейнера есть один недостаток. Учтите, что если у вас есть операция put с блокировкой WRITE, это означает, что весь «кэш» заблокирован, поэтому невозможно получить или поместить параллельно, независимо от того, является ли ключ другим.Если ваша реализация кеша является параллельной картой, вы можете использовать ее без блокировки.

Если у вас больше требований к кешу, я бы использовал Infinispan, который обеспечивает лучшую производительность.Кеш здесь может быть локальным или распределенным в кластере.

0 голосов
/ 07 февраля 2019

Прочитав этот блог от Адама Бьена , я теперь улучшил свой код следующим образом:

  • Я добавил аннотацию ConcurrencyManagement
  • Я изменил свой LinkedHashMapв ConcurrentHashMap.

Пример:

@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN) // added concurrency management
public class MyCache {

    int DEFAULT_CACHE_SIZE = 30;
    int DEFAULT_EXPIRES_TIME = 60000;
    long expiresTime = 0;
    long lastReset = 0;
    Cache cache = null; 

    ....
    @PostConstruct
    void init() {
        resetCache();
    }

    public void resetCache() {
        cache = new Cache(DEFAULT_CACHE_SIZE);
        lastReset = System.currentTimeMillis();
    }

    public void put(String key, Object value) {
        cache.put(key, value);
    }

    public Object get(String key) {
        // test if cache is expired
        if (expiresTime > 0) {
            Long now = System.currentTimeMillis();
            if ((now - lastReset) > expiresTime) {
                logger.finest("...... Cache expired!");
                resetCache();
            }
        }
        return cache.get(key);
    }

    // changed from LinkedHashMap to ConcurrentHashMap
    class Cache extends ConcurrentHashMap<String, Object> implements Serializable {
        private static final long serialVersionUID = 1L;
        private final int capacity;

        public Cache(int capacity) {
            super(capacity + 1, 1.1f);
            this.capacity = capacity;
        }

        protected boolean removeEldestEntry(Entry<String, Object> eldest) {
            return size() > capacity;
        }
    }
}

Я думаю, что теперь это более правильная реализация.

0 голосов
/ 06 февраля 2019

Прежде всего, поскольку для вашего компонента не указано управление параллелизмом, оно падает до "контейнера" ​​по умолчанию.

Из спецификации EJB 3.1:

При проектировании сессионного компонента Singleton разработчик должен решить, будет ли компонент использовать управляемый контейнером или управляемый компонентом параллелизм.Обычно бинам Singleton назначается разграничение параллелизма, управляемого контейнером.Это значение по умолчанию, если тип управления параллелизмом не указан.

Затем для управления параллелизмом контейнеров требуются спецификации уровня блокировки типа блокировки.Как только они отсутствуют, применяется значение «Запись» по умолчанию:

По умолчанию, если аннотация атрибута блокировки параллелизма не указана для метода бина Singleton с разграничением управляемого параллелизма контейнера, значениеатрибута блокировки параллелизма для метода определен как Write.

Вышеуказанное означает, что доступ к вашим методам bean-компонентов должен быть синхронизирован, возможно, даже больше, чем вам действительно нужно.Вы можете установить тип блокировки «Чтение» для методов только для чтения (get), чтобы разрешить одновременный доступ для чтения.

...