Elastic-5分布式文档存储

分片位置的确定
1
shard = hash(routing) % number_of_primary_shards

routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数

这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。

阅读全文

Elastic 版本控制

悲观并发控制

这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。

乐观并发控制

Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。 例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。

阅读全文

Elastic-1分布式简介

Elastic集群分布式特性

  • 分配文档到不同的容器 或 分片 中,文档可以储存在一个或多个节点中
  • 按集群节点来均衡分配这些分片,从而对索引和搜索过程进行负载均衡
  • 复制每个分片以支持数据冗余,从而防止硬件故障导致的数据丢失
  • 将集群中任一节点的请求路由到存有相关数据的节点
  • 集群扩容时无缝整合新节点,重新分配分片以便从离群节点恢复

阅读全文

Elastic-3数据的输入和输出

无论我们写什么样的程序,目的都是一样的:以某种方式组织数据服务我们的目的。 但是数据不仅仅由随机位和字节组成。我们建立数据元素之间的关系以便于表示实体,或者现实世界中存在的 事物 。 如果我们知道一个名字和电子邮件地址属于同一个人,那么它们将会更有意义。

阅读全文

Elastic-1集群内的原理

集群基础

一个运行中的 Elasticsearch 实例称为一个 节点,而集群是由一个或者多个拥有相同 cluster.name 配置的节点组成, 它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。

阅读全文

网络传输基础二

接入网络需要设置的必选项
  • 本机的IP地址
  • 子网掩码
  • 网关的IP地址
  • DNS的IP地址

如果是使用动态IP地址上网,就是使用了DHCP协议

这个协议规定,每一个子网络中,有一台计算机负责管理本网络的所有IP地址,它叫做”DHCP服务器”。新的计算机加入网络,必须向”DHCP服务器”发送一个”DHCP请求”数据包,申请IP地址和相关的网络参数。

DHCP协议

是建立在UDP协议基础上的。

DHCP数据包结构

(1)最前面的”以太网标头”,设置发出方(本机)的MAC地址和接收方(DHCP服务器)的MAC地址。前者就是本机网卡的MAC地址,后者这时不知道,就填入一个广播地址:FF-FF-FF-FF-FF-FF。

(2)后面的”IP标头”,设置发出方的IP地址和接收方的IP地址。这时,对于这两者,本机都不知道。于是,发出方的IP地址就设为0.0.0.0,接收方的IP地址设为255.255.255.255。

(3)最后的”UDP标头”,设置发出方的端口和接收方的端口。这一部分是DHCP协议规定好的,发出方是68端口,接收方是67端口。

这个数据包构造完成后,就可以发出了。以太网是广播发送,同一个子网络的每台计算机都收到了这个包。因为接收方的MAC地址是FF-FF-FF-FF-FF-FF,看不出是发给谁的,所以每台收到这个包的计算机,还必须分析这个包的IP地址,才能确定是不是发给自己的。当看到发出方IP地址是0.0.0.0,接收方是255.255.255.255,于是DHCP服务器知道”这个包是发给我的”,而其他计算机就可以丢弃这个包。

接下来,DHCP服务器读出这个包的数据内容,分配好IP地址,发送回去一个”DHCP响应”数据包。这个响应包的结构也是类似的,以太网标头的MAC地址是双方的网卡地址,IP标头的IP地址是DHCP服务器的IP地址(发出方)和255.255.255.255(接收方),UDP标头的端口是67(发出方)和68(接收方),分配给请求端的IP地址和本网络的具体参数则包含在Data部分。

新加入的计算机收到这个响应包,于是就知道了自己的IP地址、子网掩码、网关地址、DNS服务器等等参数。

上网访问一个网页的实例

DNS协议

为了将网址转换成IP地址。已知DNS服务器为8.8.8.8,于是我们向这个地址发送一个DNS数据包(53端口)。

接下来利用子网掩码确定是不是同一个子网络;如果不是一个子网络,必须通过网关进行转发,此事接收方的地址是网关的MAC地址。

应用层协议

Http协议的数据包构造:

HTTP协议结构

以太网数据包的数据部分最大长度为1500字节,如果IP数据包的长度太大,就会分割成多个包。

服务器拿到http请求数据包后会将多个TCP数据包取出来拼起来,整合成完整的TCP数据包独处里面的“HTTP”请求,并做相应的HTTP响应,通过TCP协议发回来。

网络传输基础一

计算机网络的七层协议:

物理层 传输介质,光纤,电缆。Rj45,802.3

数据链层 单个链路如何传输数据。ATM,FDDI

以太网协议:Ethernet,一组电信号构成一个数据包,叫做”帧”(Frame)。每一帧分成两个部分:标头(Head)和数据(Data)。“标头”包含数据包的一些说明项,比如发送者、接受者、数据类型等等;”数据”则是数据包的具体内容。”标头”的长度,固定为18字节。“数据”的长度,最短为46字节,最长为1500字节。因此,整个”帧”最短为64字节,最长为1518字节。如果数据很长,就必须分割成多个帧进行发送。

