服务器每秒钟执行命令数量是什么_日常学习笔记.md

## ShedLock

ShedLock不是一个`定时任务`框架,而是一个`保证定时任务在分布式环境中的合理执行`的辅助框架,保证定时任务在分布式环境中同一时间最多`只执行一次`。同时一个任务在执行时,另一个任务无法获取锁时会跳过当前任务的执行。

ShedLock的实现原理是采用`公共存储实现的锁机制`,使得同一时间点只有第一个执行定时任务的服务实例能执行成功,并在公共存储中存储"我正在执行任务,从什么时候(预计)执行到什么时候",其他服务实例执行时如果发现任务正在执行,则直接跳过本次执行,从而保证同一时间一个任务只被执行一次。

参考博客:https://blog.csdn.net/johnf_nash/article/details/90741405

## Redis

### Redis如何对key进行分类存储?

Redis中的key的分类存储是通过`key的命名`来实现的。

Key的命名方式:

```java

aaa:bbb:ccc=XXX

```

其实就是我们把 key 定义的有规律一些,通过在key的字符串内部 分类,上图只是因为我们用 工具查看的时候,它会把`key中的第一个:前的部分显示成文件夹`的形式,其实实际存储的时候 还是 正常的 key:value 形式。

参考博客:https://www.cnblogs.com/libin6505/p/9799238.html

### Redis集群的三大模式

#### 1、主从模式

在主从复制中,数据库分为两类:`主数据库`(master)和`从数据库`(slave)。

> 工作机制

当slave启动后,主动向master发送SYNC命令。master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这段时间的命令,然后将保存的快照文件和缓存的命令发送给slave。slave接收到快照文件和命令后加载快照文件和缓存的执行命令。

复制初始化后,master每次接收到的写命令都会同步发送给slave,保证主从数据一致性。

> 缺点

master节点在主从模式中唯一,若master挂掉,则redis无法对外提供写服务。

#### 2、Sentinel模式

主从模式的弊端就是不具备高可用性,当master挂掉以后,Redis将不能再对外提供写入操作,因此sentinel应运而生。

sentinel中文含义为哨兵,它的作用就是`监控redis集群的运行状况`。

> 工作机制

1、每个sentinel以每秒钟一次的频率向它所知的master,slave以及其他sentinel实例发送一个PING 命令。

2、如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被sentinel标记为主观下线。

3、如果一个master被标记为主观下线,则正在监视这个master的所有sentinel要以每秒一次的频率确认master的确进入了主观下线状态。

4、当有足够数量的sentinel(大于等于配置文件指定的值)在指定的时间范围内确认master的确进入了主观下线状态, 则master会被标记为客观下线。

5、在一般情况下, 每个sentinel会以每 10 秒一次的频率向它已知的所有master,slave发送 INFO 命令。

6、当master被sentinel标记为客观下线时,sentinel向下线的master的所有slave发送 INFO 命令的频率会从 10 秒一次改为 1 秒一次。

7、若没有足够数量的sentinel同意master已经下线,master的客观下线状态就会被移除。

8、若master重新向sentinel的 PING 命令返回有效回复,master的主观下线状态就会被移除。

当使用sentinel模式的时候,客户端就不要直接连接Redis,而是连接sentinel的ip和port,由sentinel来提供具体的可提供服务的Redis实现,这样当master节点挂掉以后,sentinel就会感知并将新的master节点提供给使用者。

#### 3、Cluster模式

sentinel模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中。cluster模式的出现就是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。

cluster可以说是sentinel和主从模式的结合体,通过cluster可以实现主从和master重选功能,所以如果配置两个副本三个分片的话,就需要六个Redis实例。因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容。

使用集群,只需要将redis配置文件中的cluster-enable配置打开即可。每个集群中至少需要三个主数据库才能正常运行,新增节点非常方便。

redis cluster集群是去中心化的,每个节点都是平等的,连接哪个节点都可以获取和设置数据。

当然,平等指的是master节点,因为slave节点根本不提供服务,只是作为对应master节点的一个备份。

参考博客:https://blog.csdn.net/miss1181248983/article/details/90056960

