0%

前言

在 Window10 上安装 Linux 已经很方便了,但是很多镜像是在 C:\Users\maxzhao\AppData\Local\Docker目录下的。

修改路径

1
2
3
4
5
# 关闭 Docker 服务
cd C:\Users\maxzhao\AppData\Local\Docker
# 把 C:\Users\maxzhao\AppData\Local\Docker 复制到 D:\Docker
mklink /J Docker D:\Docker
# 开启Docker服务

maxzhao为当前用户名

本文地址: https://github.com/maxzhao-it/blog/post/2660/

前言

在 Window10 上安装 Linux 已经很方便了,下面简单记录一下安装 WSL2

安装

开启电脑的虚拟化支持

需要在 BIOS 中设置 vm 开启

开启 Win10 子系统

1
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

开启 Win10 虚拟化

1
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

下载 Linux Kernel

WSL2 Linux kernel update package for x64 machines

下载上面系统然后直接安装。

ARM64 版本点击这里下载 ARM64 package

把WSL2 设置为默认

1
wsl --set-default-version 2

安装 Linux 系统

打开 Microsoft Store ,搜索想要安装的系统

下面一些系统链接

我这里安装的 Kail Linux

运行安装的 Linux 系统

第一次运行会安装,有如下输出

1
Installing, this may take a few minutes..

安装结束后需要配置名称

安装结束

安装 Window 终端

Windows终端支持多个选项卡(在多个Linux命令行、Windows命令提示符、PowerShell、Azure CLI等之间快速切换),创建自定义键绑定(打开或关闭选项卡的快捷键,复制粘贴等),使用搜索功能,和自定义主题(配色方案、字体样式和大小、背景图像/模糊/透明)。

Install Windows Terminal.

本文地址: https://github.com/maxzhao-it/blog/post/61214/

Java

下载:VSCode

下载插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"java.configuration.runtimes": [
{
"name": "JavaSE-1.8",
"path": "/path/to/jdk-8",
},
{
"name": "JavaSE-11",
"path": "/path/to/jdk-11",
},
{
"name": "JavaSE-17",
"path": "/path/to/jdk-17",
"default": true
},
]

代码提示

IntelliCode

SpringBoot支持

下载:

spring-boot

spring-initializr

spring-boot-dashboard

boot-dev-pack

java-pack

SQLTools支持

下载:

SQLTools

Markdown支持

下载:

HTML\CSS支持

下载:

HTML\CSS

HTML CSS Support

自动补全:Auto Close Tag

同步修改闭合标签:Auto Rename Tag

路径提示:Path Intellisense

Open in Browser

自动刷新:Live Server

ESLint

ESLint

Debugger for Chrome

Debugger for Chrome

Git 增强

Vue

npm

PowerShell

下载:

PowerShell

主题

One Dark Pro

Docker

Docker

Angular Language Service

Angular Language Service

简体中文

Chinese (Simplified)

Microsoft Edge Tools

Debugger for Firefox

IntelliJ IDEA Keybindings

Dart

ES7+ React/Redux/React-Native snippets

rest client

本文地址: https://github.com/maxzhao-it/blog/post/10bb53e3/

前言

背景

在持久层使用 MyBatisPlus 的项目开发过程中,开发人员会使用 listselectListMyBatisPlus 自带函数根据一定条件查询全表数据,由于代码的健壮性不强,部分查询条件不生效时 listselectList 等函数的查询结果集合过大。

目的

限制 MyBatisPlus 非分页查询情况下查询的数据量,提高项目稳定性。

思路

  1. MyBatisPlus 中执行的非分页查询 SQL 进行拦截,添加默认最大查询数据量。
  2. 提供符合 MyBatisPlus规范的 BigPage(允许自定义分页查询的最大数据量),供大数据量查询的业务。
  3. MyBatisPlus 分页查询,设置全局单页最大数据量。
  4. 查询结果集合等于设置的最大数据量时,打印 warn 级别的日志提醒或抛出异常(可配)。

限制

支持版本

  1. MyBatisPlus 3.4.x
  2. MyBatisPlus 3.5.x

