源码解读
java1、ArrayList无参构造方法的默认容量是10,当需要扩容时,ArrayList的实现中大量地调用了Arrays.copyof()和System.arraycopy()方法,ArrayList基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就 ...
阅读更多
elasticsearch-注意点

原理剖析

1)写数据

  • 客户端选择一个 node 发送请求过去,这个 node 就是 coordinating node(协调节点)。
  • coordinating node 对 document 进行路由,将请求转发给对应的 node(有 primary shard)。
  • 实际的 node 上的 primary shard 处理请求,然后将数据同步到 replica node。
  • coordinating node 如果发现 primary node 和所有 replica node 都搞定之后,就返回响应结果给客户端。

2)读数据。

  • 客户端发送请求到任意一个 node,成为 coordinate node。
  • coordinate node 对 doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin - - 随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。
  • 接收请求的 node 返回 document 给 coordinate node。
    coordinate node 返回 document 给客户端。

写数据,实际上都写到磁盘文件里去了,查询的时候,操作系统会将磁盘文件里的数据自动缓存到 filesystem cache 里面去。es 的搜索引擎严重依赖于底层的 filesystem cache,你如果给 filesystem cache 更多的内存,尽量让内存可以容纳所有的 idx segment file 索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。所以,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。建议用 es + hbase 这么一个架构。

3)搜索数据。

  • 客户端发送请求到一个 coordinate node。
  • 协调节点将搜索请求转发到所有的 shard 对应的 primary shard 或 replica shard,都可以。
  • query phase:每个 shard 将自己的搜索结果(其实就是一些 doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。
  • fetch phase:接着由协调节点根据 doc id 去各个节点上拉取实际的 document 数据,最终返回给客户端。

其实 es 第一是准实时的,数据写入 1 秒后可以搜索到;可能会丢失数据的。有 5 秒的数据,停留在 buffer、translog os cache、segment file os cache 中,而不在磁盘上,此时如果宕机,会导致 5 秒的数据丢失。数据写入 segment file 之后,同时就建立好了倒排索引。

4)删除/更新数据
如果是删除操作,commit 的时候会生成一个 .del 文件,里面将某个 doc 标识为 deleted 状态,那么搜索的时候根据 .del 文件就知道这个 doc 是否被删除了。

如果是更新操作,就是将原来的 doc 标识为 deleted 状态,然后新写入一条数据。

buffer 每次 refresh 一次,就会产生一个 segment file,所以默认情况下是 1 秒钟一个 segment file,这样下来 segment file 会越来越多,此时会定期执行 merge。每次 merge 的时候,会将多个 segment file 合并成一个,同时这里会将标识为 deleted 的 doc 给物理删除掉,然后将新的 segment file 写入磁盘,这里会写一个 commit point,标识所有新的 segment file,然后打开 segment file 供搜索使用,同时删除旧的 segment file。

常见错误以及解决方案

采用动态Mapping

1、1.5.x版本之后,需要分词的字段需要设定text类型和对应的analyzer ;仅需要精确匹配的可直接设置为keyword类型。
2、长文本高亮需要在text类型的基础上,设置fast-vector-highlighter高亮方式,高亮效率能提升20倍以上。

阅读更多
Alibaba-nacos服务

nacos是云原生应用的动态服务发现、配置管理和服务管理平台。

源码 官网

阅读更多
常用算法