### Redis集群创建

Redis集群的节点数量最少需要`6`个,才能保证高可用,每个节点需要在`redis.conf`文件中配置`cluster-enabled yes`

Redis5之后使用redis-cli创建集群,命令为:

```shell

redis-cli --cluster create --cluster-replicas 1

```

比如:`redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster--replicas 1`

`--cluster-replicas 1`表示每个主节点指定一个从节点

更多内容,参考博客: https://www.jianshu.com/p/9c65057d5143

### Linux下安装、启动、连接Redis

#### 安装

1、安装gcc编译环境

```shell

yum install gcc c++

```

2、使用xftp上传redis压缩包,解压

```shell

tar zxvf redis-3.0.0.tar.gz

```

3、xshell进入解压目录执行`make`命令进行编译

4、编译完成后安装,安装路径可自己指定

```shell

make install PREFIX=/usr/local/redis

```

#### 启动

##### 前端启动

进入redis安装目录

```shell

cd /usr/local/redis/bin

```

执行命令

```shell

./redis-server

```

##### 后端启动

1、复制redis.conf到redis的安装目录

```shell

cp redis.conf /usr/local/redis/bin

```

2、编辑redis.conf,配置为后台启动,然后保存退出

```shell

daemonize yes

```

3、执行命令

```shell

./redis-server redis.conf

```

4、查看是否启动成功

```shell

ps aux|grep redis

```

#### 连接

进入redis安装目录,

```shell

cd /usr/local/redis/bin/

```

执行

```shell

./redis-cli

```

参考博客:https://blog.csdn.net/wb01234567890456/article/details/78653802

### 查看redis集群节点的主从关系

1、启动redis-cli连接redis server

```shell

#若想在全局使用redis-cli命令,需把redis-cli文件复制到/usr/local/bin目录下(相当于创建一个全局快捷方式)

redis-cli -p 6379

```

2、密码验证

```shell

auth password

```

3、查看节点的主从关系

```shell

info Replication

```

示例:

```shell

[root@svc-app-t24 ~]# redis-cli -p 6379

127.0.0.1:6379> auth 123

OK

127.0.0.1:6379> info Replication

# Replication

role:master #master角色

connected_slaves:1

slave0:ip=192.168.0.1,port=8379,state=online,offset=11727611939,lag=1

master_replid:f557834ddec8c2939403bb456ee6c7e446ae123f

master_replid2:0000000000000000000000000000000000000000

master_repl_offset:11727341997

second_repl_offset:-1

repl_backlog_active:1

repl_backlog_size:1045676

repl_backlog_first_byte_offset:21726563422

repl_backlog_histlen:1041576

```

## 数据结构

### 红黑树的特点

1.结点是红色或黑色。

2.根结点是黑色。

3.每个叶子结点都是黑色的空结点(NIL结点)。

4.每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)

5.从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。

## 算法

### 快速排序

快速排序是对冒泡排序的一种改进。

#### 基本思想

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据小,然后再按照此方法对两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

本质:把基数大的都放在基准数的右边,把基准数小的放在基准数的左边,这样就找到了该数据在数组中的正确位置,递归时分别对前半部分和后半部分排序

#### 算法代码

```java

public class QuickSort{

public static void main(String[] args){

int arr= { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };

quickSort(arr, 0, arr.length - 1);

System.out.println("排序后:");

for (int i : arr) {

System.out.println(i);

}

}

private static void quickSort(int[] arr,int low,int high){

if(low < high){

// 找寻基准数据的正确索引

int index=getIndex(arr,low,high);

//进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序

quickSort(arr,low,index-1);

quickSort(arr,index+1,high);

}

}

private static int getIndex(int[] arr, int low, int high) {

//基准数据

int tmp=arr[low];

while (low < high) {

// 当队尾的元素大于等于基准数据时,向前挪动high指针

while (low < high && arr[high] >= tmp) {

high--;

}

// 如果队尾元素小于tmp了,需要将其赋值给low

arr[low] = arr[high];

// 当队首元素小于等于tmp时,向前挪动low指针

while (low < high && arr[low] <= tmp) {

low++;

}

// 当队首元素大于tmp时,需要将其赋值给high

arr[high] = arr[low];

}

// 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置

arr[low] = tmp;

return low; // 返回tmp的正确位置

}

}

```