开发限制

  1. 必须使用 MyBatisPlus 分页插件。
  2. 只能对 MyBatisPlus 语法下的 SQL 进行拦截,比如Service函数、Mapper函数、动态SQL。(自定义实现的 Mapper 函数,暂不考虑兼容测试
  3. 自定义 SQL 中的分页数据量不能控制(业务相关,不方便兼容)。
  4. 如果因为“程序错误”导致查询数据量过大的问题,比较难以排查。

自定义 SQL 中的分页 与 IPage 一同使用会报错,MyBatisPlus认定为代码逻辑上的错误,这里不做处理。

数据库

兼容

  • DB2
  • Dm
  • MySQL(已测试)
  • Oracle12c
  • Oracle
  • Postgre
  • SQLServer

理论上兼容(未测试)

  • Gauss(Oracle)
  • GBase(MySQL)
  • H2(Postgre)
  • HSQL(Postgre)
  • Kingbase(Postgre)
  • MariaDB(MySQL)
  • Oscar(MySQL)
  • Phoenixt(Postgre)
  • SQLite(Postgre)
  • XuGu(MySQL)

未兼容(不考虑实现)

  • SQLServer2005
  • Sybase

实现

1、全表查询SQL限制最大查询数据量

配置最大查询数量

1
2
3
4
5
6
7
8
9
10
maxzhao:
mybatis-plus:
# 最大查询条数限制
limit:
# 开启限制(默认开启)
enabled: true
# 分页查询最大条数限制(默认 1w)
maxPageLimit: 10000
# 全表查询最大条数限制(默认 10w)
maxListLimit: 100000

自定义配置分页拦截器

自定义MyBatisPlus分页拦截器。

分页拦截器需要做的是在查询之前拦截并处理SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
start=>start: 查询之前
end=>end: 开始查询
nodo=>end: 不做处理
是否包含分页参数=>condition: 是否包含分页参数
SQL中是否包含限制属性=>condition: SQL中是否包含限制属性
构建默认分页参数=>operation: 构建默认分页参数
构建分页SQL=>operation: 构建分页SQL
start->是否包含分页参数
是否包含分页参数(yes)->构建分页SQL
是否包含分页参数(no)->SQL中是否包含限制属性
SQL中是否包含限制属性(yes)->nodo
SQL中是否包含限制属性(no)->构建默认分页参数->构建分页SQL
构建分页SQL->end

分页参数:指的是主动设置的 IPage

SQL中是否包含限制属性:指的是动态SQL中是否包含用于限制查询条数的关键字。

MyBatisPlus分页拦截器:是对 MyBatis 拦截器的一层封装。

注入分页

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
/**
* 添加 MyBatisPlus 最大查询数据量限制 <br>
* 默认开启
*
* @author zhaoliansheng
* @since 2022-06-29 9:55
*/
@Configuration
@ConditionalOnProperty(value = "maxzhao.mybatis-plus.limit.enabled", matchIfMissing = true)
@ConditionalOnClass(value = MybatisPlusInterceptor.class)
public class MybatisPlusConfig {
private static final Logger log = LoggerFactory.getLogger(MybatisPlusConfig.class);
/**
* 分页查询最大条数限制 <br>
* 默认1w
*/
@Value(value = "${maxzhao.mybatis-plus.limit.maxPageLimit:0}")
private Long maxPageLimit;
/**
* 全表查询最大条数限制 <br>
* 默认10w
*/
@Value(value = "${maxzhao.mybatis-plus.limit.maxListLimit:0}")
private Long maxListLimit;

/**
* 配置 MyBatisPlus 分页插件
*/
@Order(9)
@Bean
@ConditionalOnMissingBean(MybatisPlusInterceptor.class)
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationForListInnerInterceptor paginationForListInnerInterceptor = new PaginationForListInnerInterceptor();
/*分页查询最大数据量限制*/
paginationForListInnerInterceptor.setMaxLimit(maxPageLimit);
/*全表查询最大数据了限制*/
paginationForListInnerInterceptor.setMaxListLimit(maxListLimit);
interceptor.addInnerInterceptor(paginationForListInnerInterceptor);
log.info("已开启最大条数限制:分页查询单页数据量限制:{} ;全表查询数据量限制:{}", maxPageLimit, maxListLimit);
return interceptor;
}

/*****************************/
}

2、BigPage实现

MyBatisPlus原分页实现类 com.baomidou.mybatisplus.extension.plugins.pagination.Page 没有 maxLimit 属性的默认值,但是我们可以通过继承 Page 来实现 BigPage,并且给与一个较大的默认值(100W),如果超过这个默认值,我们认为这个业务很可能存在问题(业务上真的超过100W数据量的限制,setMaxLimit就可以了)。

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

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import java.util.Optional;

/**
* MyBatisPlus 大数据量的分页查询
*
* @author zhaoliansheng
* @since 2022-06-29 10:38
*/
public class BigPage<T> extends Page<T> {
/**
* 默认 100W 数据量
*/
private static final long MAX_LIMIT = 1000000L;

/*****************************/

/**
* 获取限制数据量值的时候判断是否需要给与默认值
*/
@Override
public Long maxLimit() {
this.maxLimit = Optional.ofNullable(this.maxLimit).orElse(MAX_LIMIT);
return this.maxLimit;
}

/**
* 获取限制数据量值的时候判断是否需要给与默认值
*/
@Override
public Long getMaxLimit() {
this.maxLimit = Optional.ofNullable(this.maxLimit).orElse(MAX_LIMIT);
return this.maxLimit;
}
}

