- @return
*/
public Long hash(String key);
/**
-
模拟意外情况断掉一个节点,用于测试缓存命中率
-
@param node
*/
public void removeNodeUnexpected(Node node);
}
普通hash的实现非常简单,代码如下
package cn.enjoy.hash;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;
public class NormalHashNodeServiceImpl implements HashNodeService{
/**
- 存储节点列表
*/
private List nodes = new ArrayList<>();
@Override
public void addNode(Node node) {
this.nodes.add(node);
}
@Override
public Node lookupNode(String key) {
long k = hash(key);
int index = (int) (k % nodes.size());
return nodes.get(index);
}
@Override
public Long hash(String key) {
CRC32 crc32 = new CRC32();
crc32.update(key.getBytes());
return crc32.getValue();
}
@Override
public void removeNodeUnexpected(Node node) {
nodes.remove(node);
}
}
好,有什么问题呢,测试一下
package cn.enjoy.hash;
import cn.enjoy.hash.HashNodeService;
import cn.enjoy.hash.Node;
import cn.enjoy.hash.NormalHashNodeServiceImpl;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
public class HashTest {
private HashNodeService nodeService;
@Before
public void init() {
nodeService= new NormalHashNodeServiceImpl();
Node addNode1 = new Node(“node1”, “192.168.0.11”);
Node addNode2 = new Node(“node2”, “192.168.0.12”);
Node addNode3 = new Node(“node3”, “192.168.0.13”);
Node addNode4 = new Node(“node4”, “192.168.0.14”);
Node addNode5 = new Node(“node5”, “192.168.0.15”);
Node addNode6 = new Node(“node6”, “192.168.0.16”);
Node addNode7 = new Node(“node7”, “192.168.0.17”);
Node addNode8 = new Node(“node8”, “192.168.0.18”);
nodeService.addNode(addNode1);
nodeService.addNode(addNode2);
nodeService.addNode(addNode3);
nodeService.addNode(addNode4);
nodeService.addNode(addNode5);
nodeService.addNode(addNode6);
nodeService.addNode(addNode7);
nodeService.addNode(addNode8);
}
@Test
public void test2() {
//用于检查数据分布情况
Map<String, Integer> countmap = new HashMap<>();
Node node = null;
for (int i = 1; i <= 100000; i++) {
String key = String.valueOf(i);
node = nodeService.lookupNode(key);
node.cacheString(key, “TEST_VALUE”);
String k = node.getIp();
Integer count = countmap.get(k);
if (count == null) {
count = 1;
countmap.put(k, count);
} else {
count++;
countmap.put(k, count);
}
}
System.out.println(“初始化数据分布情况:” + countmap);
// 正常情况下的去获取数据,命中率
int hitcount = 0;
for (int i = 1; i <= 100000; i++) {
String key = String.valueOf(i);
node = nodeService.lookupNode(key);
if (node != null) {
String value = node.getCacheValue(key);
if (value != null) {
hitcount++;
}
}
}
double h = Double.parseDouble(String.valueOf(hitcount))/ Double.parseDouble(String.valueOf(100000));
System.out.println(“初始化缓存命中率:”+ h);
Node addNode9 = new Node(“node9”, “192.168.0.19”);
nodeService.addNode(addNode9);
hitcount = 0;
for (int i = 1; i <= 100000; i++) {
String key = String.valueOf(i);
node = nodeService.lookupNode(key);
if (node != null) {
String value = node.getCacheValue(key);
if (value != null) {
hitcount++;
}
}
}
h = Double.parseDouble(String.valueOf(hitcount))/ Double.parseDouble(String.valueOf(100000));
System.out.println(“增加一个节点后缓存命中率:”+ h);
}
}
本来8个服务器(node),使用hash算法从集群里取值命中率100%,当新增了一个节点后可怜兮兮,命中率直接变成了10%左右,这个时候普通hash的问题显露无疑了,怎么办,试试一致性hash.
1.1.2. 一致性hash算法
一致性hash是为了解决普通hash的问题,使用一个环状结构,具体可以分层4步骤。
1.1.2.1. 步骤
1.1.2.1.1. 构建环形hash空间
说明:
一致性hash算法通过一个叫作一致性hash环的数据结构实现
这个环的起点是0,终点是2^32 - 1,并且起点与终点连接,环的中间的整数按逆时针分布
这个环的整数分布范围是[0, 2^32-1]
1.1.2.1.2. 把对象映射到hash空间
假设现在我们有4个对象,分别为o1,o2,o3,o4,
使用hash函数计算这4个对象的hash值(范围为0 ~ 2^32-1)
1.1.2.1.3. 把cache节点映射到hash空间
使用同样的hash函数,我们将机器也放置到hash环上。
假设我们有三台缓存机器,分别为 c1,c2,c3
使用hash函数计算这3台机器的hash值
1.1.2.1.4. 对象映射到cache节点
将对象和机器都放置到同一个hash环后
在hash环上顺时针查找距离这个对象的hash值最近的机器,即是这个对象所属的机器。
1.1.2.1.5. 增加一个节点的情况
增加机器c4的部署并将机器c4加入到hash环的机器c3与c2之间。
只有对象o4被重新分配到了c4
其他对象仍在原有机器上
1.1.2.1.6. 删除一个节点的情况
将机器c1下线(当然,也有可能是机器c1宕机)
只有对象o2被重新分配到机器c3
其他对象仍在原有机器上
1.1.2.1.7. 虚拟节点
仅仅使用真实的节点来形成环的结构可能会导致数据的倾斜,为了解决这种数据不对称的现象,可以在原理缓存的机器的基础上,增加相应的虚拟节点,这样数据就会均匀的打散到其他节点中。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析
资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
CoPEF.jpg" />
最后
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
[外链图片转存中…(img-V61tqcHi-1712339670145)]
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析
[外链图片转存中…(img-lSIstF8E-1712339670146)]
资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图