## ElasticSearch

### 查询语句

#### 查询所有

GET /school/student/_search

#### 多索引,多type查询

/_search:在所有的索引中搜索所有的类型

/school/_search:在 school 索引中搜索所有的类型

/school,ad/_search:在 school 和ad索引中搜索所有的类型

/s*,a*/_search:在所有以g和a开头的索引中查询所有的类型

/school/student/_search:在school索引中搜索student类型

/school,ad/student,phone/_search:在school和ad索引上搜索student和phone类型

/_all/student,phone/_search:在所有的索引中搜索student和phone类型

#### 按条件查询

GET /school/student/_search?q=name:houyi

......

参考博客:https://www.jianshu.com/p/c377477df7fc

### Kibana

Kibana是为ElasticSearch设计的开源分析和可视化平台,你可以使用Kibana来搜索,查看存储在ElasticSearch索引中的数据并与之交互,你可以很容易实现高级的数据分析和可视化,以图标的形式展现出来

## 前端

### Promise是什么?

Promise是异步编程的一种解决方案,在ES6中Promise被列为了正式规范,统一了用法,原生提供了Promise对象。

```javascript

// resolve代表成功 reject失败 都是一个函数

let p = new Promise(function(reslove,reject){

//reslove('成功') //状态由等待变为成功,传的参数作为then函数中成功函数的实参

reject('失败') //状态由等待变为失败,传的参数作为then函数中失败函数的实参

})

//then中有2个参数,第一个参数是状态变为成功后应该执行的回调函数,第二个参数是状态变为失败后应该执行的回调函数。

p.then((data)=>{

console.log('成功'+data)

},(err)=>{

console.log('失败'+err)

})

```

参考博客:https://www.jianshu.com/p/3023a9372e5f

### Npm强制清理缓存

```powershell

npm cache clean --force

```

## kafka

### 使用场景

- 消息

kafka更好的替换传统的消息系统,消息系统被用于各种场景(解耦数据生产者,缓存未处理的消息等),与大多数消息系统比较,kafka有更好的吞吐量,内置分区,副本和故障转移,这有利于处理大规模的消息。

根据我们的经验,消息往往用于较低的吞吐量,但需要低的`端到端`延迟,并需要提供强大的耐用性的保证。

在这一领域的kafka比得上传统的消息系统,如的`ActiveMQ`或`RabbitMQ`的。

- 网站活动追踪

kafka原本的使用场景:用户的活动追踪,网站的活动(网页游览,搜索或其他用户的操作信息)发布到不同的话题中心,这些消息可实时处理,实时监测,也可加载到Hadoop或离线处理数据仓库。

每个用户页面视图都会产生非常高的量。

- 指标

kafka也常常用于监测数据。分布式应用程序生成的统计数据集中聚合。

- 日志聚合

许多人使用Kafka作为日志聚合解决方案的替代品。日志聚合通常从服务器中收集物理日志文件,并将它们放在中央位置(可能是文件服务器或HDFS)进行处理。Kafka抽象出文件的细节,并将日志或事件数据更清晰地抽象为消息流。这允许更低延迟的处理并更容易支持多个数据源和分布式数据消费。

- 流处理

kafka中消息处理一般包含多个阶段。其中原始输入数据是从kafka主题消费的,然后汇总,丰富,或者以其他的方式处理转化为新主题,例如,一个推荐新闻文章,文章内容可能从“articles”主题获取;然后进一步处理内容,得到一个处理后的新内容,最后推荐给用户。这种处理是基于单个主题的实时数据流。从`0.10.0.0`开始,轻量,但功能强大的流处理,就可以这样进行数据处理了。

除了Kafka Streams,还有Apache Storm和Apache Samza可选择。

- 事件采集

事件采集是一种应用程序的设计风格,其中状态的变化根据时间的顺序记录下来,kafka支持这种非常大的存储日志数据的场景。