3、MyBatisPlus 分页查询,设置全局单页最大数据量。

配置单页最大数据量

1
2
3
4
5
6
7
8
9
10
maxzhao:
mybatis-plus:
# 最大查询条数限制
limit:
# 开启限制(默认开启)
enabled: true
# 分页查询最大条数限制(默认 1w)
maxPageLimit: 10000
# 全表查询最大条数限制(默认 10w)
maxListLimit: 100000

MybatisPlusInterceptor 注入分页拦截器时,设置分页拦截器的单页查询最大数据量。

4、查询数据量等于最大限制时的处理

目前有两种推荐的处理方式:

  1. 打印 warn 级别的日志提醒。
  2. 抛出异常。

这两种实现方式可以动态配置。

需要重写 com.baomidou.mybatisplus.core.override.MybatisMapperMethod暂时不做处理

本文地址: https://github.com/maxzhao-it/blog/post/4e95394/

前言

Log4j2对接ELK

  1. Log4j2 使用 Socketlogstash 推送数据
  2. logstash经过过滤后,把数据保存到 ElasticSearch 中。
  3. log4j2使用socket推送日志到 logstash

环境信息:

  • 部署路径:/opt/logstash
  • Log4j2 对接方式:Socket
  • 部署地址:192.168.2.8
  • logstash建议使用 grok插件

grok是一个十分强大的logstash filter插件,他可以通过正则解析任意文本,将非结构化日志数据解析成结构化和方便查询的结构。

在线测试 官方pattern 插件安装

安装插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看
/opt/logstash/bin/logstash-plugin list
# 在线安装
/opt/logstash/bin/logstash-plugin install logstash-filter-grok
vim /opt/logstash/Gemfile
# 更新
/opt/logstash/bin/logstash-plugin update logstash-filter-grok
mkdir /opt/logstash/plugins
cd /opt/logstash/plugins
wget https://github.com/logstash-plugins/logstash-filter-grok/archive/refs/tags/v4.4.2.tar.gz
tar -zxvf v4.4.2.tar.gz
vim /opt/logstash/Gemfile
# 写入
# gem "logstash-filter-grok", :path => "path"

配置

logstash

配置 PIP 文件

完成实例

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
cat > /opt/logstash/config/app-log4j2.conf <<EOF
input {
tcp {
host =>"192.168.2.8"
port => 9699
codec => json {
charset => "UTF-8"
}
}
stdin{}
}
filter{
json{
source => "message"
}
}
output {
elasticsearch {
#action => "index"
#manage_template => false
hosts => "192.168.2.8:9200"
index => "app-logstash"
#document_type => "logstash"
}
stdout { codec=> rubydebug }
}
EOF

配置 pipelines.yml(可选)

vim /opt/logstash/config/pipelines.yml

1
2
- pipeline.id: app-log4j2-processing
path.config: "/opt/logstash/config/app-log4j2.conf"

配置 logstash.yml(可选)

1
path.config: "/opt/logstash/config/piptlines.yml"

启动logstash

1
2
# 如果 pipelines.yml 与 logstash.yml 没有配置,需要命令行中主动添加配置
/opt/logstash/bin/logstash -f /opt/logstash/config/app-log4j2.conf

项目

log4j2.yml

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
Configuration:
status: warn
Appenders:
Console:
name: CONSOLE
target: SYSTEM_OUT
PatternLayout:
pattern: "%clr{%d{yyyy-MM-dd HH:mm:ss,SSS}}{faint}:%clr{%-6p} %clr{${sys:PID}}{magenta} [thread-%T] %clr{[%t]}{faint} %clr{%-40.40c{1.}:}{cyan} %m%n%xwEx"
Socket:
- name: LOGSTASH
host: 192.168.2.8
port: 9699
protocol: TCP
thresholdFilter:
level: INFO
# 大于等于 info 放行
onMatch: ACCEPT
# 低于 info 拦截
onMismatch: DENY
PatternLayout:
pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS}:%-6p ${sys:PID} [thread-%T] [%t] %-40.40c{1.}: %m%n%xwEx"
Loggers:
Root:
level: INFO
AppenderRef:
- ref: CONSOLE
Logger:
- name: com.skytech
additivity: false
level: DEBUG
AppenderRef:
- ref: CONSOLE
- ref: LOGSTASH

异步日志

依赖

1
2
3
4
5
 <dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>

Springboot配置log4j2.component.properties

1
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

https://logging.apache.org/log4j/2.x/manual/async.html

