架构师训练营 Week5 学习总结

用户头像
Up
关注
发布于: 2020 年 07 月 08 日

第一节课:分布式缓存



缓存:存储在计算机上的一个原始数据复制集,以便于访问。通常是key-value格式的,主要用来提高系统的响应性能的。

缓冲可以理解为一种提高读写的速度的通道,数据存储在通道中,提高存取速度。

种类:

  1. 硬件:CPU缓存、操作系统缓存、数据库缓存、JVM缓存

  2. 软件:CDN缓存、代理与反向代理缓存、前端缓存、应用程序缓存、分布式对象缓存

常见的缓存算法(hash表):

将键进行哈希处理,得到值在数组中的索引,后续可以利用这个索引进行访问,时间复杂度是O(1)。

缓存的关键指标:

  1. 缓存命中率:缓存是否有效依赖于能多少次重用同一个缓存响应业务请求,这个度量之变被称作缓存命中。假设十次缓存响应中,有9次成功响应,那么缓存命中率就是90%。

  2. 缓存键集合大小: 键的数量多少。键数量越少,缓存的命中效率越高

  3. 缓存可使用内存空间:缓存可使用的物理内存空间,缓存对象越多,缓存命中率越高。

  4. 缓存对象生存时间:缓存对象的有效时间,其越长,缓存对象被重用的可能性就越高。

常见缓存种类:

  • 代理缓存

  • 反向代理缓存

  • 多层反向代理缓存

  • 内容分发网络

  • CDN同时配置静态文件和动态内容

  • 通读缓存(给客户端返回缓存资源,并在请求命中缓存时获取实际数据,代理缓存、反向代理缓存、CDN缓存)



  • 旁路缓存

  • 浏览器对象缓存本地对象缓存

  • 本地对象缓存构建分布式集群

  • 远程分布式对象缓存

案例:Memcached分布式对象缓存

Java访问其代码:

import com.danga.MemCached.MemCachedClient;
//通用分布式缓存访问
public class CommonCache<T> implements Cache<T> {
private static MemCachedClient memCachedClient = null;
private String base = null;
CommonCache(Class<T> t, MemCachedClient client) {
memCachedClient = client;
base = t.getSimpleName() + "-";
}
public T get(String key) {
return (T) memCachedClient.get(base + key);
}
public boolean set(String key, T value) {
return memCachedClient.set(base + key, value);
}
@Override
public boolean update(String key, T value) {
return memCachedClient.replace(base + key, value);
}
@Override
public boolean delete(String key) {
return memCachedClient.delete(base + key);
}
@Override
public boolean add(String key, T value) {
return memCachedClient.add(base + key, value);
}
}
//分布式集群访问
import com.danga.MemCached.MemCachedClient;
import com.schooner.MemCached.SchoonerSockIOPool;
import com.yx.cache.util.HashCodeUtil;
import com.yx.task.ThreadPoolManager;
public class ClusterCache<T> implements Cache<T> {
private static MemCachedClient memCachedClient = null;
private static ThreadPoolManager taskManager = ThreadPoolManager
.getInstance("cache");
private String base = null;
private SchoonerSockIOPool pool = SchoonerSockIOPool.getInstance();
ClusterCache(Class<T> t, MemCachedClient client) {
memCachedClient = client;
base = "i-" + t.getSimpleName() + "-";
}
@Override
public T get(String key) {
T value = null;
if (key == null) {
return null;
}
key = base + key;
if (pool.getServers().length < 2) {
value = (T) memCachedClient.get(key);
} else {
int hashCode = HashCodeUtil.getHash(key);
value = (T) memCachedClient.get(key, hashCode);
if (value == null) {
hashCode = this.getRehashCode(key, hashCode);
value = (T) memCachedClient.get(key, hashCode);
if (value != null) {// 如果在另外一台服务器上取到了缓存,则恢复第一台服务器
UpdateTask task = new UpdateTask(key, value);
taskManager.submit(task);
}
}
}
return value;
}
@Override
public boolean set(String key, T value) {
if (key == null) {
return false;
}
key = base + key;
boolean result = false;
if (pool.getServers().length < 2) {
result = memCachedClient.set(key, value);
} else {
int hashCode = HashCodeUtil.getHash(key);
result = memCachedClient.set(key, value, hashCode);
// if (result) {
hashCode = getRehashCode(key, hashCode);
memCachedClient.set(key, value, hashCode);
// }
}
return result;
}
private int getRehashCode(String key, int oldHashcode) {
String host = pool.getHost(key, oldHashcode);
int rehashTries = 0;
// if (result) {
int hashCode = HashCodeUtil.getHash(rehashTries + key);
while (host.equals(pool.getHost(key, hashCode))) {
rehashTries++;
hashCode = HashCodeUtil.getHash(rehashTries + key);
}
return hashCode;
}
@Override
public boolean update(String key, T value) {
if (key == null) {
return false;
}
key = base + key;
boolean result = false;
if (pool.getServers().length < 2) {
result = memCachedClient.replace(key, value);
} else {
int hashCode = HashCodeUtil.getHash(key);
result = memCachedClient.replace(key, value, hashCode);
// if (result) {
hashCode = getRehashCode(key, hashCode);
memCachedClient.replace(key, value, hashCode);
// }
}
return result;
}
@Override
public boolean delete(String key) {
if (key == null) {
return false;
}
key = base + key;
boolean result = false;
if (pool.getServers().length < 2) {
result = memCachedClient.delete(key);
} else {
int hashCode = HashCodeUtil.getHash(key);
result = memCachedClient.delete(key, hashCode, null);
// if (result) {
hashCode = this.getRehashCode(key, hashCode);
memCachedClient.delete(key, hashCode, null);
// }
}
return result;
}
@Override
public boolean add(String key, T value) {
if (key == null) {
return false;
}
key = base + key;
boolean result = false;
if (pool.getServers().length < 2) {
result = memCachedClient.add(key, value);
} else {
int hashCode = HashCodeUtil.getHash(key);
result = memCachedClient.add(key, value, hashCode);
// if (result) {
hashCode = getRehashCode(key, hashCode);
memCachedClient.add(key, value, hashCode);
// }
}
return result;
}
static class UpdateTask implements Runnable {
private String key;
private Object value;
UpdateTask(String key, Object value) {
this.key = key;
this.value = value;
}
@Override
public void run() {
memCachedClient.set(key, value, HashCodeUtil.getHash(key));
}
}
}