- 提交日志

kafka可以作为一种分布式的外部日志,可帮助节点之间复制数据,并作为失败的节点来恢复数据重新同步,kafka的日志压缩功能很好的支持这种用法,这种用法类似于`Apacha BookKeeper`项目。

参考网站:https://www.orchome.com/295

### windows环境下安装Kafka

#### zookeeper安装

1、安装Kafka之前必须先安装zookeeper,下载地址:

http://zookeeper.apache.org/releases.html#download

2、解压进入目录D:\Kafka\zookeeper-3.4.9\conf,复制zoo_sample.cfg,重命名为zoo.cfg

3、编辑zoo.cfg,配置dataDir

4、添加系统环境变量:ZOOKEEPER_HOME=D:\Kafka\zookeeper-3.4.9

5、Path环境变量追加路径:%ZOOKEEPER_HOME%\bin

6、cmd运行zookeeper:`zkServer`,运行后请不要关闭此窗口

#### Kafka安装

1、下载安装包:http://kafka.apache.org/downloads,注意要下载二进制版本(Binary downloads)

2、解压进入目录D:\Kafka\kafka_2.12-0.11.0.0\config,编辑server.properties,配置log.dirs

3、Kafka默认运行在9092端口运行,并连接zookeeper默认端口2181

4、进入目录D:\Kafka\kafka_2.12-0.11.0.0,cmd,输入kafka启动命令:

```shell

.\bin\windows\kafka-server-start.bat .\config\server.properties

```

参考博客:https://www.jianshu.com/p/d64798e81f3b

## Dubbo+zookeeper

### 服务提供者和消费者配置

1、服务提供者配置dubbo-provider.xml

```xml

#配置当前应用信息

#提供方配置,这里配置了一个请求过滤器

#用于配置连接注册中心相关信息

#用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受

#用于暴露一个服务,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心,ref指定服务接口的实现类引用

```

接口

```java

public interface DemoService{

String sayHello();

}

```

实现类

```java

@Service("demoService")

public calss DemoServiceImpl implement DemoService{

@override

public String sayHello(){

return "Hello World";

}

}

```

2、服务消费者配置dubbo-consumer.xml

```xml

#配置当前应用信息

#用于配置连接注册中心相关信息

#用于创建一个远程服务代理,一个引用可以指向多个注册中心

#check:启动时检查提供者是否存在,true报错,false忽略

```

3.在代码中使用`@Reference`注入接口实例

```java

@RestController

@RequestMapping("/test")

public class Test{

@Reference

private DemoService demoService;

@GetMapping("/hello")

public String sayHello(){

return demoService.sayHello();

}

}

```

注:服务提供者接口需打包导入到消费者pom依赖中

## JWT

JWT,即`JSON Web Token`,是目前最流行的跨域认证解决方案

### 原理

1、用户发送账号密码,服务器认证以后,生成一个JSON对象,发送给用户

```json

{

"姓名": "张三",

"角色": "管理员",

"到期时间": "2018年7月1日0点0分"

}

```

2、之后用户和服务器通信都要携带这个JSON对象,服务器只靠这个对象认定用户身份,为防止用户篡改数据,服务器生成该对象时应加上签名

3、服务器不保存任何session数据,服务器`无状态`,方便扩展

### 数据结构

JWT是一个很长的字符串,分为三个部分:`头部`(Header)、`负载`(Payload)和`签名`(Signature),每个部分之间用`.`隔开

1、Header是一个JSON对象,描述JWT的元数据

```json

{

"alg": "HS256",

"typ": "JWT"

}

```

2、Payload也是一个JSON对象,用了存放实际需要传递的数据,规定了7个官方字段

- iss (issuer):签发人

- exp (expiration time):过期时间

- sub (subject):主题

- aud (audience):受众

- nbf (Not Before):生效时间

- iat (Issued At):签发时间

- jti (JWT ID):编号

除了官方字段,也可以在这个部分定义私有字段,注意不要把私密信息放在这个部分

```json

{

"sub": "1234567890",

"name": "John Doe",

"admin": true

}

```

