利用husky和commitlint约束git提交

依赖工具

husky

Git hooks 工具, 可以在执行 git 命令时,执行自定义的脚本程序

commitlint

检测 git commit 内容是否符合定义的规范,只有规范的 commit message 才能提交

工具安装(推荐全局)

前置条件:npm

npm install -g husky

npm install -g @commitlint/cli @commitlint/config-conventional

项目内配置

Windows(使用powershell)

1
2
3
husky

"module.exports = {extends: ['@commitlint/config-conventional']};" | Out-File -Encoding UTF8 commitlint.config.js

项目目录下修改文件(命令行会出现乱码字符)

目录:.\.husky\commit-msg

内容:npx --no -- commitlint --edit

Mac

1
2
3
4
5
husky

echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js

echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg

使用效果

image

idea中也同样会报错

image

特殊情况跳过验证

增加-n参数

image

阿里云logtail采集容器内日志到sls导致容器内存增加

现象

nginx容器接入logtail后,因为服务请求量较大导致日志量快速增加,很快就触发了内存告警,甚至触发容器伸缩
查看内存使用如图所示(单位:GB,统计参数:container_memory_working_set_bytes)
2022/08/20220829175653

排查过程

略去一大波nginx参数配置的苦逼历程...
最终排查后发现nginx使用内存并不多,而大部分内存是被pagecache占用。因为logtail会读文件,文件会占用pagecache,这时候系统释放是比较慵懒的,所以pagecache越用越多,这部分内存随时都可以释放,但是在容器里,这是不合理的

随后进行了一些操作测试对内存占用的影响

  1. 清理容器内日志
  2. 宿主机上清理pagecache

内存清理情况如下图
2022/08/20220829191705

解决

虽然在宿主机增加定时脚本清理pagecache最省力,但是影响范围过大,不是很合理。最终采用容器内增加定时清理日志脚本解决,效果如下图
2022/08/20220829192127

k8s下php定时任务接入阿里云SchedulerX

背景

php上阿里云ack(k8s)后,想保留原来定时任务,但是又需要保持单点运行,所以考虑使用阿里云SchedulerX来方便管理。
官方文档如下:
2022/05/20220509193421
虽然官方文档显示可以用Sidecar方式接入,但是实际并非进入到目标容器执行,仅能执行http请求到目标容器。

实现

使用agent方式接入,直接打包入php镜像,缺点就是需要java运行环境,镜像会很大并且占用运行内存。建议有定时任务需求才使用此镜像。
链接地址:https://help.aliyun.com/document_detail/149969.html
然后在Deployment配置文件上增加env

1
2
3
4
5
6
7
8
9
10
11
12
13
# spec > template > spec > containers
- name: php-fpm
env:
- name: "SCHEDULERX_ENDPOINT"
value: "${SCHEDULERX_ENDPOINT}"
- name: "SCHEDULERX_NAMESPACE"
value: "${SCHEDULERX_NAMESPACE}"
- name: "SCHEDULERX_GROUPID"
value: "${SCHEDULERX_GROUPID}"
- name: "SCHEDULERX_APPKEY"
value: "${SCHEDULERX_APPKEY}"
- name: "SCHEDULERX_STARTER_MODE"
value: "sidecar"

部分dockerfile

php5.6-fpm

1
2
3
4
5
6
7
8
9
10
11
FROM php:5.6-fpm

# SchedulerX Agent
RUN mkdir -p /usr/share/man/man1 \
&& apt-get install -y openjdk-8-jdk wget \
&& cd /opt \
&& wget https://schedulerx2.oss-cn-hangzhou.aliyuncs.com/agent/schedulerxAgent-1.4.2.tar.gz \
&& tar -xf schedulerxAgent-1.4.2.tar.gz \
&& rm schedulerxAgent-1.4.2.tar.gz \
&& mkdir -p /root/logs/schedulerx \
&& sed -i '45c \ \ \ \ \ \ \ \ sed -i "4c appKey=${SCHEDULERX_APPKEY}" ${WORKER_CONF_DIR}/agent.properties' /opt/schedulerxAgent/bin/start-200m.sh

php7.4-fpm

1
2
3
4
5
6
7
8
9
10
FROM php7.4-fpm

# SchedulerX Agent
RUN apt-get install -y openjdk-11-jdk wget \
&& cd /opt \
&& wget https://schedulerx2.oss-cn-hangzhou.aliyuncs.com/agent/schedulerxAgent-1.4.2.tar.gz \
&& tar -xf schedulerxAgent-1.4.2.tar.gz \
&& rm schedulerxAgent-1.4.2.tar.gz \
&& mkdir -p /root/logs/schedulerx \
&& sed -i '45c \ \ \ \ \ \ \ \ sed -i "4c appKey=${SCHEDULERX_APPKEY}" ${WORKER_CONF_DIR}/agent.properties' /opt/schedulerxAgent/bin/start-200m.sh

php阿里云oss-sdk上传失败情况处理

现象

oss上传没有成功,但是拿到了地址,导致访问时返回404状态码

思路

我们先看自己写的上传oss部分代码

1
2
3
4
5
6
public function oss(string $file, string $object)
{
$ossClient = new OssClient($this->config['accessKeyId'], $this->config['accessKeySecret'], $this->config['endpoint']);
$result = $ossClient->uploadFile($this->config['bucket'], ltrim($object, '/'), $file);
return $result['oss-request-url'];
}

按理说是由sdk返回的result中拿到的地址,上传失败应该会直接抛出异常
再看其中uploadFile代码部分

