Дизайн сервиса с высокой степенью параллелизма — кэш

задняя часть Архитектура JVM дизайн

Когда фреймворк находится в разработке

1. Стратегия утилизации кеша

1.1 Космический

Т.е. предоставляется буферное хранилище, например до 10Мб, при достижении памяти съемные данные согласно определенной политике.

1.2 в зависимости от емкости

На основе емкости означает, что установлен максимальный размер кеша.Когда кэшированная запись превышает максимальный размер, старые данные будут удалены в соответствии с определенной стратегией.

1.3 По времени

TTL (Время жить):Время жизни, то есть период времени с момента создания закэшированных данных в кэше до его истечения (он истечет вне зависимости от того, был ли доступ в этот период).

TTI (время простоя):Периоды простоя, то есть не то, как долго кешированные данные доступны из кеша, чтобы удалить время.

1.4 На основе справочника по объектам Java

Мягкие ссылки:Если объект является мягкой ссылкой, то при нехватке памяти кучи JVM сборщик мусора может вернуть эти объекты. Мягкие ссылки подходят для кэширования, так что, когда памяти кучи JVM недостаточно, эти объекты могут быть освобождены, чтобы освободить место для объектов с сильными ссылками, чтобы избежать OOM.

Слабая ссылка:Когда память освобождается сборщиком мусора, если обнаруживается слабая ссылка, она будет немедленно освобождена. Имеет более короткое время жизни, чем мягкие ссылки.

Уведомление:Объект со слабой ссылкой/мягкой ссылкой будет утилизирован сборщиком мусора только в том случае, если на него не ссылаются другие объекты с сильными ссылками. То есть, если есть объект (не слабая ссылка/мягкая ссылка), который ссылается на объект слабой ссылки/мягкой ссылки, то сборка мусора не будет восстанавливать объект ссылки.

1.5 Алгоритм восстановления

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

  • FIFO (First In First Out): Алгоритм «первым пришел — первым обслужен», т. е. те, которые первыми попадают в кеш, удаляются первыми.

  • LRU (наименее недавно использованный): Алгоритм наименее недавно использовавшийся, удаляются данные с наибольшим временем использования.

  • LFU (наименее часто используемый): наименее часто используемый алгоритм, удаляются данные с наименьшим количеством раз (частотой) за определенный период времени.

В практических приложениях существует множество кэшей на основе LRU, например, Guava Cache и EhCache поддерживают LRU.

2. Тип кеша Java

2.1 Кэш-память

Объекты хранятся с использованием памяти кучи Java. Может быть реализован с использованием Guava Cache, Ehcache 3.x, MapDB.

  • Преимущества: Преимущество использования кэша кучи в том, что нет сериализации/десериализации, и это самый быстрый кэш;

  • Недостатки: очевидно, что когда объем кэшированных данных велик, время паузы GC будет увеличиваться, а емкость хранилища ограничена размером кучи; обычно кэшированные объекты хранятся с помощью мягких ссылок/слабых ссылок, что То есть, когда памяти кучи недостаточно, вы можете принудительно освободить эту часть памяти, чтобы освободить место в памяти кучи. Кэши кучи обычно используются для хранения более теплых данных.

2.2 Кэш вне кучи

То есть кэшированные данные хранятся в памяти вне кучи. Его можно реализовать с помощью Ehcache 3.x, MapDB.

  • Преимущества: Время паузы GC может быть уменьшена (объект кучи переносятся на за пределами стека, сканирования и GC меньше движущихся объектов), может поддерживать больший кэш-пространство (ограничение размера памяти только на машине без воздействия пространства кучи влияния).

  • Недостаток: при чтении данных требуется сериализация/десериализация, что намного медленнее, чем кэш-память.

2.3 Кэш диска

То есть кэшированные данные хранятся на диске. Данные все еще там, когда JVM перезапускается. При перезапуске кэша кучи или кэша вне кучи данные будут потеряны, и их потребуется перезагрузить. Его можно реализовать с помощью Ehcache 3.x, MapDB.

2.4 Распределенный кеш