3、Signature是对前两部分的签名,防止数据篡改

需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名

```json

HMACSHA256(

base64UrlEncode(header) + "." +

base64UrlEncode(payload),

secret)

```

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用`.`分隔,就可以返回给用户

参考博客:http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

### 生成token

1、引入jwt依赖

```xml

io.jsonwebtoken

jjwt

0.9.1

```

2、编写方法,传入主体信息和密钥(用于校验时解密)

```java

public static Map generateToken(String userId,String secret){

//过期时间

long EXPIRE=1000*60*60*24;

Map data = new HashMap<>(16);

Date expiration=new Date(System.currentTimeMillis() + EXPIRE);

String token = Jwts.builder()

.setHeaderParam("typ","JWT")//token的类型

.setHeaderParam("alg","HS256")//加密算法

.setExpiration(expiration)//过期时间

.setIssuedAt(new Date())//签名时间,时间戳

.claim("userId",userId)//主体信息

.signWith(SignatureAlgorithm.HS256,secret)//使用密钥进行签名加密

.compact();

//将token和过期时间返回给用户

data.put("token",token);

data.put("expire_time",new SimpleDateFormat("yyyy-MM-dd HH:ss:mm").format(expiration));

return data;

}

```

### 校验token

token解密后返回`Claims`对象,其本质上是一个Map,可以从Claims对象中获取`主体信息`

```java

public static Claims validateTokenAndGetClaims(String token, String secret){

Claims claims = Jwts.parser()

.setSigningKey(secret)//使用密钥对签名进行解密

.parseClaimsJws(token)//对token进行解析

.getBody();//获得主体信息

return claims;

}

```

查看Claims的实现类`DefaultClaims`源码,可以看到能够获取的字段的get/set方法

```java

public class DefaultClaims extends JwtMap implements Claims {

public DefaultClaims() {

}

public DefaultClaims(Map map) {

super(map);

}

public String getIssuer() {

return this.getString("iss");//iss (issuer):签发人

}

public Claims setIssuer(String iss) {

this.setValue("iss", iss);

return this;

}

public String getSubject() {

return this.getString("sub");//sub (subject):主题

}

public Claims setSubject(String sub) {

this.setValue("sub", sub);

return this;

}

public String getAudience() {

return this.getString("aud");//aud (audience):受众

}

public Claims setAudience(String aud) {

this.setValue("aud", aud);

return this;

}

public Date getExpiration() {

return (Date)this.get("exp", Date.class);//exp (expiration time):过期时间

}

public Claims setExpiration(Date exp) {

this.setDate("exp", exp);

return this;

}

public Date getNotBefore() {

return (Date)this.get("nbf", Date.class);//nbf (Not Before):生效时间

}

public Claims setNotBefore(Date nbf) {

this.setDate("nbf", nbf);

return this;

}

public Date getIssuedAt() {

return (Date)this.get("iat", Date.class);//iat (Issued At):签发时间

}

public Claims setIssuedAt(Date iat) {

this.setDate("iat", iat);

return this;

}

public String getId() {

return this.getString("jti");//jti (JWT ID):编号

}

public Claims setId(String jti) {

this.setValue("jti", jti);

return this;

}

//获取用户自己定义的claim主体信息字段

public T get(String claimName, Class requiredType) {

Object value = this.get(claimName);

if (value == null) {

return null;

} else {

if ("exp".equals(claimName) || "iat".equals(claimName) || "nbf".equals(claimName)) {

value = this.getDate(claimName);

}

return this.castClaimValue(value, requiredType);

}

}

//将claim参数的值转换为想转换的类型

private T castClaimValue(Object value, Class requiredType) {

if (requiredType == Date.class && value instanceof Long) {

value = new Date((Long)value);//Long类型转换为日期类型

}

//Integer类型转其他类型

if (value instanceof Integer) {

int intValue = (Integer)value;

if (requiredType == Long.class) {

value = (long)intValue;//转换为long类型

} else if (requiredType == Short.class && -32768 <= intValue && intValue <= 32767) {

value = (short)intValue;//转换为short类型

} else if (requiredType == Byte.class && -128 <= intValue && intValue <= 127) {

value = (byte)intValue;//转换为byte类型

}

}

if (!requiredType.isInstance(value)) {//未知的类型,直接报错

throw new RequiredTypeException("Expected value to be of type: " + requiredType + ", but was " + value.getClass());

} else {//其他类型转换

return requiredType.cast(value);

}

}

}

```