1
2
3
4
5
6
7
public function uploadFile($bucket, $object, $file, $options = NULL)
{
// ...省略代码
$response = $this->auth($options);
$result = new PutSetDeleteResult($response);
return $result->getData();
}

其中主要方法是$this->auth($options),再进去看

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
private function auth($options)
{
// ...省略代码

try {
$request->send_request();
} catch (RequestCore_Exception $e) {
throw(new OssException('RequestCoreException: ' . $e->getMessage()));
}
$response_header = $request->get_response_header();
$response_header['oss-request-url'] = $requestUrl;
$response_header['oss-redirects'] = $this->redirects;
$response_header['oss-stringtosign'] = $string_to_sign;
$response_header['oss-requestheaders'] = $request->request_headers;

$data = new ResponseCore($response_header, $request->get_response_body(), $request->get_response_code());
//retry if OSS Internal Error
if ((integer)$request->get_response_code() === 500) {
if ($this->redirects <= $this->maxRetries) {
//Sets the sleep time betwen each retry.
$delay = (integer)(pow(4, $this->redirects) * 100000);
usleep($delay);
$this->redirects++;
$data = $this->auth($options);
}
}

$this->redirects = 0;
return $data;
}

可以看到抛异常情况只在send_request方法,而里面只在curl本身无法请求(比如host无法解析)才会抛出RequestCore_Exception异常
也就是说如果是业务类的错误是会正常走下去的
虽然response_code为500时会有重试,但是在这之前已经创建了response对象
如果非500错误,或者超过重试次数都会返回这个对象,最终情况就是不管怎么样都会返回result结构体

解决

在拿到result结构体后先进行response_code判断,代码如下

1
2
3
4
5
6
7
8
9
public function oss(string $file, string $object)
{
$ossClient = new OssClient($this->config['accessKeyId'], $this->config['accessKeySecret'], $this->config['endpoint']);
$result = $ossClient->uploadFile($this->config['bucket'], ltrim($object, '/'), $file);
if ($result['info']['http_code'] !== 200) {
throw new OssException('上传失败');
}
return $result['oss-request-url'];
}

补充

查阅了官方示例也没有对这些情况进行处理,示例如下:
地址:https://help.aliyun.com/document_detail/88473.html

1
2
3
4
5
6
7
8
9
10
11
// ...省略代码
try{
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);

$ossClient->uploadFile($bucket, $object, $filePath);
} catch(OssException $e) {
printf(__FUNCTION__ . ": FAILED\n");
printf($e->getMessage() . "\n");
return;
}
print(__FUNCTION__ . "OK" . "\n");

附上文档中其他错误码说明,文档链接:https://help.aliyun.com/document_detail/31978.html#section-orz-dlw-bz

错误码 HTTP状态码 描述
MissingContentLength 411 请求头没有采用chunked encoding编码方式,或没有设置Content-Length参数。
InvalidEncryptionAlgorithmError 400 指定x-oss-server-side-encryption的值无效。取值:AES256、KMS或SM4。
AccessDenied 403 添加Object时,用户对设置的Bucket没有访问权限。
NoSuchBucket 404 添加Object时,设置的Bucket不存在。
InvalidObjectName 400 传入的Object key长度大于1023字节。
InvalidArgument 400 返回该错误的可能原因如下:添加的Object大小超过5 GB。x-oss-storage-class等参数的值无效。
RequestTimeout 400 指定了Content-Length,但没有发送消息体,或者发送的消息体小于指定的大小。此种情况下服务器会一直等待,直至请求超时。
Bad Request 400 在请求中指定Content-MD5后,OSS会计算所发送数据的MD5值,并与请求中Conent-MD5的值进行比较。如果二者不一致,则返回该错误。
KmsServiceNotEnabled 403 x-oss-server-side-encryption指定为KMS,但没有预先购买KMS套件。
FileAlreadyExists 409 当请求的Header中携带x-oss-forbid-overwrite=true时,表示禁止覆盖同名文件。如果同名文件已存在,则返回该错误。
FileImmutable 409 Bucket中的数据处于被保护状态时,如果尝试删除或修改相应数据,则返回该错误。

nginx vue前后端分离配置示例

vue + thinkphp

vue为history路由模式,固定/api,/static前缀为php使用

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
server {
listen 80;
server_name localhost;

set $static_root ''; # web目录
set $php_root ''; # php目录
root $static_root;

index index.html index.htm index.php;

location ~ \.php$ {
root $php_root;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 128k;
fastcgi_buffers 32 32k;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
}

location / {
try_files $uri $uri/ /index.html;
}

location ^~ /api/ {
root $php_root;
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
break;
}
}

location ^~ /static/ {
root $php_root;
access_log off;
}

# 首页不缓存
location = /index.html {
add_header Cache-Control no-cache;
add_header Pragma no-cache;
add_header Expires 0;
}

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 12h;
log_not_found off;
access_log off;
}
}

vue + 端口转发

二级目录可使用此方法

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
server {
listen 80;
server_name localhost;
root '';
index index.html index.htm;

location ^~ /api/ {
proxy_set_header HOST $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:8080;
# proxy_pass http://localhost:8080/; #转发时想去除/api前缀可使用这条
}

location / {
try_files $uri $uri/ /index.html;
}

# 首页不缓存
location = /index.html {
add_header Cache-Control no-cache;
add_header Pragma no-cache;
add_header Expires 0;
}

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 12h;
log_not_found off;
access_log off;
}
}