alpine镜像设置时区

FROMalpineENV TZ=Asia/ShanghaiRUN echo 'http://mirrors.aliyun.com/alpine/v3.4/main/' > /etc/apk/repositories \ && apk --no-cache add tzdata zeromq \ && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \ && echo '$TZ' > /etc/timezone

2022-03-29 · 1 分钟

构建自己的go-gin-api脚手架(三)

Viper 地址:https://github.com/spf13/viper Viper 是适用于 Go 应用程序的完整配置解决方案。它被设计用于在应用程序中工作,并且可以处理所有类型的配置需求和格式。 文档 英文:可查看 Github 仓库的 README 文件 中文:可查看这篇文章的翻译 安装 go get github.com/spf13/viper 使用 新建配置文件/config/config.toml aaa = "111" bbb = "222" Viper 只需简单设置就可以读取到配置信息。 //设置文件路径 viper.AddConfigPath("/config") //设置文件名 viper.SetConfigName("config") //设置文件类型 viper.SetConfigType("toml") //读取配置文件 _ = viper.ReadInConfig() //监控配置文件变动 viper.WatchConfig() for { //输出所有配置 fmt.Println(viper.AllSettings()) time.Sleep(time.Second) } 多配置文件 随着项目开发,配置文件中的信息可能会越来越多,全部集中到一个文件中会变的难以管理。按功能分为不同的配置文件更易于管理,我们来自己实现一个多配置文件的方法。 方法一 使用MergeInConfig()方法将多个配置文件内容合并到一起。 在新建一个/config/router.toml配置文件 port = 8000 //设置文件路径 viper.AddConfigPath("/config") //设置文件名 viper.SetConfigName("config") //设置文件类型 viper.SetConfigType("toml") //读取配置文件 _ = viper.ReadInConfig() //设置第二个配置文件名称 viper.SetConfigName("router") //读取并合并已有配置 _ = viper....

2022-02-27 · 2 分钟

Dockerfile编写注意事项与技巧

1、多条指令应换行 ...RUN apt-get clean apt-get update...多条指令写在一行会当做一条指令执行,有可能后面的指令不执行或者报错。 应改为: ...RUN apt-get clean \ && apt-get update...2、忽略错误继续运行 我们在使用apt-get安装一个包时,常常会因为缺少依赖而安装失败,我们可以使用apt-get install -y -f --fix-missing命令来安装上一次安装失败所需要的依赖包,可以很方便的管理所以依赖,而不用我们手动按照依赖顺序把所有依赖包安装一遍,但是必须在安装失败后执行。Dockerfile 在构建过程中如果出现报错会立即退出构建,我们可以使用逻辑或||来忽略错误继续执行后面的语句。 ...# 下载Chrome安装包RUN wget -P /tmp https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \ # 安装Chrome失败 && dpkg -i /tmp/google-chrome-stable_current_amd64.deb \ # 安装Chrome的依赖 && apt-get install -y -f --fix-missing \ # 再次安装Chrome && dpkg -i /tmp/google-chrome-stable_current_amd64.deb...上面的写法在 Chrome 安装失败时构建会退出,改为下面的写法就可以成功构建了。 ...# 下载Chrome安装包RUN wget -P /tmp https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \ # 安装Chrome失败之后安装Chrome的依赖 && dpkg -i /tmp/google-chrome-stable_current_amd64.deb || apt-get install -y -f --fix-missing \ # 再次安装Chrome && dpkg -i /tmp/google-chrome-stable_current_amd64....

2022-01-26 · 1 分钟

IDE进入Docker容器输出中文乱码

如下图所示,中文字符输出为乱码。 解决 如下图所示编辑 VM Options ,添加一行 -Dfile.encoding=UTF-8并重启 IDE 。

2022-01-22 · 1 分钟

从零完成一个带滑动验证的自动登陆

前言 最近帮女朋友写了一个监控九价订阅的小程序,对方接口需要登陆,Token 有效时间只有大概 2 个小时,每次都要手动去更新登陆状态,太过麻烦了。所以就想要解决一下自动登陆的问题,登陆有滑动验证码,查找了一些资料决定使用 Python + Selenium 来解决,可惜我也不会 Python ,只能一点点摸索了,顺便记录下来。 了解 Python 因为以前没有使用过 Python ,所以先了解一下 Python 的基本语法。 菜鸟教程 廖雪峰的 Python 教程 配置环境 了解完基础之后先安装一个环境吧,不想在电脑上在安装 Python ,而且完成之后肯定要部署服务器,所以还是使用 Docker 来统一环境。 安装 Python 先简单写一个 docker-compose.yml 的文件,拉取一个 Python 镜像并创建容器。 docker-compose.yml version: "3.7" services: Python: image: Python volumes: - .:/home working_dir: /home tty: true 使用 PyCharm 可以很方便的管理 Docker ,运行 docker-compose.yml 并进入容器。 安装 Chrome 打开 Chrome 官网,滑动到最下面,点击其他平台,然后在弹出窗口选择 Linux,选择适用系统的安装包,获取下载链接,然后进入容器执行以下命令安装 Chrome 。 # 更换apt源 $ sed -i s@/deb....