## Linux

### Linux查找软件安装目录和位置

1.`find`查找关键字

```shell

find / -name xxx

```

2.`whereis`查找文件位置

```shell

whereis xxx

```

3.`which`查询软件命令的运行文件路径

```shell

which xxx

```

4.`locate`是`find -name`的另一种写法,但是比它快,因为它不搜索具体目录,而是搜索一个数据库,但它查不到最新变动过的文件,因此查询之前可以先`update`

```shell

updatedb #手动更新数据库

locate /etc/xxx

```

参考博客:https://www.fujieace.com/linux/software-location.html

## SpringBoot

### SpringBoot添加拦截器

1、在interceptor包下创建拦截器,继承`HandlerInterceptor`,重写相应方法

```java

public class AuthenticationInterceptor implements HandlerInterceptor {

//处理请求之前调用(大部分时候都是重写此方法)

@Override

public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {

return true;

}

//处理请求之后调用

@Override

public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

}

//请求结束之后调用

@Override

public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

}

```

2、在config包下创建WebMvcConfig,继承`WebMvcConfigurer`,重写`addInterceptor`方法

```java

@Configuration

public class WebMvcConfig extends WebMvcConfigurer {

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new AuthenticationInterceptor())//添加拦截器

//添加拦截规则,如果设置此规则,不在拦截规则下的路径则不会被拦截

//如果不设置此规则,默认会拦截所有路径

.addPathPatterns("/faq/**");

//添加排除拦截规则,如果存在此规则,在此规则下的路径不会被拦截

.excludePathPatterns("/auth/**")

super.addInterceptors(registry);

}

}

```

SpringBoot的拦截器匹配逻辑可以查看源码`MappedInterceptor`中的`matcher`方法

```java

public boolean matches(String lookupPath, PathMatcher pathMatcher) {

PathMatcher pathMatcherToUse = this.pathMatcher != null ? this.pathMatcher : pathMatcher;

String[] var4;

int var5;

int var6;

String pattern;

//如果排除规则不为空

if (this.excludePatterns != null) {

var4 = this.excludePatterns;

var5 = var4.length;

for(var6 = 0; var6 < var5; ++var6) {

pattern = var4[var6];

//匹配到的路径返回false,即不拦截

if (pathMatcherToUse.match(pattern, lookupPath)) {

return false;

}

}

}

//如果拦截规则为空,所有的路径都会返回true,即所有路径都会被拦截

if (ObjectUtils.isEmpty(this.includePatterns)) {

return true;

} else {//如果拦截规则不为空

var4 = this.includePatterns;

var5 = var4.length;

for(var6 = 0; var6 < var5; ++var6) {

pattern = var4[var6];

//匹配到的路径会返回true,即拦截该路径

if (pathMatcherToUse.match(pattern, lookupPath)) {

return true;

}

}

//不匹配拦截规则的路径直接返回false,即不拦截

return false;

}

}

```

### @RequestBody

@RequestBody用来接收前端传递给后端的JSON字符串,需要放在请求体中,因此不能使用get方式提交数据。

**一个请求只能有一个@RequestBody,但可以有多个@RequestParam。**

示例代码

```java

@PostMapping("hello")

public String hello(HttpServletRequest request,@RequestBody(required = true) String jsonParam){

return "Hello RequestBody";

}

```

### @Valid

@Valid用于验证注解是否符合要求,直接加在Controller方法入参中,在变量中添加验证信息的要求,当不符合要求时就在方法中返回message的错误提示信息

示例代码

```java

@PostMapping

public User create (@Valid @RequestBody User user) {

System.out.println(user.getId());

System.out.println(user.getUsername());

System.out.println(user.getPassword());

user.setId("1");

return user;

}

```

