0%

常用注解

@Import: 可以在第三方包的类直接导入ioc容器中

1
2
3
4
5
6
@Import(StringUtils.class)
@Configuration //标注该类是一个配置类
@SpringBootConfiguration // 和上面的作用一样,用于人区分,上面做公共配置
public class AppConfig {

}

自动配置原理

场景启动器springboot提供了很多场景启动器,命名规则是spring-boot-starter-xx。如:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

场景启动器的启动器所有场景启动器都会引入-spring-boot-starter

1
2
3
4
5
6
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>

启动器会帮我们自动加载所有配置,这些配置都在
/org/springframework/boot/spring-boot-autoconfigure/3.1.0/spring-boot-autoconfigure-3.1.0.jar!/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

WebMvcAutoConfiguration

阅读全文 »

Query的使用

Spring官方文档:https://docs.spring.io/spring-data/elasticsearch/reference/elasticsearch/template.html#elasticsearch.operations.queries

Query是Springboot定义的接口,他的实现类如下:

Query子类

使用StringQuery

直接接收DSL语句,你可以编写语句,然后转换成字符串直接进行查询,前提是你对语法相当熟练。

举例1:
1
查询字段city为Cumminsville或者state=NY, 并且过滤出balance在25000至50000之间

如果使用REST API的调用方式,我们的请求体构造查询条件如下:

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
{
"query": {
"bool": {
"should": [ // should表示 or , must表示and
{
"match": {
"state": "NY" // 所查询的条件
}
},
{
"match": {
"city": "Cumminsville" // 所查询的条件
}
}
],
"filter": {
"range": { //范围查询
"balance": {
"ge": 25000, //大于
"le": 50000 //小于
}
}
}
}
}
}
阅读全文 »

前言

在使用springboot2.0.x时,整合elasticsearch,更多的是使用es提供的高级客户端RestHighLevelClient。在elasticsearch8后好多都被标注为过时了。

本文主要基于Springboot3,对Elasticsearch的索引、文档进行操作。版本信息如下:

1
2
3
JDK版本: JDK17
Springboot版本:3.0.2
Elasticsearch:8.5.3

PS: springbootElasticsearch的版本对应关系参考Spring官网-https://docs.spring.io/spring-data/elasticsearch/reference/elasticsearch/versions.html

前置工作

  • 安装Elasticsearch

使用的是docker镜像,拉取镜像: docker pull elasticsearch:8.5.3

启动Elasticsearch,将配置文件和插件目录都挂载到外部文件夹:

1
2
3
4
5
6
7
docker run --name elasticsearch --restart=always --privileged -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx128m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d docker.elastic.co/elasticsearch/elasticsearch:8.5.3
阅读全文 »

对象

Hotspot虚拟机在Java堆中对象分配、布局和访问的全过程。

对象的创建

1.类加载

当Java虚拟机遇到new指令时,就会去常量池中查找是否存在该对象的类的引用,检查类是否已经被加载、解析和初始化过。如果没有,就会执行类加载的过程。

2.内存分配

对象所需要的内存大小在类加载完成后就确定下来了。对象内存分配是从java堆中划分一块空间给对象。

内存的分配方式
  • 指针碰撞(Bump The Pointer):如果Java堆中,已经使用的内存在一边,空闲的内存在另一边,即Java堆中的内存是十分规整的,则通过一个指针指向两者的分界处,当完成内存分配后,指针往空闲空间那边移动对应的距离。
  • 空闲列表(Free List):通过一个列表来维护空闲的内存空间。

具体的分配方式如上所述,其实是取决于内存空间是否是规整的,内存空间是否规整那就取决于垃圾回收算法了。

阅读全文 »

主要记录一下学习阅读周志明先生的《深入理解Java虚拟机》,并且总结相关知识。

运行时数据区域

Java虚拟机会在执行Java程序时把内存划分为不同的内存区域。不同的内存区域的生命周期、所产生的异常都是不一样的。Java虚拟机所管理的内存包括以下几个运行时数据区域:

程序计数器

该内存区域能够产生的异常: 程序计数器是Java运行时数据区域唯一一个不会发生OOM异常的区域。

程序计数器(Program Counter Register):可以把它看作是执行的字节码的行号指示器。字节码解释工作就是通过程序计数器的值来选取下一条需要执行的指令。程序中分支、循环、跳转、异常处理以及线程的刮挂起/恢复都是由程序计数器来指示。

线程私有:多线程是通过CPU上下文切换,并且分配处理时间,实际上同一时刻都只会执行一条指令。为了在切换过程中能够准确地找到下一条执行指令。程序计数器是线程私有的

对于Java方法,程序计数器记录的是当前正在执行的字节码指令的地址;对于Native方法,程序计时器的值为空(Undefined)。

Java虚拟机栈

阅读全文 »

逆推二叉树

根据中序、后序得到二叉树。根据中序、前序得到二叉树。

二叉树的层序遍历

​ 假设现在有一颗二叉树如下

img

其中序遍历为: CBAEDF (左 - 根- 右)

其后序遍历为:CBEFDA (左 - 右 - 根)

其前序遍历为:ABCDEF (根 - 左 - 右)

如果我们现在只知道中序和后序,或者中序和前序。如何构建出二叉树。

假设已知中序和后序,构建过程如下。

阅读全文 »

zookeeper实现分布式锁-curator框架

