晨曦's Blog

This is a window to the soul

很少在Mac上调试Shell,今天在Mac调试Shell的时候发现sed的用法还有点不一样,报错:

1
sed: 1: "02.data.sql": invalid command code .

解决

-i后面增加''变成-i ''即可

Mac

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash

public_domain="auth.xxx.com"
private_domain="authconsole.xxx.com"

if [ "$(sed -i '' s/ztsg.xxx.com/${public_domain}/ 02.data.sql)" ]; then
echo "public_domain update success"
fi

if [ "$(sed -i '' s/ztsgconsole.xxx.com/${private_domain}/ 02.data.sql)" ]; then
echo "private_domain update success"
fi

Linux

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash

public_domain="auth.xxx.com"
private_domain="authconsole.xxx.com"

if [ "$(sed -i s/ztsg.xxx.com/${public_domain}/ 02.data.sql)" ]; then
echo "public_domain update success"
fi

if [ "$(sed -i s/ztsgconsole.xxx.com/${private_domain}/ 02.data.sql)" ]; then
echo "private_domain update success"
fi

MacLinux下分别用以上两种写法就可以了

说到写Shell第一反应肯定都是Vim,接下来我们看看如何通过VsCode的插件把VsCode打造成一个Shell IDE

shellman

说到IDE最起码的需要自动提示和补全,这就需要shellman效果如下:

ShellCheck

有了提示和自动补全,还需要校验我们写得代码是否优雅,这就需要ShellCheck语法检验,效果如下:

而且shellcheckshellman是兼容的
shellcheck compatible (99.99%).

shell-format

最后就是格式化插件shell-format,写完代码风格还是需要格式化一下的

在用PythonNodejs做项目的时候我喜欢把项目的路由分文件存放在routers文件夹下,然后在启动文件夹中加载,这样即简洁也好维护,试试如何在Gin中实现

main.go

1
2
3
r := gin.Default()
routers.Router(r) // 路由
r.Run(":8888")

3行代码,Router方法用来加载所有的路由,接下来看Router方法具体实现

routers/router.go

1
2
3
4
5
6
7
8
9
func Router(e *gin.Engine) {
// V1版本
v1 := e.Group("/v1")
{
RouterUser(v1)
// 其他模块路由
}
// V2版本
}

Router方法主要是去把当前下的其他模块的路由加载进来,这里使用了路由组,如果不需要路由组的话去掉即可,接来下看看RouterUser方法具体实现

routers/user.go

1
2
3
4
func RouterUser(e *gin.RouterGroup) {
e.GET("/user/list", controllers.UserList)
e.POST("/user/add", controllers.UserAdd)
}

使用路由组接收的参数是RouterGroup不使用路由组接收的参数是Engine,这里需要注意一下

目录

最后规划下来的目录如下:

1
2
3
4
5
6
7
8
9
10
11
.
├── README.md
├── app
│   └── controllers
│   └── user.go
├── go.mod
├── go.sum
├── main.go
└── routers
   ├── router.go
   └── user.go

这样规划的话,就只需要维护router.go和其他的对应模块自己的路由规则,main.go也就会变得非常的简洁

一个非常简单的需求,需要在ES中去匹配IPV62409:8a10:*段的数据,看看如何查询

实现

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
GET logstash-nginx-*/doc/_search
{
"_source": "remote_ip",
"query": {
"bool": {
"must": [
{
"query_string": {
"fields": [
"remote_ip.keyword"
],
"query": """2409\:8a10\:*""",
"analyzer": "keyword",
"analyze_wildcard": true
}
}
],
"filter": {
"range": {
"@timestamp": {
"gte": "2021-12-14T09:54:00.000000+0800",
"lt": "2021-12-16T09:55:00.000000+0800"
}
}
}
}
}
}

这里的重点是"""2409\:8a10\:*"""也可以写成"2409\\:8a10\\:*",其他的符号也可按此类推即可

最后效果:


ES 相关参考

Python中需要热加载是非常简单的,以Sanic为例只需要设置auto_reload=True,但是在Gin中没有内置相应的功能,这里我们可以利用Go中的一些其他包实现,即更改源码,保存后,自动触发更新,浏览器上刷新即可。免去了杀进程、重新启动之苦

Fresh

试了下github.com/gravityblast/fresh还觉得不错
安装:

1
go get github.com/pilu/fresh

使用:

1
fresh

其他

记录一下其他实现框架
Air:https://github.com/cosmtrek/air
Bee:https://github.com/beego/bee
Realize:https://github.com/oxequa/realize
Gin:https://github.com/codegangsta/gin
gowatch:https://github.com/silenceper/gowatch


题外话:
在安装非项目使用的包时如:fresh,最好是不要在项目根目录下进行go get或者会被写入到项目的go.mod

前记

最近需要实现通过Supervisor来守护Haproxy,折腾了半天记录下实现配置

实现

1
2
3
4
5
6
[program:haproxy]
command=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
process_name=haproxy
autostart=true
autorestart=true
startretries=1

这里的重点是-p /run/haproxy.pid必须要指定pid的路径,要不然没法获取到program状态

类型?

type定义的结构体接口都是类型

导出名