在User类中添加验证信息的要求

```java

public class User {

private String id;

@NotBlank(message = "密码不能为空")

private String password;

}

```

`@NotBlank`注解所指的password字段,表示验证密码不能为空,如果为空的话,上面的Controller的create方法会将message中的“密码不能为空”返回。

更多验证注解参考博客:https://blog.csdn.net/weixin_38118016/article/details/80977207

### SpringBoot配置文件数据库密码加密jasypt

jasypt是一个加解密组件,能够配置数据库登录密码、redis登录密码以及第三方的密钥等进行加密。

#### 使用步骤

1、引入maven

```xml

com.github.ulisesbocchio

jasypt-spring-boot-starter

2.1.0

```

2、application.yml增加如下配置

```yaml

#jasypt加密的密匙

jasypt:

encryptor:

password: EbfYkitulv73I2p0mXI50JMXoaxZTKJ7

```

3、测试生成加密后的密钥

```java

@RunWith(SpringRunner.class)

@SpringBootTest

@WebAppConfiguration

public class testTest {

@Autowired

StringEncryptor encryptor;

@Test

public void getPass() {

String url = encryptor.encrypt("jdbc:mysql://47.97.192.116:3306/sell?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2b8");

String name = encryptor.encrypt("你的数据库名");

String password = encryptor.encrypt("你的数据库密码");

System.out.println(url);

System.out.println(name);

System.out.println(password);

Assert.assertTrue(name.length() > 0);

Assert.assertTrue(password.length() > 0);

}

}

```

加密后的输出结果如下

```shell

3OW8RQaoiHu1DXfDny4FDP0W5KOSVcWN5yWNxQ6Q4UE=

ITE8wJryM8hVnofDKQodFzPZuPpTaMtX71YDoOTdh0A=

```

4、将生成的name和password替换配置文件中的数据库账户和密码,如下

```yaml

spring:

#数据库相关配置

datasource:

driver-class-name: com.mysql.jdbc.Driver

#这里加上后缀用来防止mysql乱码,serverTimezone=GMT%2b8设置时区

url: ENC(i87lLC0ceVq1vK91R+Y6M9fAJQdU7jNp5MW+ndLgacRvPDj42HR8mUE33uFwpWqjOSuDX0d1dd2NilrnW7yJbZmoxuJ3HmOmjwY5+Vhu+e3We4QPDVCr/s/RHsQgYOiWrSQ92Mjammnody/jWI5aaw==)

username: ENC(3OW8RQaoiHu1DXfDny4FDP0W5KOSVcWN5yWNxQ6Q4UE=)

password: ENC(ITE8wJryM8hVnofDKQodFzPZuPpTaMtX71YDoOTdh0A=)

#jasypt加密的密匙

jasypt:

encryptor:

password: EbfYkitulv73I2p0mXI50JMXoaxZTKJ7

```

`ENC()`是固定写法,()里面是加密后的信息

参考博客:https://www.jianshu.com/p/b047ed4a8dfa

## Http

### post和put请求的区别

`幂等性`

通俗来说是指不管进行多少次重复操作,都是实现相同的结果

get、put、delete都是`幂等性操作`,而post不是

参考博客:https://blog.csdn.net/qq_33223761/article/details/83511628

### Ajax发送put和delete请求的两种方式

#### 1、采用post+_method:delete/put+filter的方式

ajax发送put和delete请求

- 如果参数在url地址上,没问题

- 如果在data中传参,则需要修改:

- type/method设置为`post`

- data中加入`_method="DELETE"`或`_method="PUT"`参数,如:`data:{_method:"DELETE", id:issueId,userId:userId}`

- 配置filter,SpringBoot则不需要

```xml

HiddenHttpMethodFilter

org.springframework.web.filter.HiddenHttpMethodFilter

HiddenHttpMethodFilter

springmvc

```

#### 2、仍然使用put/delete请求

- data参数转换成`JSON字符串`