В случае нескольких экземпляров JVM есть две проблемы во внутрипроцессном кеше и дисковом кеше: 1. Проблема емкости одной машины; 2. Проблема согласованности данных (поскольку данные разрешено кэшировать, это означает, что допускается несогласованность в течение определенного периода времени, поэтому время истечения срока действия кэшированных данных может быть установлено для регулярного обновления данных); 3. При промахе кеша нужно отправлять больше запросов обратно в БД/сервис: каждый инстанс будет возвращаться в БД для загрузки данных в случае промаха кеша, поэтому общий объем доступа к БД увеличится после нескольких экземпляров. Решение может быть решено с использованием алгоритма сегментирования, такого как Consistent Hash. Следовательно, эти ситуации могут быть решены с помощью распределенного кэша. Вы можете использовать ehcache-clustered (с Terracotta server) для реализации распределенного кеша между Java-процессами. Конечно, распределенное кэширование можно реализовать и с помощью, например, Redis.

Эти два режима следующие:

  • На одной машине: храните самые горячие данные в кеше кучи, относительно горячих данных в кеше вне кучи и храните менее горячие данные в кэше диска.

  • При кластеризации: хранить самые горячие данные в кэше кучи, относительно горячие данные в кэше вне кучи и хранить полный объем данных в распределенном кэше.

3. Реализация кеша Java

3.1 Кэш-память

3.1.1 Реализация кэша Guava

Guava Cache предоставляет только кэш кучи, который является небольшим и гибким, и имеет лучшую производительность.Если используется только кеш кучи, его достаточно использовать.

Cache<String, String> myCache=        CacheBuilder.newBuilder()        .concurrencyLevel(4)        .expireAfterWrite(10, TimeUnit.SECONDS)        .maximumSize(10000)        .build();

Затем вы можете читать и записывать кеш через put и getIfPresent. CacheBuilder имеет несколько типов параметров: стратегия утилизации кеша, настройки параллелизма и т. д.

3.1.1.1 Политика высвобождения кэша/на основе емкости

maxSize: Установите емкость кеша.При превышении максимального размера кеш восстанавливается в соответствии с LRU.

3.1.1.2 Политика высвобождения кэша/на основе времени

  • expireAfterWrite: Установите TTL.Если кэшированные данные не будут записаны (созданы/перезаписаны) в течение заданного времени, они будут переработаны, то есть кэшированные данные будут регулярно перерабатываться.

  • expireAfterAccess: установите TTI, и кэшированные данные будут переработаны, если они не будут считаны/записаны в течение заданного времени. Каждый раз, когда к нему обращаются, его TTI обновляется, поэтому, если в кеше очень горячие данные, срок его действия никогда не истечет, что может привести к тому, что грязные данные будут существовать в течение длительного времени (поэтому рекомендуется установить expireAfterWrite).

3.1.1.3 Стратегия восстановления кэша/на основе ссылок на объекты Java

SEADKELS / SALLESVALUES: Установка слабых ссылок кэш. SoftValues: Установите реферальный кеш.

3.1.1.4 Стратегия рециркуляции кеша / активный аннулирование

invalidate (ключ объекта)/invalidateAll (Iterablekeys)/invalidateAll(): Активно аннулировать некоторые кэшированные данные.

Когда срабатывает инвалидация? Guava Cache не запускает операцию очистки сразу, когда кэшированные данные недействительны (если вы хотите сделать это, вам нужен дополнительный поток для их очистки), он будет активно очищать кеш один раз во время PUT Конечно, читатели также могут вызвать метод очистки для очистки, создав собственный поток в соответствии с реальным бизнесом.

3.1.1.5 Уровень параллелизма

concurrencyLevel: Guava Cache перезаписывает ConcurrentHashMap, а concurrencyLevel используется для установки количества сегментов.Чем выше уровень concurrencyLevel, тем сильнее возможности параллелизма.

3.1.1.6 Частота попаданий в статистику

RecordStats: начать запись статистики, такой как частота просмотров и т. д.

3.1.2 Реализация EhCache 3.x

CacheManager cacheManager = CacheManagerBuilder. newCacheManagerBuilder(). build(true);CacheConfigurationBuilder<String, String> cacheConfig= CacheConfigurationBuilder.newCacheConfigurationBuilder(       String.class,       String.class,       ResourcePoolsBuilder.newResourcePoolsBuilder()               .heap(100, EntryUnit.ENTRIES))       .withDispatcherConcurrency(4)       .withExpiry(Expirations.timeToLiveExpiration(Duration.of(10,TimeUnit.SECONDS)));Cache<String, String> myCache = cacheManager.createCache("myCache",cacheConfig);