app-log4j2.conf配置

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
input { 
tcp {
host =>"192.168.2.8"
port => 9699
codec => "plain"
}
}
filter{
if "c.s.s.l.s.f.StorageApiLogFileServiceImpl" in [message] {
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:logDate}:%{LOGLEVEL:logLevel}%{SPACE}%{INT:pid}%{SPACE}\[%{WORD}-%{INT:thread}\]%{SPACE}\[%{WORD}-%{WORD}-%{INT}-%{WORD}-%{INT}\]%{SPACE}(?<targetClass>.*):%{SPACE}接口日志信息:(?<logData>.*)"
}
}
json {
source => "logData"
target => "logDataJson"
remove_field => ["server","timestamp"]
}
mutate {
add_field => {
"id" => "%{[logDataJson][id]}"
"optType" => "%{[logDataJson][optType]}"
"optTime" => "%{[logDataJson][optTime]}"
"trackId" => "%{[logDataJson][trackId]}"
"spendTime" => "%{[logDataJson][spendTime]}"
"optUser" => "%{[logDataJson][optUser]}"
"optUserName" => "%{[logDataJson][optUserName]}"
"otherValue" => "%{[logDataJson][otherValue]}"

"optName" => "%{[logDataJson][optName]}"
"optDesc" => "%{[logDataJson][optDesc]}"
"apiMapping"=> "%{[logDataJson][apiMapping]}"
"optMethod" => "%{[logDataJson][optMethod]}"
"sourceIp" => "%{[logDataJson][sourceIp]}"
"successFlag" => "%{[logDataJson][successFlag]}"
"requestHeaders" => "%{[logDataJson][requestHeaders]}"
"requestParams" => "%{[logDataJson][requestParams]}"
"requestBody" => "%{[logDataJson][requestBody]}"
"responseHeaders" => "%{[logDataJson][responseHeaders]}"
"responseBody" => "%{[logDataJson][responseBody]}"
}
}
if([methodParams]){
mutate {
# opt
add_field => {
"methodParams" => "%{[logDataJson][methodParams]}"
"methodResponse" => "%{[logDataJson][methodResponse]}"
}
}
}
if([authProcess]){
mutate {
# sso
add_field => {
"authType" => "%{[logDataJson][authType]}"
"authProcess" => "%{[logDataJson][authProcess]}"
"authUser" => "%{[logDataJson][authUser]}"
"authStatus" => "%{[logDataJson][authStatus]}"
}
}
}
mutate {
remove_field=>["logData","tags","_source","message","logDataJson","event"]
}
}
if([optTime]) {
date {
match => ["optTime", "yyyy-MM-dd HH:mm:ss"]
target => "@timestamp"
}
}
}
output {
stdout { codec=> rubydebug }
if "接口日志" in [optType] {
elasticsearch {
hosts => "192.168.2.8:9200"
index => "app-log-api-%{+YYYY.MM.dd}"
}
}
if "操作日志" in [optType] {
elasticsearch {
hosts => "192.168.2.8:9200"
index => "app-log-opt-%{+YYYY.MM.dd}"
}
}
if "认证日志" in [optType] {
elasticsearch {
hosts => "192.168.2.8:9200"
index => "app-log-sso-%{+YYYY.MM.dd}"
}
}
}

对应的log4j2.yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Configuration:
Appenders:
Socket:
- name: LOGSTASH
host: 192.168.2.8
port: 9699
protocol: TCP
thresholdFilter:
level: INFO
# 大于等于 info 放行
onMatch: ACCEPT
# 低于 info 拦截
onMismatch: DENY
PatternLayout:
pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS}:%-6p ${sys:PID} [thread-%T] [%t] %c{1.}: %m%n%xwEx"

app-kafka.conf配置

1
2
3
4
5
6
7
8
9
10
11
12

input {
kafka {
group_id => "logstash"
# 此处的topics_pattern => 应该是需要和 filebeat 中配置的topic一致。
topics_pattern => "TOPIC_LOG_API"
# 配置Kafka服务器集群地址,多个用,隔开
bootstrap_servers => "192.168.2.8:9092"
auto_offset_reset => "latest"
codec => "json"
}
}

重启脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat > /opt/script/restart-logstash << EOF
#!/bin/bash
pid=\$(ps -ef|grep /opt/logstash |grep -v grep|awk '{print \$2}')
if [ -z "\$pid" ]
then
echo '/opt/logstash not start '
else
kill -9 \$pid
echo '/opt/logstash shutdown'
fi
cd /opt/
nohup /opt/logstash/bin/logstash -f /opt/logstash/config/app-log4j2.conf >nohup-logstash.log 2>&1 &

echo '/opt/logstash/bin/logstash -f /opt/logstash/config/app-log4j2.conf is start '
tail -f nohup-logstash.log
EOF
chmod +x /opt/script/*
cat /opt/script/restart-logstash

注意:使用 cat 输出到文件中的执行脚本 $ 需要改为 \$

本文地址: https://github.com/maxzhao-it/blog/post/907423512/