2022-01-21 · 3 分钟

构建自己的go-gin-api脚手架(二)

在前文中我们已经成功的使用 Gin 框架搭建了一个简易的 API 服务,但是在一个项目中,所有的业务逻辑都写在 main.go 一个文件中显然是不合理的。所以接下来我们会对其进行重构,在整个搭建 go-gin-api 脚手架的过程中,会经常对其重构。 每当我要进行重构的时候,第一个步骤永远相同:我得确保即将修改的代码拥有一组可靠的测试。这些测试必不可少,因为尽管遵循重构手法可以使我避免绝大多数引入 bug 的情形,但我毕竟是人,毕竟有可能犯错。程序越大,我的修改不小心破坏其他代码的可能性就越大——在数字时代,软件的名字就是脆弱。 —— 《重构:改善既有代码的设计》 所以在此之前我们需要编写一套可靠的单元测试,来确保代码重构之后还能良好的运行。 Go 单元测试框架 testing testing 是 Go 标准库中提供的自动化测试支持,通过 go test 命令,能够自动执行如下形式的任何函数: func TestXxx(*testing.T) 注意:Xxx 可以是任何字母数字字符串,但是第一个字母不能是小写字母。 在这些函数中,使用 Error、Fail 或相关方法来发出失败信号。 要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 TestXxx 函数,如上所述。 将该文件放在与被测试文件相同的包中。该文件将被排除在正常的程序包之外,但在运行 go test 命令时将被包含。 有关详细信息,请运行 go help test 和 go help testflag 了解。 标准库的 testing 实现比较简单,并不支持断言,需要写 if 判断。所以不考虑使用。 Testify 地址:https://github.com/stretchr/testify Testify 是基于 testing 编写的,所以测试文件与执行方式与其完全相同,并且支持断言方法。 编写测试 首先创建一个 main_test.go 的文件,写入以下代码并执行 go mod tidy更新依赖。...

2022-01-11 · 3 分钟

构建自己的go-gin-api脚手架(一)