MAC地址:以太网规定,连入网络的所有设备,都必须具有”网卡”接口。数据包必须是从一块网卡,传送到另一块网卡。网卡的地址,就是数据包的发送地址和接收地址,这叫做MAC地址。每块网卡出厂的时候,都有一个全世界独一无二的MAC地址,长度是48个二进制位,通常用12个十六进制数表示。

广播:broadcasting, 以太网采用了一种很”原始”的方式,它不是把数据包准确送到接收方,而是向本网络内所有计算机发送,让每台计算机自己判断,是否为接收方。

网络层 对端到端的包传输进行定义,定义逻辑地址,路由的实现,将包分解。IP,FDDI

依照上面的方法,不同局域网的电脑无法相互传输信息。”网络层”出现以后,每台计算机有了两种地址,一种是MAC地址,另一种是网络地址。两种地址之间没有任何联系,MAC地址是绑定在网卡上的,网络地址则是管理员分配的,它们只是随机组合在一起。

判断两台计算机是否属于同一个子网络呢?这就要用到另一个参数”子网掩码”(subnet mask)。

知道”子网掩码”,我们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,否则为0),然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否则就不是。

IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个是确定哪些地址在同一个子网络。

IP数据包: IP协议发送的数据,就叫做IP数据包。不难想象,其中必定包括IP地址信息。

IP数据包也分为”标头”和”数据”两个部分:”标头”部分主要包括版本、长度、IP地址等信息,20到60字节,”数据”部分则是IP数据包的具体内容。Ip数据包组成了以太网数据包的数据部分。由于整个IP数据包的最大长度为65535字节,IP数据包的数据部分最长65515字节。如果IP数据包数据超过1500字节,就需要分割成多个以太网数据包分开发送。

因为IP数据包是放在以太网数据包里发送的,所以我们必须同时知道两个地址,一个是对方的MAC地址,另一个是对方的IP地址。通常情况下,对方的IP地址是已知的(),但是我们不知道它的MAC地址。

第一种情况,如果两台主机不在同一个子网络,那么事实上没有办法得到对方的MAC地址,只能把数据包传送到两个子网络连接处的”网关”(gateway),让网关去处理。

第二种情况,如果两台主机在同一个子网络,那么我们可以用ARP协议,得到对方的MAC地址。ARP协议也是发出一个数据包(包含在以太网数据包中),其中包含它所要查询主机的IP地址,在对方的MAC地址这一栏,填的是FF:FF:FF:FF:FF:FF,表示这是一个”广播”地址。它所在子网络的每一台主机,都会收到这个数据包,从中取出IP地址,与自身的IP地址进行比较。如果两者相同,都做出回复,向对方报告自己的MAC地址,否则就丢弃这个包。

传输层 是否选择差错恢复协议,对收到的数据包进行排序。TCP,UDP,SPX

在互联网上任意两台主机上建立通信时,多个程序之间使用端口区分(每一个使用网卡的程序的编号),不同程序取得各自的数据。

“传输层”的功能,就是建立”端口到端口”的通信。相比之下,”网络层”的功能是建立”主机到主机”的通信。只要确定主机和端口,我们就能实现程序之间的交流。因此,Unix系统就把主机+端口,叫做”套接字”(socket)。有了它,就可以进行网络应用程序开发了。

UDP协议:也是由”标头”和”数据”两部分组成。UDP数据包非常简单,”标头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包。

TCP协议:有确认机制的UDP协议,每发出一个数据包都要求确认。如果有一个数据包遗失,就收不到确认,发出方就知道有必要重发这个数据包了。TCP数据包和UDP数据包一样,都是内嵌在IP数据包的”数据”部分。TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割。

以太网数据包结构

会话层 如何开始、结束一个会话。RPC,SQL

表示层 定义数据格式及加密

应用层 “应用层”的作用,就是规定应用程序的数据格式。

参考:互联网协议入门(一)

分布式限流方案

1、使用Java自带delayqueue的延迟队列实现

2、使用Redis实现,存储两个key,一个用于计时,一个用于计数。请求每调用一次,计数器增加1,若在计时器时间内计数器未超过阈值,则可以处理任务

3、使用guava提供工具库里的RateLimiter类(内部采用令牌捅算法实现)进行限流

阅读全文

Redis中zset类型数据的应用(实例+原理)

项目需求

公司APP页面需要展示一个横轴为时间,纵轴为指定基金和沪深300指数(或者其他指数)的折线图。

折线图的范围是可选的(比如一个月内,三个月内,六个月内等等),并且由于每一支基金的净值公布节奏不同,同一个时间范围的实际首尾时间,以及具体哪些日期是有值也是不一样的。