Пожалуйста, вызовите метод CacheManager.close(), когда CacheManager закроет JVM. Кэш можно читать и записывать с помощью PUT и GET. CacheConfigurationBuilder также имеет несколько типов параметров: стратегия утилизации кеша, настройки параллелизма, статистика попаданий и т. д.

3.1.2.1 Политика высвобождения кэша/на основе емкости

heap(100, EntryUnit.ENTRIES): Установите количество записей в кеше. При превышении этого числа кэш восстанавливается в соответствии с LRU.

3.1.2.2 Политика высвобождения кэша/на основе пространства

куча(100, MemoryUnit.MB): Установите объем памяти кэша.При превышении этого объема кэш освобождается в соответствии с LRU. Кроме того, следует установить withSizeOfMaxObjectGraph(2): глубину обхода графа объектов и withSizeOfMaxObjectSize(1, MemoryUnit.KB): максимальный размер объекта, который можно кэшировать.

3.1.2.3 Политика высвобождения кэша/на основе времени

withExpiry(Expirations.timeToLiveExpiration(Duration.of(10,TimeUnit.SECONDS))): Установить TTL, без TTI.

withExpiry(Expirations.timeToIdleExpiration(Duration.of(10,TimeUnit.SECONDS))): Установите TTL и TTI одновременно, а значения TTL и TTI одинаковы.

3.1.2.4 Стратегия повторного использования кэша/активное аннулирование

remove(клавиша K)/removeAll(Set keys)/clear(): Активно аннулировать некоторые кэшированные данные.

Когда срабатывает инвалидация?EhCache использует тот же механизм, что и Guava Cache.

3.1.2.5 Уровень параллелизма

Пока нет API для его установки.EhCache внутренне использует ConcurrentHashMap в качестве хранилища кеша, а уровень параллелизма по умолчанию равен 16. withDispatcherConcurrency используется для установки уровня параллелизма для отправки событий.

3.1.3 Реализация MapDB 3.x

HTreeMap myCache =       DBMaker.heapDB().concurrencyScale(16).make().hashMap("myCache")       .expireMaxSize(10000)       .expireAfterCreate(10, TimeUnit.SECONDS)       .expireAfterUpdate(10,TimeUnit.SECONDS)       .expireAfterGet(10, TimeUnit.SECONDS)       .create();

Затем вы можете читать и записывать кэш через PUT и GET. Существует несколько типов параметров: стратегия утилизации кеша, настройки параллелизма, статистика попаданий и т. д.

3.1.3.1 Политика высвобождения кэша/на основе емкости

expireMaxSize: Установите емкость кэша.При превышении expireMaxSize кэш освобождается в соответствии с LRU.

3.1.3.2 Политика высвобождения кэша/на основе времени

  • expireAfterCreate/expireAfterUpdate: Установите TTL, если кэшированные данные не будут записаны (созданы/перезаписаны) в течение заданного времени, они будут переработаны. То есть кэшированные данные периодически восстанавливаются.

  • expireAfterGet: установить TTI, Кэшированные данные восстанавливаются, если они не читались/не записывались в течение заданного периода времени. Его TTI обновляется каждый раз, когда к нему обращаются, поэтому, если кеш содержит очень горячие данные, он никогда не истечет, что может привести к тому, что грязные данные будут существовать в течение длительного времени (поэтому рекомендуется установить expireAfterCreate/expireAfterUpdate).

3.1.3.3 Стратегия повторного использования кэша/активное аннулирование

  • remove(Object key) /clear(): Активно аннулировать некоторые кэшированные данные. Когда срабатывает инвалидация?MapDB по умолчанию использует механизм, аналогичный Guava Cache. Однако также поддерживается использование пула потоков для периодической аннулирования кеша путем настройки следующим образом.

  • expireExecutor(scheduledExecutorService)

  • expireExecutorPeriod(3000)

3.1.3.4 Уровень параллелизма

concurrencyScale: аналогично конфигурации кэша Guava.

Кэш кучи также можно создать с помощью DBMaker.memoryDB(), который сериализует и сохраняет данные в массиве byte[] размером 1 МБ, уменьшая влияние сборок мусора.