目标 学习并掌握 Go 语言 学习并熟练使用 Gin 框架 学习并熟练使用 Go 热门常用的组件 搭建一个自己开箱即用的 api 框架 锻炼自己的封装抽象能力 初始化 使用 Goland 创建一个新项目go-gin-api。 可以看到项目中有一个 go.mod 的文件。 module go-gin-api go 1.17 Gin 安装 下载并安装 Gin : $ go get -u github.com/gin-gonic/gin go get: added github.com/gin-contrib/sse v0.1.0 go get: added github.com/gin-gonic/gin v1.7.7 go get: added github.com/go-playground/locales v0.13.0 ... 可以看到项目中又多了一个 go.sum 的文件,go.sum 文件详细罗列了当前项目直接或间接依赖的所有模块版本,并写明了那些模块版本的 SHA-256 哈希值以备 Go 在今后的操作中保证项目所依赖的那些模块版本不会被篡改。 go.mod 文件也发生了改变: module go-gin-api go 1.17 require ( github.com/gin-contrib/sse v0....

2022-01-10 · 1 分钟

给Git历史提交添加GPG签名

今天给 commit 添加了 GPG 签名,但是以前的提交记录却没有签名。 使用以下命令 注意:以下命令会修改历史,变更 commit id ,其他人会不同步,慎用。 对历史所有提交增加签名: git filter-branch -f --commit-filter 'git commit-tree -S "$@"' HEAD 对最后 X 次提交增加签名: git filter-branch -f --commit-filter 'git commit-tree -S "$@"' HEAD~X..HEAD 对指定提交者 Email 增加签名: git filter-branch -f --commit-filter ' if [ "$GIT_COMMITTER_EMAIL" = "885046048@qq.com" ] then git commit-tree -S "$@" fi ' 推送到远程 git push -f

2022-01-06 · 1 分钟

如何记录可读性的操作日志?

前言 公司生产环境发生了一个事故,导致公司造成了损失。先说结果是因为业务人员修改参数时设置错了参数,导致事故发生。一开始业务人员认为是我们代码程序出了错误,好在有操作日志记录,于是前去查看发现,操作日志记录不全,没有记录到那个修改的参数。然后在排查代码发现没有问题之后,只能通过查找 MySQL 的 binlog 执行日志与 Nginx 的请求日志作为佐证,才将扣在身上的锅甩了出去。因此我意识到操作日志的重要性。 为什么要记录操作日志? 上文也已经说了,操作日志几乎存在于所有的系统中,尤其是后台管理系统,需要详细的记录下管理员所做出的操作,以便于更好的排查问题与溯源分析。 系统日志与操作日志的区别 系统日志 系统日志主要为了开发更好的排查问题,一般记录在日志文件中。系统日志一般是开发人员会进行查看,所以可读性不需要很高,甚至会包含一些代码信息。 操作日志 操作日志主要是为了记录使用人员的添加、修改、删除操作。一般是给用户看的,所以需要很高的可读性,能让人一眼就看明白操作了什么东西,而不需要再去结合代码进行分析。 常见的日志记录方式 自定义函数 在很多系统中都是定义了一个类似的admin_log()函数,然后在需要记录日志的地方调用这个函数传入记录的信息即可。 优点: 比较灵活,可以随意拼接自己想要记录的日志信息 在想要记录日志的地方只需调用函数就可以了 缺点: 日志的记录穿插在代码中,与代码耦合度很高,增加了代码复杂度 大块的日志信息在代码中看起来很丑 随着代码的修改可能还会需要对日志信息进行修改维护 有可能会忘记日志的记录,导致出了问题背锅 注解+ AOP 在 Java 中使用注解+ AOP 是一种很常见的日志记录方式,网上有很多这样的例子,但是在 PHP 中好像没有见过这种方式。 思考 我们现在的系统中就是使用第一种自定义函数的方式来记录操作日志的,有着很多的缺点,所以就想要寻求一种优雅的记录操作日志的方式。 在美团技术团队的《如何优雅的记录操作日志?》一文中很详细的描写了如何利用注解+AOP 实现操作日志的记录。而 PHP 也是有着注解和 AOP 的,看了这篇文章之后就一直思考该如何实现。想了很久,看了很多遍这篇文章还是感觉不是很完美,虽然把日志的记录从代码迁移到注解中与代码分离,但还是有很强的耦合,每次业务代码的修改还是需要对日志进行更新的。 想法 操作日志一般是需要记录下用户的增删改操作,对数据库增删改的操作我们一般使用 ORM 完成,所以我们可以使用 ORM 的模型事件created、updated、deleted来记录用户的增删改操作。 在其中我们可以很方便的获取到用户的操作内容,然后怎么实现可读性呢? 我的想法是通过获取表注释来明确这次操作的含义,然后通过字段注释知道操作字段的具体含义内容。如此我们就可以记录到可读性的日志内容了嘛? 实现 假如有张用户表,SQL 如下: create table tb_user ( id int auto_increment primary key, name varchar(20) null comment '姓名', sex tinyint default 0 null comment '性别' ) comment '用户'; 在其中添加一条数据:...

2022-01-04 · 1 分钟

在PhpStorm中使用PHPUnit进行单元测试

PHPUnit 是什么 PHPUnit 是一个面向 PHP 程序员的测试框架,这是一个 xUnit 的体系结构的单元测试框架。 PHPUnit 的官网地址为:https://phpunit.de/,中文镜像网站:http://www.phpunit.cn/。 安装 PHPUnit PHPUnit 有两种安装方式,一种是下载 PHAR 发行包进行全局安装,一种是使用 composer 来为某一个项目安装。 推荐使用 composer 安装,本文也是使用这种安装方式。 首先以上一篇文章创建的空的 composer 包为基础,执行以下命令即可。 root@d63b4f236f0c:/home# composer require --dev phpunit/phpunit 编写 PHPUnit 测试 首先在项目下面新建一个tests文件夹,用来存放单元测试文件。 然后编辑 composer.json 文件为 tests 文件夹增加一个命名空间"Chance\\Log\\Test\\": "tests/"并执行composer dump-autoload更新 composer 的命名空间与文件夹映射关系。 在 tests 目录创建 StackTest.php 文件,使用官网的一个例子来测试。 StackTest.php <?php namespace Chance\Log\Test; use PHPUnit\Framework\TestCase; class StackTest extends TestCase { public function testPushAndPop() { $stack = []; // 断言方法 assertEquals 判断两值是否相等 $this->assertEquals(0, count($stack)); array_push($stack, 'foo'); $this->assertEquals('foo', $stack[count($stack)-1]); $this->assertEquals(1, count($stack)); $this->assertEquals('foo', array_pop($stack)); $this->assertEquals(0, count($stack)); } } 命令行执行单元测试 运行....

2021-12-19 · 1 分钟