MemCache分布式缓存访问模型

这里最难理解的是其路由算法,牵涉到分布式对象的一致性哈希算法

基本思路:将集群节点IP放入一个环中,环大小为2^32,再将键值对根据路由算法放入离最近节点的位置。

一致性hash节点扩容

基于虚拟节点的一致性hash算法

技术栈各个层面的缓存:

缓存能提升性能的原因:

  1. 缓存数据通常直接去内存读取

  2. 缓存存储的数据不需要中间计算,减少CPU资源的消耗

  3. 缓存降低数据库、磁盘、网络的负载压力,使这些IO设备获得更好的响应特性。



缓存不是性能优化的银弹,使用缓存应该注意以下几点:

  1. 不要频繁修改数据

  2. 设置热点访问(LRU算法)

  3. 注意数据不一致性和脏读(计算机科学中最困难的三件事:缓存失效,命名事物、计数错误)

  4. 防止缓存雪崩

  5. 防止缓存预热

  6. 防止缓存穿透



Redis的优点:

  1. 支持复杂的数据结构

  2. 支持多路复用异步I/O高性能

  3. 支持主从复制高可用

  4. 原生集群和share nothin集群模式



第二节课、消息队列和异步架构



同步调用

异步调用(多个耗时操作同步进行)



有回调的异步调用

多次异步调用不阻塞应用线程

由异步演变出消息队列为了解耦

架构元素:消息生产者、消息队列、消息消费者

分类:

  1. 点对点模型

  1. 发布订阅模型



优点:实现异步处理,提升处理性能;更好的伸缩性;削峰填谷;失败隔离与自我修复



由消息队列引出了一种架构方案:事件驱动架构EDA







负载均衡架构

种类:

  • HTTP重定向负载均衡

  • DNS负载均衡

  • 反向代理负载均衡

  • IP负载均衡

  • 数据链路层负载均衡



关于负载均衡的几类算法

  • 轮询:所有请求被依次分发到每个应用服务器上,适合于所有的服务器硬件都相同的场景

  • 加权轮询:根据应用服务器硬件性能的情况,在轮询的基础上,按照配置的权重将请求分发到每个服务器,高性能的服务器分配更多请求。

  • 随机:请求被随机分配到各个应用服务器。

  • 最少连接:记录每个应用服务器正在处理的连接数,将新的到的请求分发到最少连接的服务器上。这是最符合负载均衡定义的算法

  • 源地址散列:根据请求来源的IP地址进行Hash计算,得到应用服务器,该算法可以保证同一个来源的请求总在一个服务器上处理,实现了会话粘滞。

分布式集群的Session管理

  • Session复制

  • Session绑定



  • 利用cookie记录Session



  • Session服务器



参考链接:基于memcached for java 实现分布式缓存


用户头像

Up

关注

代码,思考,架构,阅读,旅行。 2018.11.02 加入

一起来进步吧,持续学习的小白!

评论

发布
暂无评论
架构师训练营Week5学习总结