zookeeper基于文件系统+通知机制,zookeeper的节点是树状的,每一个节点为一个znode。通过zookeeper客户端可以去创建节点,创建的节点有很多种。永久节点、永久节点带序号、临时节点、临时节点带序号等等。

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
public enum CreateMode {

/**
* 持久节点,客户端断开节点不会删除
*/
PERSISTENT (0, false, false, false, false),
/**
* 持久节点,客户端断开节点不会删除,并且节点名称会有序增加
*/
PERSISTENT_SEQUENTIAL (2, false, true, false, false),
/**
* 临时节点,客户端断开链接就会自动删除
*/
EPHEMERAL (1, true, false, false, false),
/**
* 临时节点,客户端断开链接就会自动删除,并且节点名称会有序增加
*/
EPHEMERAL_SEQUENTIAL (3, true, true, false, false),
/**
* 作为容器节点,作为选举时的leader节点或者锁之类的。如果子节点被删除也会自动被删除
*/
CONTAINER (4, false, false, true, false),
/**
* 持久节点,如果没有在给定的ttl内修改节点,一旦没有子节点就会被删除
*/
PERSISTENT_WITH_TTL(5, false, false, false, true),
/**
* 持久有序节点,如果没有在给定的ttl内修改节点,一旦没有子节点就会被删除
*/
PERSISTENT_SEQUENTIAL_WITH_TTL(6, false, true, false, true);

实现分布式锁

​ zookeeper的分布式锁实现方式比较简便,因为zookeeper是由一个一个znode组成,那么定义一个节点为:“/lock”,每次请求时,通过zookeeper创建一个带有顺序的临时节点,并且每一个节点都有一个WatchEvent,通过监听上一个节点来判断是否获取到锁。对于获取到锁的线程当执行完毕后删除对应的节点即可。

1. 创建当前路径的节点

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
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;


public class InternalsDriver {
public String createNode(ZooKeeper zkClient, String path, String basePath) {
String createdPath = null;
try {
/** 判断根节点是否存在 没有则创建**/
Stat exists = zkClient.exists(basePath, false);
if (exists == null) {
zkClient.create(basePath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
createdPath = zkClient.create("/locks/lock-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return createdPath.substring(createdPath.lastIndexOf("/") + 1);
}
}

2.获取锁

2.1 首先获取到所有的子节点,并进行排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private List<String> getSortedChildren() {
List<String> sortedList = null;
try {
List<String> children = zkClient.getChildren(basePath, false);
sortedList = new ArrayList<>(children);
Collections.sort(sortedList, new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
return getLockNumber(lhs, LOCK_NAME).compareTo(getLockNumber(rhs, LOCK_NAME));
}
});
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
return sortedList;
}
阅读全文 »

创建索引指定副本和分片数量

1
2
3
4
5
6
{
"settings": {
"number_of_shards": 3, //主分片数
"number_of_replicas": 1 //副本
}
}

elasticsearch-head安装

github上克隆代码elasticsearch-head,按照md中操作启动即可,需要安装node.js

1
2
3
4
5
6
git clone https://github.com/mobz/elasticsearch-head.git

cd elasticsearch-head

npm install
npm run start

访问localhost:9200。

健康状态说明:

绿色: 正常运行;

黄色: 副本未正常运行;

红色:分片未正常运行。

阅读全文 »

ES集群部署

环境: MacOS

创建三个副本

image-20230215162316016

修改 elasticsearch.yml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 解决跨域
http.cors.enabled: true
http.cors.allow-origin: "*"
// 指定当前节点的ip以及端口号
network.host: localhost
http.port: 18122
// 集群通信所使用的端口号
transport.tcp.port: 9303
// 指定节点的名称
node.name: node-1001
node.master: true
node.data: true
// 集群的名称,统一集群下所有节点的配置都相同
cluster.name: my-application

对于node-1001配置

1
2
3
4
5
6
7
8
9
cluster.name: my-application
node.name: node-1001
node.master: true
node.data: true
network.host: localhost
http.port: 18120
transport.tcp.port: 9301
http.cors.enabled: true
http.cors.allow-origin: "*"

对于node-1002配置,需要与node-1001建立,需要增加配置

1
2
3
4
5
6
7
8
9
10
11
12
13
cluster.name: my-application
node.name: node-1002
node.master: true
node.data: true
network.host: localhost
http.port: 18121
transport.tcp.port: 9302
http.cors.enabled: true
http.cors.allow-origin: "*"
// node-1002需要发现第一个节点,与其建立集群
discovery.seed_hosts: ["localhost:9301"]
discovery.zen.fd.ping_timeout: 1m
discovery.zen.fd.ping_retries: 5
阅读全文 »

ElasticSearch

创建maven项目,引入elasticsearch依赖,使用版本7.8.0

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.8.0</version>
</dependency>
<!-- es客户端 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.8.0</version>
</dependency>

创建es客户端

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) throws IOException {
// 创建es客户端
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")));

// 关闭客户端
client.close();

}

创建索引

1
2
3
4
5
6
// 创建索引
CreateIndexResponse order = client.indices().create(new CreateIndexRequest("order"), RequestOptions.DEFAULT);
// 创建结果
boolean acknowledged = order.isAcknowledged();

System.out.println("创建结果: " + (acknowledged ? "成功": "失败"));

查询索引信息

1
2
3
4
5
6
7
// 查询索引
GetIndexResponse order = client.indices().get(new GetIndexRequest("order"), RequestOptions.DEFAULT);
// 打印相关信息; 索引的别名、结构、设置信息
System.out.println(order.getAliases());
System.out.println(order.getMappings());
System.out.println(order.getSettings());

删除索引

阅读全文 »