```javascript

$.ajax({

url:"http://localhost:8885/answer",

type:"DELETE",

contentType:"application/json",//设置请求参数类型为json字符串

data:JSON.stringify(jsonstr),//将json对象转换成json字符串发送

dataType:"json",

success:function(result){

}

});

```

- 后台controller方法使用`@RequestBody`标注入参

```java

@RequestMapping(value="/answer",method=RequestMethod.DELETE)

public ResponseResult deleteAnswer(@RequestBody Issue issue){}

```

参考博客:https://blog.csdn.net/liuyuanjiang109/article/details/78972644

## Git

### 撤销

撤销操作的常用几个命令

```shell

git commit --amend #撤销上一次提交 并将暂存区文件重新提交

git checkout -- #拉取暂存区文件 并将其替换成工作区文件

git reset HEAD -- #拉取最近一次提交到版本库的文件到暂存区 改操作不影响工作区

```

### 更新分支

gitlab新建分支,本地更新分支

```shell

git pull

```

参考博客:

https://blog.csdn.net/qq_36431213/article/details/78858848

https://blog.csdn.net/qq_32575047/article/details/85015289

### git stash

保存未提交的修改(工作区和暂存区)到堆栈中,用于后续恢复到当前工作目录

```shell

git stash #保存

git stash save 'message' #保存并添加注释

```

查看当前stash中的内容

```shell

git stash list

```

将stash中保存的内容弹出应用到当前分支对应的工作目录上,保存的内容会被删除,只能恢复一次

```shell

git stash pop stash@{num} #num是可选项,可通过git stash list查看具体值

```

将stash中保存的内容应用到当前分支对应的工作目录上,保存的内容不会被删除,可以恢复多次

```shell

git stash apply stash@{num} #num是可选项,可通过git stash list查看具体值

```

删除某个保存

```shell

git stash drop stash@{num} #num是可选项,可通过git stash list查看具体值

```

清除所有的保存

```shell

git stash clear

```

查看堆栈中最新保存的stash和当前目录的差异

```shell

git stash show

```

参考博客:https://blog.csdn.net/stone_yw/article/details/80795669

## Maven

### 编译打包时忽略测试用例

跳过测试阶段

```shell

mvn package -DskipTests

```

临时性跳过测试代码的编译(常用)

```shell

mvn package -Dmaven.test.skip=true

```

更多相关命令,参考博客:https://www.cnblogs.com/mrChangChang/p/11633704.html

## Tomcat

### 两种运行模式

**BIO**:传统的Java I/O操作,同步且阻塞IO,默认模式,配置:

```xml

protocol="HTTP/1.1"

```

**NIO**:JDK1.4开始支持,同步阻塞或同步非阻塞IO,指定使用NIO模型来接受http请求:

```xml

protocol="org.apache.coyote.http11.Http11NioProtocol"

```

### 三种部署方式

1、直接将Web项目放在webapps下,Tomcat会自动将其部署

2、在`server.xml`文件上配置``节点,设置相关的属性

3、通过Catalina来进行配置:进入到`conf\Catalina\localhost`文件下,创建一个xml文件,该文件的名字就是站点的名字,编写xml的方式来编写进行配置

### 内存调优

内存方式的设置是在catalina.sh中,调整一下JAVA_OPTS变量即可,因为后面的启动参数会把JAVA_OPTS作为JVM的启动参数来处理,具体设置如下:

```shell

JAVA_OPTS="$JAVA_OPTS -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4"

```

`-Xmx3550m`:设置JVM最大可用内存

`-Xms3550m`:设置JVM初始内存,此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存

`-Xmn2g`:设置年轻代大小为2G ,整个堆的大小=年轻代大小+年老代大小+持久代大小

`-Xss128k`:设置每个线程的堆栈大小

`-XX:NewRatio=4`:设置年轻代中伊甸区与幸存者区的大小比值

`-XX:MaxPermSize=16m`:设置持久代大小为16m

`-XX:MaxTenuringThreshold=0`:设置垃圾最大年龄(GC存活次数达到一个值就进入老年代),如果设置为0,则年轻代对象不经过幸存者区,直接进入老年代

一键复制

编辑

Web IDE

原始数据

按行查看

历史

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值