Go中,如果一个名字以大写字母开头,那么它就是已导出的,在导入一个包时,你只能引用其中已导出的名字。任何未导出的名字在该包外均无法访问
类似其他语言PublicPrivate修饰符

命名返回值

Go的返回值可被命名,它们会被视作定义在函数顶部的变量。返回值的名称应当具有一定的意义,它可以作为文档使用。没有参数的return语句返回已命名的返回值。也就是直接返回

短变量声明

函数中,简洁赋值语句:=可在类型明确的地方代替var声明
函数外的每个语句都必须以关键字开始(var, func 等等),因此 :=结构不能在函数外使用

零值

没有明确初始值的变量声明会被赋予它们的零值0false""(空字符串)

常量

使用const关键字声明,常量不能用:=语法声明

defer

defer语句会将函数推迟到外层函数返回之后执行,推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用
推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用可以看做是倒序执行

指针

指针保存了值的内存地址

切片

它会选择一个半开区间,包括第一个元素,但排除最后一个元素
切片并不存储任何数据,它只是描述了底层数组中的一段。更改切片的元素会修改其底层数组中对应的元素。与它共享底层数组的切片都会观测到这些修改

匿名函数与闭包

有了匿名函数,就可以在函数中再定义函数(函数嵌套),定义的这个匿名函数,也可以称为内部函数。更重要的是,在函数内定义的内部函数,可以使用外部函数的变量等,这种方式也称为闭包

方法与函数

方法就是一类带特殊的接收者参数的函数
接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法

选择值或指针作为接收者

使用指针接收者的原因有二:
首先,方法能够修改其接收者指向的值。
其次,这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效
通常来说,所有给定类型的方法都应该有值或指针接收者,但并不应该二者混用

接口

接口类型是由一组方法签名定义的集合
类型通过实现一个接口的所有方法来实现该接口。既然无需专门显式声明,也就没有 “implements” 关键字
隐式接口从接口的实现中解耦了定义,这样接口的实现可以出现在任何包中,无需提前准备
因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义


未完待续…

国内的PYPI源也挺多的,经过实际使用下来,还是觉得华为的PYPI源包的版本最为齐全,推荐用华为的源进行加速

临时使用

运行以下命令使用华为源:

1
pip install -r requirements.txt --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple

或者

1
pip install --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple sanic

通过上面的方式安装单个包

设为默认

Pip的配置文件为用户根目录下的:~/.pip/pip.confWindows路径为:C:\Users\<UserName>\pip\pip.ini), 您可以配置如下内容:

1
2
3
4
[global]
index-url = https://repo.huaweicloud.com/repository/pypi/simple
trusted-host = repo.huaweicloud.com
timeout = 120

其他国内镜像源:

1
2
3
4
5
阿里云:http://mirrors.aliyun.com/pypi/simple/
中国科技大学:https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban):http://pypi.douban.com/simple/
清华大学:https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学:http://pypi.mirrors.ustc.edu.cn/simple/

个人推荐使用华为的Alpine源,通过我的测试华为的源最为齐全

华为

1
sed -i 's/dl-cdn.alpinelinux.org/repo.huaweicloud.com/g' /etc/apk/repositories

阿里

1
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

科大

1
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

清华

1
sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories

Docker

1
RUN sed -i 's/dl-cdn.alpinelinux.org/repo.huaweicloud.com/g' /etc/apk/repositories

最近开发Python一直在用Sanic,感觉还不错;看看如何将Sanic部署到 Docker中并用Supervisor来守护

Dockerfile

这里利用Python 3.9来构建基础镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM python:3.9-alpine

RUN sed -i 's/dl-cdn.alpinelinux.org/repo.huaweicloud.com/g' /etc/apk/repositories

RUN apk update \
&& apk add --no-cache build-base \
&& apk add --no-cache libffi-dev

RUN mkdir /usr/src/app

COPY . /usr/src/app/

COPY ./supervisor/supervisord.conf /etc/supervisord.conf

WORKDIR /usr/src/app
ENV PYTHONPATH /usr/src/app

RUN pip3 install supervisor \
&& pip3 install -r requirements.txt --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple

CMD ["supervisord", "-c", "/etc/supervisord.conf"]

这里利用华为的alpine源和pypi

Supervisor

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
[unix_http_server]
file=/var/run/supervisor.sock

[supervisord]
logfile=/var/log/supervisord.log
logfile_maxbytes=50MB
logfile_backups=10
loglevel=info
pidfile=/var/run/supervisord.pid
nodaemon=true
silent=false
minfds=1024
minprocs=200

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock

[program:sanic]
user = root
directory=/usr/src/app
command=sanic server.app --host=0.0.0.0 --port=8888 --workers=4
stdout_logfile_backups=20
stdout_logfile_maxbytes=50MB
stdout_logfile = /var/log/sanic.log
stderr_logfile = /var/log/sanic.err
autostart=true

Supervisor 配置参考


题外话:

容器运行Supervisor记得设置nodaemon=true,或则会出现Unlinking stale socket /var/run/supervisor.sock

因为Supervisor默认是deamon模式,启动命令结束后Supervisor会在后台运行,而容器运行启动命令返回0后自己关闭了,导致一直出现无法运行的现象

0%