3.2 Кэш вне кучи

3.2.1 Реализация EhCache 3.x

CacheConfigurationBuilder<String, String> cacheConfig= CacheConfigurationBuilder.newCacheConfigurationBuilder(       String.class,       String.class,       ResourcePoolsBuilder.newResourcePoolsBuilder()               .offheap(100, MemoryUnit.MB))       .withDispatcherConcurrency(4)       .withExpiry(Expirations.timeToLiveExpiration(Duration.of(10,TimeUnit.SECONDS)))       .withSizeOfMaxObjectGraph(3)       .withSizeOfMaxObjectSize(1, MemoryUnit.KB);

Кэши вне кучи не поддерживают политики истечения срока действия кэша на основе емкости.

3.2.2 MAPDB 3.x достичь

HTreeMap myCache = DBMaker.memoryDirectDB().concurrencyScale(16).make().hashMap("myCache") .expireStoreSize(64 * 1024 * 1024) // Укажите размер кэша вне кучи 64MB .expireMaxSize(10000) .expireAfter (10, TimeUnit.SECONDS) .expireAfterUpdate(10, TimeUnit.SECONDS) .expireAfterGet(10, TimeUnit.SECONDS) .create();

При использовании кэша вне кучи не забудьте добавить параметры запуска JVM, например -XX:MaxDirectMemorySize=10G.

3.3 Кэш диска

3.3.1 Реализация Ehcache 3.x

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() // пул потоков по умолчанию .using (PooledExecutionServiceConfigurationBuilder.newPooledExecutionServiceConfigurationBuilder(). ) ("D:\\bak"))) .build(true);CacheConfigurationBuilder cacheConfig= CacheConfigurationBuilder.newCacheConfigurationBuilder( String.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .disk(100, MemoryUnit. МБ, правда)) //дисковый кеш .withSizeOfMaxObjectGraph(3) .withSizeOfMaxObjectSize(1, MemoryUnit.KB);

Когда JVM остановлена, не забудьте вызвать cacheManager.close(), чтобы убедиться, что данные памяти можно сбросить на диск.

3.3.2 Реализация MapDB 3.x

DB db = DBMaker .fileDB("D:\\bak\\a.data")//Где хранятся данные .fileMmapEnable() //включить mmap .fileMmapEnableIfSupported() //включить mmap на поддерживаемых платформах .fileMmapPreclearDisable() // Ускорение файлов mmap .cleanerHackEnable() // Обработка некоторых ошибок .transactionEnable() // Включение транзакций .closeOnJvmShutdown() .concurrencyScale(16) .make();HTreeMap myCache = db.hashMap("myCache") .expireMaxSize (10000) .expireAfterCreate(10, TimeUnit.SECONDS) .expireAfterUpdate(10, TimeUnit.SECONDS) .expireAfterGet(10, TimeUnit.SECONDS) .createOrOpen();

Поскольку транзакция включена, MapDB включает WAL. Кроме того, не забудьте вызвать метод db.commit, чтобы зафиксировать транзакцию после работы с кешем.

myCache.put("key" + counterWriter,"value" + counterWriter);db.commit();

3.4 Распределенный кеш

3.4.1 Ehcache 3.1 + Terracotta Server

Не рекомендуется для использования.

3.4.2 Redis

Производительность очень хорошая, в режиме master-slave и в режиме кластера.

3.5 Многоуровневый кэш

Если сначала выполняется поиск в кэше кучи, а не в дисковом кэше, MapDB можно реализовать с помощью следующей конфигурации.

HTreeMap diskCache = db.hashMap («myCache»). TimeUnit.SECONDS) .createOrOpen();HTreeMap heapCache = db.hashMap("myCache") .expireMaxSize(100) .expireAfterCreate(10, TimeUnit.SECONDS) .expireAfterUpdate(10, TimeUnit.SECONDS) .expireAfterGet(10, TimeUnit. SECONDS) .expireOverflow(diskCache) //Сохранение на диск при переполнении кеша .createOrOpen();

4. Шаблоны использования кеша

