Обзор
Пять элементов проектирования архитектуры Интернета: высокая производительность, высокая доступность, масштабируемость, масштабируемость и безопасность.
Как добиться высокой производительности и высокой доступности, кэширование очень помогает. Мы знаем, что в большинстве случаев чтение данных и запись данных соответствуют закону 28. А среди прочитанных данных 20% — это данные, которые читаются часто (горячие данные). Тогда наш метод решения этих 20% данных может обеспечить очень хорошую производительность.
Говоря довольно шутливой фразой, главное:
В проектировании интернет-архитектуры нет ничего, что нельзя было бы решить с помощью одного уровня абстракции (прокси), а если и есть, то двух.
Камень других гор
- Три оси архитектуры Интернета:Особенно Aliyun.com/articles/54…
- Последовательный алгоритм хеширования:краткое описание.com/afraid/oh8convenient89baby3…
- Как спроектировать и внедрить кэш LRU:www.importnew.com/18758.html - [Google Гуава] 3 - Кэш:если eve.com/Google-Melon V…
- Коллекция Java - анализ принципа ConcurrentHashMap:блог woo woo woo.cn на.com/ITTangtang/…
Основной принцип -ehcache:Томми - Road.ITeye.com/blog/223710…
-Spring+Экземпляр кэша EhCache:Woohoo. Видите лазейки. Способность /digest/Java…
Классификация кэша
Как вы можете видеть из многих проектов архитектуры Интернета, с того момента, как пользователь вводит URL-адрес в браузере, он слишком много кэшируется. Приведу примерный список:
- После ввода URL запросите кеш браузера
- запрос DNS-кеша браузера
- Запрос DNS-кеша операционной системы
- Запросить DNS-сервер, запросить кеш DNS-сервера
- Получить ip, статические ресурсы уходят в cdn cache. Динамические данные идут на сервер
- Если кэширование страниц настроено, перейдите к кэшированию страниц
- Если настроен локальный кеш, перейти к локальному кешу
- Если настроен распределенный кеш (например, Redis и т. д.), перейдите к распределенному кешу.
- операции с базой данных, кэширование базы данных
Здесь я хочу в основном поговорить о выборе 2 видов локального кеша, зачем вводить локальный кеш, что можно объяснить предложением в «Трех осях архитектуры Интернета»:
Точки доступа к данным, такие как Detail, имеют очень высокую степень доступа к некоторым элементам горячих точек. Даже сам кеш имеет проблему с узким местом. Как только объем запросов достигнет предела одной машины, возникнет проблема защиты горячих точек. Иногда кажется, что это легко решить, например, достаточно текущего лимита, но вы думаете, что как только точка доступа сработает на пороге текущего лимита машины, данные в кеше этой машины станут недействительными, что косвенно вызовет кеш Он разбит, и база данных прикладного уровня посадки запроса имеет лавинное явление. Такого рода проблемы необходимо комбинировать с конкретными продуктами Cache, чтобы получить лучшее решение.Вот общее решение, которое заключается в создании локального кэша на стороне клиента Cache.Когда горячие данные найдены, они напрямую кэшируются в клиент вместо того, чтобы запрашивать его Кэш-сервер.
Другая причина в том, что при выполнении seckill я могу установить счетчик оставшегося количества товаров со временем истечения в каждом сервере приложений, чтобы перехватывать недействительные запросы как можно дальше перед цепочкой вызовов.
Распределенный кеш
Как развернуть распределенный кеш, я не буду здесь вдаваться в подробности. Перечислите наши методы развертывания:
Этот способ не очень хорош для расширения машины. Есть лучший способ: согласованный алгоритм хеширования. Вы можете обратиться к этой статье: «Последовательный алгоритм хеширования» в «Камнях других гор».
Localcache
В java localcache по сути представляет собой карту, соответствующую каждой паре ключ-значение в карте, можно установить время истечения срока действия или можно использовать стратегию устранения, такую как LRU (наименее недавно использованная).
Принцип реализации алгоритма LRU см. в разделе «Как спроектировать и реализовать кэш LRU» в Stone of the Mountain. По сути, это хэш-карта + двусвязный список.Каждая операция доступа помещает узел во главе связанного списка, а хвост, естественно, является самым старым узлом.
Я сравнил два вида LocalCache,google的Guava库中的cache模块а такжеEhcache.
Guava может ссылаться на статью: "[Google Guava] 3-Cache" в камне других гор.Основной принцип: ConcurrentMap (использование блокировки сегмента для уменьшения детализации блокировки) + алгоритм LRU.
Принцип блокировки сегмента ConcurrentMap см. в статье «Коллекция Java — анализ принципов ConcurrentHashMap».
Разделяй и властвуй мышление.
Для информации, связанной с Ehcache, вы можете обратиться к «Основным принципам Ehcache» и «Примеру кэша Spring + EhCache». Кэш - это тяжелый локальный кеш. Есть более интересный момент: поддержка дискового кеша, так что маме больше не придется беспокоиться о нехватке памяти>->.
Мое требование: я могу выполнить некоторую настройку, не беспокоясь о нехватке памяти, или мне не нужен сильный согласованный кэш данных. Возможна даже псевдогорячая перезагрузка.
Поэтому я решил использовать Ehcache. Если делать просто кеширование памяти, то рекомендуется использовать гуаву, там много интересного, например инвалидация кеша, автоматическая загрузка данных из источников данных и т.д.
Вспомогательный класс Ehcache
В соответствии со своими потребностями он инкапсулирует класс инструментов localcache, который занимает только диск и не имеет ограничений по емкости.Это только для справки.Учитывая, что его можно разместить в нескольких местах, файла конфигурации нет.
package com.fenqile.creditcard.appgatewaysale.provider.util;
import com.alibaba.fastjson.JSONObject;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.MessageDigest;
/**
* User: Rudy Tan
* Date: 2018/3/28
*
* 本地缓存工具,基于ehcache,磁盘存储
*
* 可以运用于配置文件、接口数据等等,
*
*/
public class LocalCacheUtil {
private static final CacheManager cacheManager = CacheManager.create();
private static Logger LOG = LoggerFactory.getLogger(LocalCacheUtil.class);
private static String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(str.getBytes("utf-8"));
final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
StringBuilder ret = new StringBuilder(bytes.length * 2);
for (int i=0; i<bytes.length; i++) {
ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
}
return ret.toString();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Cache getCacheInstance(){
String cacheKey = "local_cache_"+ md5(Thread.currentThread().getStackTrace()[1].getClassName());
if (!cacheManager.cacheExists(cacheKey)){
synchronized (cacheManager){
if (!cacheManager.cacheExists(cacheKey)){
CacheConfiguration cacheConfiguration = new CacheConfiguration();
cacheConfiguration.setTimeToIdleSeconds(60);
cacheConfiguration.setTimeToLiveSeconds(60);
cacheConfiguration.setName(cacheKey);
cacheConfiguration.setMaxEntriesLocalHeap(1);
cacheConfiguration.setMaxEntriesLocalDisk(100000);
cacheConfiguration.setEternal(false);
cacheConfiguration.setOverflowToDisk(true);
cacheConfiguration.setMaxElementsInMemory(1);
cacheConfiguration.setCopyOnRead(true);
cacheConfiguration.setCopyOnWrite(true);
Cache cache = new Cache(cacheConfiguration);
cacheManager.addCache(cache);
}
}
}
return cacheManager.getCache(cacheKey);
}
private static Element serialization(String key, Object value, Integer expireTime){
if (StringUtils.isEmpty(key)
|| null == expireTime
|| 0 == expireTime){
return null;
}
String clazz = "";
String content = "";
if (null == value){
clazz = "null";
content = clazz + "_class&data_null";
}else {
clazz = value.getClass().getName();
content = clazz + "_class&data_"+ JSONObject.toJSONString(value);
}
return new Element(key, content, expireTime, expireTime);
}
private static Object unSerialization(Element element){
if (null == element){ return null; }
String content = (String) element.getObjectValue();
String[] data = content.split("_class&data_");
Object response = null;
try {
if ("null".equalsIgnoreCase(data[0])){
return null;
}
response = JSONObject.parseObject(data[1], Class.forName(data[0]));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return response;
}
/**
* 设置本地缓存
*/
public static boolean setCache(String key, Object value, Integer expireTime){
Cache cache = getCacheInstance();
if (null == cache){
LOG.info("setCache:cache is null, {}, {}, {}", key, value, expireTime);
return false;
}
if (StringUtils.isEmpty(key)
|| null == expireTime
|| 0 == expireTime){
LOG.info("setCache:params is not ok, {}, {}, {}", key, value, expireTime);
return false;
}
synchronized (cache){
cache.put(serialization(key, value, expireTime));
cache.flush();
}
return true;
}
/**
* 获取本地缓存
*/
public static Object getCache(String key){
Cache cache = getCacheInstance();
if (null == cache
|| StringUtils.isEmpty(key)){
LOG.info("getCache:params is not ok, {}", key);
return null;
}
Element element = cache.get(key);
return unSerialization(element);
}
/**
* 清理本地缓存
*/
public static boolean delCache(String key){
Cache cache = getCacheInstance();
if (null == cache){
LOG.info("delCache:cache is null, {}", key);
return true;
}
if (StringUtils.isEmpty(key)){
LOG.info("delCache:params is not ok, {}", key);
return true;
}
synchronized (cache){
cache.put(serialization(key, null, 0));
cache.flush();
}
return true;
}
}
good luck.