红包算法

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
public class RedPacket {

/**
* 生成红包最小值 1分
*/
private static final int MIN_MONEY = 1;

/**
* 生成红包最大值 200人民币
*/
private static final int MAX_MONEY = 200 * 100;

/**
* 小于最小值
*/
private static final int LESS = -1;
/**
* 大于最大值
*/
private static final int MORE = -2;

/**
* 正常值
*/
private static final int OK = 1;

/**
* 最大的红包是平均值的 TIMES 倍,防止某一次分配红包较大
*/
private static final double TIMES = 2.1F;

private int recursiveCount = 0;

public List<Integer> splitRedPacket(int money, int count) {
List<Integer> moneys = new LinkedList<>();

//金额检查,如果最大红包 * 个数 < 总金额;则需要调大最小红包 MAX_MONEY
if (MAX_MONEY * count <= money) {
System.err.println("请调大最小红包金额 MAX_MONEY=[" + MAX_MONEY + "]");
return moneys ;
}


//计算出最大红包
int max = (int) ((money / count) * TIMES);
max = max > MAX_MONEY ? MAX_MONEY : max;

for (int i = 0; i < count; i++) {
//随机获取红包
int redPacket = randomRedPacket(money, MIN_MONEY, max, count - i);
moneys.add(redPacket);
//总金额每次减少
money -= redPacket;
}

return moneys;
}

private int randomRedPacket(int totalMoney, int minMoney, int maxMoney, int count) {
//只有一个红包直接返回
if (count == 1) {
return totalMoney;
}

if (minMoney == maxMoney) {
return minMoney;
}

//如果最大金额大于了剩余金额 则用剩余金额 因为这个 money 每分配一次都会减小
maxMoney = maxMoney > totalMoney ? totalMoney : maxMoney;

//在 minMoney到maxMoney 生成一个随机红包
int redPacket = (int) (Math.random() * (maxMoney - minMoney) + minMoney);

int lastMoney = totalMoney - redPacket;

int status = checkMoney(lastMoney, count - 1);

//正常金额
if (OK == status) {
return redPacket;
}

//如果生成的金额不合法 则递归重新生成
if (LESS == status) {
recursiveCount++;
System.out.println("recursiveCount==" + recursiveCount);
return randomRedPacket(totalMoney, minMoney, redPacket, count);
}

if (MORE == status) {
recursiveCount++;
System.out.println("recursiveCount===" + recursiveCount);
return randomRedPacket(totalMoney, redPacket, maxMoney, count);
}

return redPacket;
}

/**
* 校验剩余的金额的平均值是否在 最小值和最大值这个范围内
*
* @param lastMoney
* @param count
* @return
*/
private int checkMoney(int lastMoney, int count) {
double avg = lastMoney / count;
if (avg < MIN_MONEY) {
return LESS;
}

if (avg > MAX_MONEY) {
return MORE;
}

return OK;
}


public static void main(String[] args) {
RedPacket redPacket = new RedPacket();
List<Integer> redPackets = redPacket.splitRedPacket(20000, 100);
System.out.println(redPackets);

int sum = 0;
for (Integer red : redPackets) {
sum += red;
}
System.out.println(sum);
}

}
阅读更多
springBoot --- 原理
SpringFactoriesLoader详解JVM提供了3种类加载器: BootstrapClassLoader、 ExtClassLoader、 AppClassLoader分别加载Java核心类库、扩展类库以及应用的类路径( CLASSPATH)下的类库。JVM通过双亲委派模型(当一个类加载器 ...
阅读更多
springBoot --- 开发中的技巧

后台接口统一返回的格式约定

除了定义一个的实体{"code":"","msg":"","res":""}类进行约定申明外,还可以这样操作:

在springMvc的@ResponseBody返回时,会调用MappingJackson2HttpMessageConverter和StringHttpMessageConverter两个类进行处理。于是,可以继承该类,对其进行操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//在一个全局配置类上加入如下的两个自定义Bean
@Bean
@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class, search = SearchStrategy.CURRENT)
public MappingJackson2HttpMessageConverter getJsonMessageConvent(@Value("${spring.http.encoding.charset:utf-8}") String encoding) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
JsonMessageConverter mappingJackson2HttpMessageConverter = new JsonMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);

return mappingJackson2HttpMessageConverter;
}

@Bean
@ConditionalOnMissingBean(value = StringHttpMessageConverter.class, search = SearchStrategy.CURRENT)
public StringHttpMessageConverter getStringMessageConvent(@Value("${spring.http.encoding.charset:utf-8}") String encoding) {
StringMessageConverter stringMessageConverter = new StringMessageConverter();
stringMessageConverter.setDefaultCharset(Charset.forName(encoding));
return stringMessageConverter;
}

阅读更多
study--Vue疑惑解答

首先,最重要的安装淘宝镜像: $ npm install -g cnpm --registry=https://registry.npm.taobao.org

Vue 生命周期

  • beforeCreate:
    在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

  • created:
    在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。

  • beforeMount:
    在挂载开始之前被调用:相关的 render 函数首次被调用。
    该钩子在服务器端渲染期间不被调用。以下周期在服务端渲染期间都不被调用。

  • mounted:
    el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.​$el 也在文档内。注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。

  • beforeUpdate:
    数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。

  • updated:
    由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

  • activated:
    keep-alive 组件激活时调用。

  • deactivated:
    keep-alive 组件停用时调用。

  • beforeDestroy:
    实例销毁之前调用。在这一步,实例仍然完全可用。

  • destroyed:
    Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

阅读更多
study--技术演进
基本概念1)什么是分布式?** 系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上。 2)什么是高可用? 系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性。 3)什么 ...
阅读更多
工具类-三方工具的使用

七牛云

阅读更多
锁的概念

一、为什么要用锁?

锁-是为了解决并发操作引起的脏读、数据不一致的问题。

二、锁实现的基本原理

1、volatile

java允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。volatile在多处理器开发中保证了共享变量的“ 可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。

结论:如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。

阅读更多