还有一个特点是沪深300的值是相对固定的,每天只会更新一次。但是很多基金都会以沪深300作为比较基准,使得需要查询一个数据多次。

比如A基金(每天公布净值)的三个月范围需要查0201,0202,0203…… 0430这些日期对应的沪深300;但是B基金(每周公布净值)的三个月范围只需要查0202,0209,0216,0223……这些日期对应的沪深300。如果使用上面的步骤,势必会使得程序运行效率降低。

解决思路

对于这种类似于静态数据,或者说修改一次查询很多次的数据,使用缓存是最好的选择了。下面以Redis来说明解决方案。

Redis有五种数据存储格式,分别是String,List,Hash,Set,Zset。

除了存储常用的数据,Redis还可以用Set数据类型来进行去重工作,(后面有了布隆过滤器);还可以使用Redis 共享分布式系统中的session;使用Redis解决分布式锁的问题;Redis的List还能做消息队列和发布订阅模式,实现消息传递和进程间的通信。

扯远了,具体到眼前的实例,实现起来非常简单,只需要使用带分数的集合类型就能行了。具体是使用沪深300的时间作为 score 分数,将每天的收盘价作为value,当确定时间范围之后取出对应时间范围的沪深300净值信息,即可拿到想要的数据。

代码实例

主要步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Set<Tuple> tuplesCsi300Set = RedisUtils.zrevrangeByScoreWithScores(csi300Key, dateParamMax, dateParamMin, 0, 1000);
if (tuplesCsi300Set != null && tuplesCsi300Set.size() > 0) {
compareGrowthList = getCompaFromRedis(param_csi300_arr, tuplesCsi300Set);
System.out.println("compareGrowthList:" + compareGrowthList);
} else {//先查,再存
//查询数据库全部沪深300的数据存入Redis中
JSONArray apiDataCsiRedis = getData();
Map<Double, String> csi300Map = new HashMap<>();
for (int i = 0; i < apiDataCsiRedis.size(); i++) {
JSONObject objecti = (JSONObject) apiDataCsiRedis.get(i);
csi300Map.put(Double.valueOf(objecti.getString("tDate")), objecti.getString("tClose"));
}
RedisUtils.addSortSetByScoreMap(csi300Key, csi300Map);
Set<Tuple> tuplesCsi300 = RedisUtils.zrevrangeByScoreWithScores(csi300Key, dateParamMax, dateParamMin, 0, 1000);
compareGrowthList = getCompaFromRedis(param_csi300_arr, tuplesCsi300);
System.out.println("compareGrowthList:" + compareGrowthList);
}

取数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 处理从redis中获取到的set
*
* @param param_csi300_arr
* @param csi3003YMap
* @return
*/
private LinkedList<Object> getCompaFromRedis(String[] param_csi300_arr, Set<Tuple> csi3003YMap) {

LinkedList<Object> compareGrowthList = new LinkedList<>();
compareGrowthList.addFirst(0.00);
TreeMap<String, String> treeMap = new TreeMap<>();
for (Iterator<Tuple> iterator = csi3003YMap.iterator(); iterator.hasNext(); ) {
Tuple next = iterator.next();
String score = Double.valueOf(next.getScore()).intValue() + "";
treeMap.put(score, next.getElement());
}
//最早时间的指数值 逻辑:(当前值/最早值) -1 *100
String s0 = treeMap.get(treeMap.firstKey());
for (int i = 1; i <= param_csi300_arr.length - 1; i++) {
//最早时间的指数值 逻辑:(当前值/最早值) -1 *100
if (treeMap.keySet().contains(param_csi300_arr[i])) { //判断第i个时间参数对应的取值结果是否存在
Double num = (Double.parseDouble(treeMap.get(param_csi300_arr[i])) / Double.parseDouble(s0) - 1) * 100;
compareGrowthList.add(Double.parseDouble(df_No_comma.format(num)));
} else {
compareGrowthList.add(BLANKVALUE);
}
}
return compareGrowthList;
}

主要代码逻辑还是比较简单的,拿到开始和结束时间后查询缓存,查到了就拿出区间内的数据进行后续处理,否则就从数据库(或者其他接口)查询并处理。

Redis有序集合实现原理

Redis的有序集合是使用跳表实现的。

相比红黑树,跳表的代码实现还是比较简单的,并且他也是一种支持动态扩容的数据结构。跳表的介绍不做多展开,具体参见数据结构章节的文章。

跳表这种结构实现类的类似二分查找的速度,查询的时间复杂度是O(logn),很高效。

HashMap学习笔记

基本介绍

Hash是指把任意长度的输入压缩映射成为固定长度的散列值。不同散列值对应的输入肯定不同,但是同一个散列值对应的输入可能有多个(碰撞)。

阅读全文