В основном делится на две категории: Cache-Aside и Cache-As-SoR (сквозное чтение, сквозная запись, отложенная запись).

  • SoR (система записи): система записи или источник данных — это система, которая фактически хранит исходные данные.

  • Кэш: Кэш-данные моментального снимка SoR. Скорость доступа к кешу выше, чем у SoR. Цель помещения его в кеш-улучшить скорость доступа и уменьшить количество раз возврата к источнику к СОР.

  • Back-to-source: то есть вернуться к источнику данных для получения данных.Когда кеш не попадает, ему нужно прочитать данные из SoR, что называется back-to-source.

4.1 Cache-Aside

Cache-Aside означает, что бизнес-код написан вокруг кеша, а бизнес-код непосредственно поддерживает кеш.Пример кода выглядит следующим образом.

4.1.1 Сценарий чтения

Сначала получаем данные из кеша, если нет попадания, возвращаемся к источнику в SoR и помещаем исходные данные в кеш для следующего чтения.

// 1, получите значение данных = mycache.getifpresent (ключ); если (значение == NULL) {//2.1, если кэш не ударяется, вернитесь к SOR получить исходное значение данных = LoadFromsor (ключ); // 2.2, поместите данные в кэш, в следующий раз вы получите данные mycache.put (ключ, значение) из кэша;}

4.1.2 Запись сценария

Сначала запишите данные в SoR, а затем синхронно запишите данные в кэш сразу после успешной записи.

//1.Сначала записываем данные в SoRwriteToSoR(key, value);//2.Сразу после успешного выполнения записываем данные в кеш синхронно myCache.put(key, value);

Или сначала запишите данные в SoR, истечет срок действия кэшированных данных после успешной записи и загрузите кэш при следующем чтении.

//1.Сначала записываем данные в SoRwriteToSoR(key, value);//2.Инвалидируем кеш, а затем загружаем кеш в следующий раз при чтении myCache.invalidate(key);

Cache-Aside подходит для использования режима АОП для достижения

4.2 Cache-As-SoR

Cache-As-Sorce Смачатся Cache As Sor, все операции выполняются на кэше, а затем кэш делегирован для SOR для реального чтения / записи. То есть в бизнес-коде рассматривается только операция кэша, и код, связанный с соком, не видим. Есть три реализации: прочитанные, запись, запись сзади.

4.2.1 Read-Through

При сквозном чтении бизнес-код сначала вызывает Cache, и если Cache пропускает источник, Cache возвращается к SoR вместо бизнес-кода (то есть Cache считывает SoR). Используя режим Read-Through, вам необходимо настроить компонент CacheLoader для загрузки исходных данных обратно в источник в SoR. Гуава И Cache, и Ehcache 3.x поддерживают этот режим.

4.2.1.1 Реализация кэша Guava

LoadingCache<Integer,Result<Category>> getCache =       CacheBuilder.newBuilder()       .softValues()       .maximumSize(5000).expireAfterWrite(2, TimeUnit.MINUTES)        .build(new CacheLoader<Integer,Result<Category>>() {           @Override           public Result<Category> load(final Integer sortId) throwsException {                return categoryService.get(sortId);           }       });

При построении кеша передается CacheLoader для загрузки кеша, процесс работы следующий:

  • Бизнес-код приложения напрямую вызывает getCache.get(sortId).

  • Сначала запросите кеш, если он есть в кеше, верните кешированные данные напрямую.

  • Если кэш не попадает, он будет делегирован CacheLoader, CacheLoader вернется к SoR для запроса исходных данных (возвращаемое значение не должно быть нулевым, его можно обернуть как нулевой объект), а затем записать в кеш.

Есть несколько преимуществ использования CacheLoader:

  • Бизнес-код приложения более лаконичен, а код запроса кэша и код SoR не нужно переплетать, как в режиме Cache-Aside. Если логика использования кеша разбросана по нескольким местам, это простой способ исключить дублирование кода.

  • Чтобы устранить эффект собачьей кучи, то есть когда определенный кеш выходит из строя, существует большое количество одних и тех же запросов, которые не попадают в кеш, так что запросы к серверной части выполняются одновременно, что приводит к слишком большому давлению на бэкэнд.

4.2.1.2 Реализация Ehcache 3.x

CacheManager cacheManager = CacheManagerBuilder. newCacheManagerBuilder(). build(true);org.ehcache.Cache<String, String> myCache =cacheManager. createCache ("myCache",       CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,String.class,               ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100,MemoryUnit.MB))               .withDispatcherConcurrency(4)               .withExpiry(Expirations.timeToLiveExpiration(Duration.of(10,TimeUnit.SECONDS)))                .withLoaderWriter(newDefaultCacheLoaderWriter<String, String> () {                   @Override                   public String load(String key) throws Exception {                        return readDB(key);                   }                    @Override                   public Map<String, String> loadAll(Iterable<? extendsString> keys) throws BulkCacheLoadingException, Exception {                        return null;                   }               }));

Ehcache 3.1 сам по себе не устраняет эффект кучи собак.

4.2.2 Write-Through

Сквозная запись называется режимом проникающей записи/режимом сквозной записи.Бизнес-код сначала вызывает кэш для записи (добавления/изменения) данных, а затем кэш отвечает за запись кэша и запись SoR, а не бизнес-кода.

Использование режима Write-Through требует настройки компонента CacheWriter для обратной записи SoR. Guava Cache не поддерживает. Ehcache 3.x поддерживает этот режим.

Ehcache нужно настроить CacheLoaderWriter, CacheLoaderWriter умеет писать SoR. Когда кэшу необходимо записать (добавить/изменить) данные, он сначала вызывает CacheLoaderWriter для синхронизации (немедленно) с SoR, а кэш обновляется после успешного выполнения.

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);Cache<String, String> myCache =cacheManager.createCache ("myCache",       CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,String.class,               ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100,MemoryUnit.MB))               .withDispatcherConcurrency(4)               .withExpiry(Expirations.timeToLiveExpiration(Duration.of(10,TimeUnit.SECONDS)))               .withLoaderWriter(newDefaultCacheLoaderWriter<String, String> () {                   @Override                   public void write(String key, String value) throws Exception{                        //write                   }                   @Override                   public void writeAll(Iterable<? extends Map.Entry<? extendsString, ? extends String>> entries) throws BulkCacheWritingException,Exception {                        for(Object entry: entries) {                            //batch write                        }                   }                   @Override                    public void delete(Stringkey) throws Exception {                        //delete                   }                   @Override                   public void deleteAll(Iterable<? extends String>keys) throws BulkCacheWritingException, Exception {                        for(Object key :keys) {                            //batch delete                        }                   }               }).build());

Ehcache 3.x по-прежнему использует CacheLoaderWriter для реализации через запись (ключ String, String значение), writeAll(Iterable> записи) и delete(String key), deleteAll (итерируемые ключи) для поддержки одиночной записи, пакетной записи и одиночного удаления, операций пакетного удаления соответственно.

Поток операций выглядит следующим образом: Когда мы вызываем myCache.put("e", "123") или myCache.putAll(map), записываем кеш. Сначала Cache сразу делегирует операции записи CacheLoaderWriter #write и #writeAll, а затем CacheLoaderWriter отвечает за немедленную запись SoR. После успешной записи SoR запишите в кэш.

4.2.3 Write-Behind

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

4.2.3.1 Асинхронная запись

Опущено, может быть реализовано с помощью EhCache

4.2.3.2 Пакетная запись

Опущено, может быть реализовано с помощью EhCache

4.2.4 Copy Pattern

Есть два шаблона копирования, Копирование при чтении и копирование при записи. Кэши кучи в Guava-Cache и EhCache основаны на ссылках, поэтому, если кто-то получит кэшированные данные и изменит их, могут возникнуть непредсказуемые проблемы. Гуава Кэш не поддерживает, EhCache 3.x поддерживает.

public interface Copier { T copyForRead(T obj); //Копирование при чтении, например myCache.get() T copyForWrite(T obj); //Копирование при записи, например myCache.put( )}

Источник ссылки:[1] Базовая технология архитектуры веб-сайта с миллиардным трафиком, Чжан Кайтао.

Источник: http://blog.csdn.net/foreverling/article/details/78012205.

Уведомление об авторских правах: сеть источника контента, первоначальные правообладатели. Если это не может быть подтверждено, мы укажем источник и автора, если нарушение, пожалуйста, сообщите, мы немедленно удалим его и приносим свои извинения. благодаря.

-END-

Архитектурный дайджест

ID: Архдайджест

Архитектура интернет-приложений丨Архитектурные технологии丨Большие веб-сайты丨Большие данные丨Машинное обучение

Чтобы увидеть больше интересных статей, нажмите ниже: читать оригинал