饭饭's Blog https://ffis.me/ zh-CN 饭饭的博客(ffis.me) - 不忘初心,方得始终 Tue, 14 Jan 2025 20:57:00 +0800 Tue, 14 Jan 2025 20:57:00 +0800 使用Joplin Server搭建属于自己的私有云笔记 https://ffis.me/archives/2259.html https://ffis.me/archives/2259.html Tue, 14 Jan 2025 20:57:00 +0800 饭饭 1 背景

在当今数字时代,笔记软件已经成为我们生活和工作中不可或缺的工具。然而,随着数据安全和隐私保护意识的提升,越来越多的用户开始关注笔记数据的存储位置和访问便捷性。
在众多笔记软件选择中,Joplin 作为一款开源的笔记应用,以其优秀的特性和灵活的部署方式脱颖而出。
对于Joplin,我已经使用3年了,所以强烈推荐有动手能力的同学自己搭建私有云笔记。
2025-01-14T13:01:14.png

为什么选择私有云笔记?

与传统的云笔记服务相比,私有云笔记具有以下优势:

  1. 数据安全性:数据完全由自己掌控,不用担心服务商数据泄露
  2. 隐私保护:笔记内容只存储在自己的服务器上,不会被第三方访问
  3. 成本可控:无需支付高额的存储费用,可以根据需求灵活扩展
  4. 功能自定义:可以根据个人需求进行功能定制和优化

为什么选择 Joplin

让我们来对比一下市面上常见的笔记软件:

  • 有道云笔记

    • 优点:界面友好,功能齐全,中文支持好
    • 缺点:数据存储在第三方服务器,无法私有部署,付费功能较多
  • OneNote

    • 优点:与微软生态集成度高,功能强大
    • 缺点:依赖微软服务,同步有时不稳定,无法私有部署
  • Joplin

    • 优点:

      • 开源免费
      • 支持私有部署
      • 全平台支持(Windows, macOS, Linux, Android, iOS)
      • Markdown 编辑器
      • 端到端加密
      • 插件系统
    • 缺点:

      • 界面相对简单
      • 无web端

2 Joplin Server 部署要求

官方支持情况

Joplin 官方提供了完整的服务器端实现,并支持多种部署方式:

  • Docker 容器化部署(推荐)
  • 源码部署
  • 二进制部署

服务器配置要求

最低配置要求
CPU:1核
内存:1GB
存储:取决于笔记数量,建议至少 20GB
操作系统:支持 Docker 的任何系统

3 部署准备工作

域名准备

  1. 购买域名(如:note.yourdomain.com)
  2. 配置 DNS 解析到服务器 IP
  3. 等待 DNS 生效(通常需要几分钟到几小时)

环境准备

# 安装 Docker
curl -fsSL https://get.docker.com | sh

# 安装 Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# 验证安装
docker --version
docker-compose --version

# 安装nginx
sudo apt update
sudo apt install nginx

4 使用 Docker Compose 部署 Joplin Server

首先创建项目目录

mkdir joplin-server
cd joplin-server

创建 docker-compose.yml 文件

可参考官方配置:https://raw.githubusercontent.com/laurent22/joplin/dev/docker-compose.server.yml

version: '3'

services:
    db:
        image: postgres:13
        container_name: postgres_13
        volumes:
                - ./postgresData:/var/lib/postgresql/data
        ports:
                - "127.0.0.1:5432:5432"
        restart: unless-stopped
        environment:
                - POSTGRES_PASSWORD=your_password
                - POSTGRES_USER=joplin
                - POSTGRES_DB=joplin
    app:
        image: joplin/server:latest
        depends_on:
            - db
        container_name: joplin
        ports:
            - "127.0.0.1:22300:22300"
        restart: unless-stopped
        environment:
            - APP_PORT=22300
            - APP_BASE_URL=https://your-domain.com
            - DB_CLIENT=pg
            - POSTGRES_PASSWORD=your_password
            - POSTGRES_DATABASE=joplin
            - POSTGRES_USER=joplin
            - POSTGRES_PORT=5432
            - POSTGRES_HOST=db
        logging:
                driver: "json-file"
                options:
                        max-size: "100m"
                        max-file: "5"

这里会部署两个docker容器,一个是PostgreSQL数据库,一个是Joplin Server,如果你有现成的PostgreSQL数据库,可以把数据库的容器去掉,只保留Joplin Server的容器
这里我就是独立部署,可以参考我的部署示例,并且带邮件的相关配置

services:
  joplin_server:
    image: joplin/server:latest
    container_name: joplin
    ports:
      - "22300:22300"
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
      - APP_PORT=22300
      - APP_BASE_URL=https://your-domain.com
      - DB_CLIENT=pg
      - POSTGRES_PASSWORD=your_password
      - POSTGRES_DATABASE=joplin
      - POSTGRES_USER=joplin
      - POSTGRES_PORT=5432
      - POSTGRES_HOST=postgresql
      - MAILER_ENABLED=1
      - MAILER_HOST=your_mail_host
      - MAILER_PORT=465
      - MAILER_SECURITY=tls
      - MAILER_AUTH_USER=your_user
      - MAILER_AUTH_PASSWORD=your_password
      - MAILER_NOREPLY_NAME=饭饭's Note Server
      - MAILER_NOREPLY_EMAIL=your_email
    networks:
      - local_network
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

Nginx反向代理配置

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://joplin_server:22300;
    }

现在已经是 2025 年,请为域名配置 HTTPS 证书,并将 HTTP 流量阻挡或重定向到 HTTPS,以保障数据传输的安全。

启动服务

cd joplin-server
docker-compose up -d

查看资源占用

docker stats

正常资源占用在200m左右,我的服务跑了2年了,占用也才400m
2025-01-14T12:27:58.png

登录修改密码

部署成功并配置好nginx反向代理后,此时访问你的域名会进入joplin的登录页面
yourdomain.com/login
默认情况下,Joplin Server 将设置一个管理员用户:
账户:admin@localhost
密码:admin

我们使用默认的账户密码登录后,在右上角,点个人资料按钮更新管理员账户和密码,建议使用强密码
也可以新建一个账户给自己用
2025-01-14T12:43:26.png

5 客户端配置

1.下载对应平台的客户端,Jopin客户端基本覆盖了全平台

2.下载后客户端后,打开工具-->选项-->同步菜单中进行登录
2025-01-14T12:44:42.png
同步目标:Joplin服务器(BETA)
填写自己刚刚设置的账号密码即可完成登录

6 注意事项

1.安全性考虑:

  • 始终使用 HTTPS
  • 定期更新系统和 Docker 镜像
  • 设置强密码
  • 配置防火墙

2.数据备份:

  • 定期备份 PostgreSQL 数据
  • 备份 Joplin 数据目录
  • 考虑使用自动备份脚本
]]>
5 https://ffis.me/archives/2259.html#comments https://ffis.me/feed/
使用雪花算法生成16位全局唯一自增ID https://ffis.me/archives/2263.html https://ffis.me/archives/2263.html Sat, 16 Sep 2023 18:03:00 +0800 饭饭 背景

实际业务中,往往会用数据库自增ID来作为业务对象的唯一ID;
但数据日渐增多的互联网行业,分库分表则成了行业的通用解决方案;
此时数据库自增ID就不能满足业务需求了,行业内有各种分布式ID生成解决方案,其中雪花ID就是其中使用较多的一种。

雪花算法原理

1 基本构成

雪花算法(Snowflake)是 Twitter 开源的分布式 ID 生成算法,可以基于时间生成全局不重复的、有序的、可自增的 64 Bit 的 ID,适用于分布式系统中的 ID 生成需求。
在标准版本中由以下部分组成:
符号位(1bit)- 时间戳相对值(41bit)- 数据标志(5bit)- 机器标志(5bit)- 递增序号(12bit)
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
一起来一共64Bit组成,其中:
1、最高位是符号位,用于区分是正数还是负数,这里始终为0,我们用不到;
2、41位的毫秒级时间戳,41位的长度可以使用69年;
3、5位datacenterId和5位workerId,加起来共10位,最多支持32 x 32 = 1024个节点
4、最后12位是毫秒内的计数,12位的计数顺序号支持每个节点每毫秒产生4096个ID序号

2 生成ID原理

标准版雪花算法在实例化时,由于支持的时间范围有限制,所以开始的时候需要指定一个开始时间,并指定数据标识和机器标识;
如设定开始时间为 2020-01-01 00:00:00,数据标识设置为:15,机器标识设置为18;
此时我们想要生成1个雪花ID
1、获取时间戳差值
先获取当前时间戳与设定的开始时间的差值
当前时间2023-09-16 17:00:00转换为时间戳:1694854800000
开始时间2020-01-01 00:00:00转换为时间戳:1577808000000
计算时间戳差值:1694854800000 - 1577808000000 = 117046800000
转换为二级制:1101101000000100010000110111010000000
2、获取数据标识和机器标识
数据标识:15;机器标识:18
分别转换为二级制:数据标识:1111;机器标识:10010
3、获取毫秒内计数
毫秒内计数即是同一毫秒内,获取的第n个雪花ID,假设我们获取的是第1024个雪花ID
1024换算为二级制:10000000000
则生成的雪花ID为:时间戳差值+数据标识+机器标识+毫秒内计数
1101101000000100010000110111010000000 1111 10010 10000000000
二进制转为十进制,生成的雪花ID即为:122732465357820928 - 共18位

雪花算法优缺点

1 优点

  • 唯一性:雪花算法生成的ID是全局唯一的,可以在分布式系统中生成不重复的业务ID;
  • 有序性:基于时间戳的特性,使得雪花算法生成的ID是顺序增长的;
  • 高性能:雪花算法的生成过程是基于位运算实现的,性能好,并且标准的雪花算法每毫秒支持4096个ID生成,满足绝大多数的业务场景;
  • 独立性:雪花算法不依赖中央系统或数据库,非常方便在业务中落地和进行横向水平扩展;

2 缺点

  • 依赖系统时钟:雪花算法依赖系统时钟,如果时钟发生回退,会导致ID生成重复,当然这个都有对应的解决方案;
  • 有限的容量:标准的雪花算法支持每毫秒生成4096个ID,如果超过了容量限制,则需要等待下一毫秒才可以生成新的ID;
  • 前端精度丢失:标准雪花算法生成的ID在18位和19位之间(时间差超过7.56年,就会达到19位),JavaScript 的 number 类型的数值范围是:2的53次方减1,所以数字位数大于16位且大于 9007199254740991 的数,进制转换会存在精度问题,而雪花ID生成的数值过大,导致超出JavaScript的精度范围,无法直接在前端展示,使用字符串展示雪花ID可避免此问题。

定制16位雪花ID

1 雪花算法定制

既然JavaScript只支持16位一下的数字展示,那我们生成16位以下的雪花ID即可;
标准的雪花算法为64Bit,每个位置的设计冗余量都很大,业务中完全可以根据自己的业务形态去定制雪花算法;
1、缩短支持时间
标准雪花算法为41位时间戳差值,支持使用69年,实际业务中很少有能跑69年的业务,我们可以将时间戳差值修改为39位,39位时间戳支持使用:(2^39)/(1000606024365) = 17.43 年,17年足够大部分业务使用;
2、只保留机器标识
标准雪花算法支持:5位的数据标识和5位的机器标识
即最大支持 32 x 32 = 1024 节点部署
实际业务很少有这么大规模的机器部署,一般最多也就10台机器,这里完全可以将5位数据标识去除,只保留5位机器标识,即支持32个节点部署;
3、缩短毫秒内计数
标准雪花算法同一毫秒支持12位的序列,即每毫秒支持4096个ID生成
实际业务也很少能用到这么多ID,我们可以将序列缩短为8位,即每毫秒支持256个ID生成,完全满足大部分业务需要;
经过以上定制的雪花算法,最多支持 32 台机器,每台机器每毫秒能够生成最多 256 个 ID,整个集群理论上每秒可以生成 32 1000 256 = 800万 个ID;

2 最大支持ID

39位时间戳,最大值:549755813887毫秒,约17.43264250022197年
5位机器号,支持最大机器数量:32
8位序列,每毫秒生成:256个ID,每秒生成:256000个ID
组成的最大二进制:1111111111111111111111111111111111111111111111111111 共52位
最大生成ID:4503599627370495 - 共16位

经过定制的雪花算法,完全满足大部分业务使用。

源码

源码发布于:moyu-framework
详见 Github:https://github.com/MoYu-Group/moyu-framework/blob/main/moyu-base/moyu-util/src/main/java/io/github/moyugroup/base/util/UUIDUtil.java

]]>
1 https://ffis.me/archives/2263.html#comments https://ffis.me/feed/
使用HotSwapAgent实现SpringBoot热加载 https://ffis.me/archives/2234.html https://ffis.me/archives/2234.html Thu, 24 Feb 2022 22:32:00 +0800 饭饭 众所周知,IDEA 自带的热加载只支持方法内的热加载,而使用 HotSwapAgent 不仅支持方法内的热加载,并且可实现新增方法的热加载,甚至是新增类的热加载,可谓是提高开发效率的神器 真棒.png
本文主要介绍在 IDEA 下使用 HotSwapAgent 来进行 SpringBoot 下的热部署;
接下来我们开始配置 HotSwapAgent

1 安装 DCEVM

DCEVM 是个JDK的插件,提供类似 JRebel 的热加载功能,能够在运行时重新定义加载的类,实现“热加载、热插拔、热部署”,而 HotSwapAgent 插件则是实现了 Servlet 程序的热加载功能,并且 DCEVM + HotSwapAgent 开源免费,更适合广大开发者使用。
安装 DCEVM 需要和 JDK 版本相对应,你需要先确定自己的 JDK 版本是否是 DCEVM 所支持的版本
https://github.com/dcevm/dcevm/releases
写此文时,DCEVM 支持的最新 JDK 版本是Java 8u181
如果你的JDK版本不被支持,则需要去 Oracle Java Archive 这个页面下载对应版本 JDK 并安装
https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html
下载 JDK 需要自备 Oracle 的账号
下载并安装好对应版本 JDK 后,我们再次来确定 JDK 版本为对应版本
2022-02-24T13:42:56.png

此时我们可以下载 DCEVM patch 进行安装,这里我下载的是 DCEVM-8u181-installer-build2.jar
https://github.com/dcevm/dcevm/releases
运行以下命令开始进行安装

java -jar DCEVM-8u181-installer-build2.jar

2022-02-24T13:43:41.png
这里需要选择安装目录,安装目录默认为 JDK 安装目录,如果没有则需要手动添加,然后点击 Install DCEVM as altjvm 这个按钮安装。

2 IntelliJ IDEA 配置

在 IDEA 插件库中搜索 HotSwapAgent 进行安装插件
2022-02-24T13:59:22.png
然后进行 HotSwapAgent plugin 配置
勾选为所有项目启动 HotSwapAgent 插件
2022-02-24T14:04:55.png
PS:请手动检查插件目录下的 jar 包是否自动下载成功,如果下载不成功,则手动下载 jar 包替换
2022-02-24T14:04:36.png
图中的 Agent installation 路径为Jar包保存路径
jar 包下载地址:https://github.com/HotswapProjects/HotswapAgent/releases

3 热部署启动

在 DEBUG 模式下启动应用程序。如果设置正确完成,您将在控制台中看到 HOTSWAP 代理通知。
2022-02-24T14:08:12.png

4 手动触发热部署方式

在应用启动的过程中,如果有修改代码,可以手动触发热部署使其生效
MacOS: Command+Shift+F9 / Win: Ctrl+Shift+F9
手动触发热加载的时候,光标需要将焦点保持在需要热加载的类上面
2022-02-24T14:09:02.png
当然你也可以配置自动触发热部署,由于我不喜欢自动触发,所以在此不做过多介绍。

5 热加载测试

我们使用Spring Initializr快速创建一个 SpringBoot 应用,并且编写一个简单的测试接口:
2022-02-24T14:11:02.png
2022-02-24T14:19:03.png
应用使用 DEBUG 模式启动的时候有提示被 HotSwapAgent 代理
2022-02-24T14:22:39.png
使用浏览器访问测试接口,正常访问
2022-02-24T14:23:03.png

1 修改方法体内代码,手动触发热加载
2022-02-24T14:23:49.png
提示1个类被重新加载
2022-02-24T14:23:31.png
再次访问接口,方法体内修改的代码已经生效
2022-02-24T14:24:09.png

2 新增一个方法,手动触发热加载
2022-02-24T14:24:53.png
提示1个类被重新加载,并且类被重新注册
2022-02-24T14:25:27.png
访问新增的接口,新增的方法已经生效
2022-02-24T14:25:48.png

3 新增一个类,手动触发热加载
2022-02-24T14:26:28.png
提示0个类被重新加载,但是新增的Controller2类已经被注册,说明只能重新加载被 Spring 注解标注的类
2022-02-24T14:26:49.png
访问新增的类接口,新增的类已经生效
2022-02-24T14:27:04.png

6 测试总结

经测试,修改方法体内代码,修改方法,并且新增类都是可以支持热加载,但是新增类只支持注解扫描的类,XML配置文件不能进行热加载

7 参考文档

https://github.com/HotswapProjects/HotswapAgent
https://github.com/dmitry-zhuravlev/hotswap-agent-intellij-plugin

]]>
7 https://ffis.me/archives/2234.html#comments https://ffis.me/feed/
将网站程序升级到了 Typecho v1.2.0-beta.2 https://ffis.me/archives/2219.html https://ffis.me/archives/2219.html Tue, 14 Sep 2021 22:38:00 +0800 饭饭 念念不忘,必有回响

Typecho终于迎来了v1.2.0的第二个测试版本,相信离正式版发布已经不远了! 真棒.png
真是有生之年系列,距离上次发布1.1版本,已经过去了4年之久...

期间虽然一直没有发布正式版本,但是Typecho的开发版本其实一直保持在活跃中,作者也一直在修复BUG和开发新的功能,只是一直没有发布新版本而已;这次作者也是鸽了好久,准备发布一个新的版本,带来了许多新特性:

具体详见:新版的Typecho开发计划

其实本站从1.1版本发布之后,一直使用的都是Typecho开发版本; 捂嘴笑.png
我是直接在我网站根目录下把开发版本master分支的Github项目给clone下来,然后Nginx手动屏蔽敏感文件的访问;
这样的好处就是比较方便能够保持程序与最新开发版的更新,如果作者修复了BUG,我直接执行git clone命令即可更新到最新的版本,而不用等到作者多年后发布正式版...

这次看到作者发布第一个测试版本Typecho v1.2.0-beta.1时,我就跃跃欲试去更新了最新版,但由于这次作者的改动比较大,而我也没有去手动测试,就直接头铁的在线上环境更新了程序,然后果不其然网站直接就挂掉了... 惊哭.png ,虽然作者说这次更新会兼容以前的插件,但是我所用的众多插件还是都不兼容,博客主题也有不兼容的情况... 数据也进行了升级,网站直接打不开也没法回退版本了...

还好我有每日的自动备份(定期备份真是一个程序员的基本素养),先把网站恢复到上次备份... 看来新版本还是有很多BUG啊 笑尿.png

经过几天测试,我将测试出来的BUG都提交到了Typecho项目的issues,作者回复的很及时,确认是BUG的话基本上当天就修复完毕,经过一周测试,作者发布了第二个测试版本Typecho v1.2.0-beta.2,此时最新版本和本站已经基本没有什么兼容性问题了,除了又拍云的UpyunFile插件有点不兼容,不过这个已经定位到BUG,作者也表示需要自己修改插件实现,手动修改一下函数调用方式即可;
又拍云插件UpyunFile适配Typecho1.2.0:https://github.com/noisky/UpyunFile

到此兼容问题全部解决,终于可以放心升级了 滑稽.png

QQ截图20210914212905.png

开始升级

  1. 升级前一定要先备份,考虑到新版程序可能不兼容老版的配置文件,这里我备份程序后删除了之前版本的config.inc.php,这样等到升级完成后需要重新运行安装程序,然后使用原有数据安装即可自动生成新版配置文件;
  2. 升级就比较简单了,直接git pull就升级到了最新版 你懂的.png
    QQ截图20210914222017.png
  3. 升级后由于之前删除了配置文件,我们进入了安装程序,一路下一步安装即可,数据库信息填写之前的数据;
    QQ截图20210914215126.png
  4. 安装成功
    QQ截图20210914215258.png
  5. 由于是使用原有数据库安装的,这里我们直接升级数据,即完成了版本升级;
    QQ截图20210914215314.png
  6. 修改配置文件中的头像源为自定义头像源,Typecho默认的头像源在国内无法访问,推荐使用这个头像源,是LOC的大佬搭建的,速度非常快;

    // 系统头像源
    define('__TYPECHO_GRAVATAR_PREFIX__', 'https://gravatar.loli.top/avatar/');

    QQ截图20210914220536.png

写在最后

时间过得真快,一转眼,建站已经有8个年头了,而我也从一个懵懂无知的愤青,进化到了一个只会复制粘贴的工程师,Typecho也从0.8一路迭代到了1.2;
QQ截图20210914213701.png

未来很长,生活不易,我们还需更加努力,一起加油,让自己变的更好吧! 哈哈.png
正如作者所说:

这是一个纷繁的时代,信息渠道已经多到快让人喘不过气来。而个人博客这种来自互联网初期的载体,似乎与这个环境显得格格不入。它就像大海中的小岛,在海浪中显得那么不起眼,但它的存在本身就彰显了一种意义。无论是波涛汹涌,还是风平浪静,它都在那里,而你的心也就找到了一处可以停靠的港湾。
]]>
3 https://ffis.me/archives/2219.html#comments https://ffis.me/feed/
Drone下多工程项目使用Commit日志控制子工程运行 https://ffis.me/archives/2213.html https://ffis.me/archives/2213.html Wed, 11 Aug 2021 23:36:00 +0800 饭饭 最近拖延症犯了,一个文章标题写了一个月才写了个标题... 不高兴.png

前言

自从入坑Drone CI/DI以来,我极力推荐在小微项目上使用Drone来完成自动构建,主要是轻量化,安装配置方便 吐舌.png ,
只需写一个docker compose文件即可完成Drone的安装配置,
只需写一个.drone.yml即可完成接入,极为方便;

Drone更深一层探究

经过我的不懈努力,经主管同意,最终也在公司项目上使用上了Drone来逐步替代Jenkins进行小项目微服务的自动构建部署 太开心.png

但是实际使用中发现一个问题,即SpringBoot工程,通常是一个主工程下包含多个微服务子工程,使用drone不太好控制其中某一个工程的自动构建部署,总不能每次都重新构建整个服务,然后重启所有工程吧,这样效率也太低了 不高兴.png

//多工程目录结构,本文主要演示SpringBoot多工程项目自动构建部署
demoParant
├── common   //公共工程
├── api      //API工程
├── user     //用户工程
└── back     //后台工程

能想到的最简单实现的方式就是来通过不同的分支来触发不同的构建任务,这样理论上可行,但是实际操作会产生一堆分支,显得极为不整洁,并且正常开发也是开发一个分支,测试一个分支,生产一个分支,太多了操作起来也不方便 黑线.png

所以最理想的方式也就是在同一个分支下,通过某种方式来触发不同的构建任务;

Drone的启发

在Drone CI下有一个默认功能,即在Commit log中输入[CI SKIP]即可跳过本次自动构建,于是我就想能否通过Commit log来控制本次部署具体哪个子工程,这样下来我只要在输入commit log的时候输入需要构建的工程,即可完成对应工程的自动构建部署,并且不影响同项目下的其他工程;

初步设想的原理就是在drone执行部署命令时,通过自定义脚本完成工程部署,并将commit log作为参数传入脚本,在脚本中判断commit log中是否指定某些工程的运行的参数,如果不指定则默认运行所有工程 乖.png

例如:我提交commit日志update Admin.java; add admin management interface; [CI API] [CI BACK];
这样一来,经过drone自动构建后,只重新部署了API工程和后台工程;

具体实现

具体实现可参考如下脚本:

Drone自动构建脚本参考

# drone 自动构建
name: test-project autodeploy
kind: pipeline
type: docker
# drone 构建步骤
steps:
  # 1. 进行 maven 打包
  - name: maven compile
    pull: if-not-exists
    image: maven:ibmjava-alpine
    volumes:
      # 本机准备挂载到宿主机的 maven构建缓存
      - name: cache
        path: /root/.m2
    commands:
      # 开始打包maven工程 跳过测试步骤
      - mvn clean install -Dmaven.test.skip=true
      - cp api/target/*.jar ./
      - cp user/target/*.jar ./
      - cp back/target/*.jar ./
  # 2. 将打包后的jar包部署到指定服务器
  - name: jar deploy
    pull: if-not-exists
    image: appleboy/drone-scp
    settings:
      host:
        from_secret: test_host
      username:
        from_secret: test_username
      password:
        from_secret: test_password
      port:
        from_secret: test_port
      target: /home/drone/data/${DRONE_REPO_NAME}
      source: ./*.jar
    when:
      branch:
        - master
  #3. 使用ssh访问测试服务器进行服务部署
  - name: test ssh-start
    pull: if-not-exists
    image: appleboy/drone-ssh
    settings:
      host:
        from_secret: test_host
      username:
        from_secret: test_username
      password:
        from_secret: test_password
      port:
        from_secret:test_port
      script:
        # 运行部署脚本
        - cd /data/testProjrct
        # 这一步是运行部署脚本,并将Comment日志作为参数传入给脚本,部署命令一定要放到drone的最后一条命令,这样在脚本中抛出异常退出后,drone可以捕捉到异常退出,将该次构建标记为构建失败
        - ./drone.sh ${DRONE_COMMIT_MESSAGE}
        -
    when:
      branch:
        - master
# 挂载的主机卷,映射到宿主机对应的目录,对应name为steps.volumes.name
volumes:
  # maven构建缓存
  - name: cache
    host:
      # path: /tmp/cache/.m2
      path: /opt/maven
# drone执行触发分支
trigger:
  branch:
    - master
  event:
    - push

Jar运行部署脚本参考
只做了最简单的实现,脚本有很多可以优化的空间,在这里不多加阐述了

#!/bin/bash
echo "开始运行Demo服务..."
PARAM=$1;
echo -e "Commit log: "$PARAM;
#----------------------- 基本参数配置 start -----------------------
# JAVA安装目录
JAVA_PATH="/opt/jdk1.8.0_191/bin/java";
# 运行环境配置
JAVA_RUN_ENV="test";
# drone部署的jar包所在路径
DRONE_JAR_PATH="/home/drone/data/demoParant";
# API工程
APP_API_NAME="apiService-0.0.1-SNAPSHOT.jar"
APP_API_PATH="/home/demoPatent/apiService"
APP_API_PING_URL="http://127.0.0.1:18091/api/open/ping"
APP_API_RUN=
# 用户端工程
APP_USER_NAME="userService-0.0.1-SNAPSHOT.jar"
APP_USER_PATH="/home/demoPatent/userService"
APP_USER_RUN=
APP_USER_PING_URL="http://127.0.0.1:18071/user/open/ping"
# 后台服务工程
APP_BACK_NAME="backService-0.0.1-SNAPSHOT.jar"
APP_BACK_PATH="/home/demoPatent/backService"
APP_BACK_RUN=
APP_BACK_PING_URL="http://127.0.0.1:18081/back/open/ping"

# 记录是否有启动失败的服务
APP_START_FAIL=
APP_START_RESULT=
#----------------------- 基本参数配置 end -----------------------

echo "程序运行环境:$JAVA_RUN_ENV"
#----------------------- 定义启动函数 start-----------------------
startAPP() {
    APP_NAME=$1;
    APP_PATH=$2;
    PING_URL=$3;
    # echo "APP_NAME:$APP_NAME"
    # echo "APP_PATH:$APP_PATH"
    # echo "PING_URL:$APP_NAME"
    # 部署新jar包到程序运行目录
    if [ -f "$DRONE_JAR_PATH/$APP_NAME" ];then
        echo "Move the new jar package to the deployment directory ..."
        mv -f $DRONE_JAR_PATH/$APP_NAME $APP_PATH/$APP_NAME
        else
        echo "The drone original jar file not exist, skip move."
    fi

    # 获取程序PID
    getPid() {
        APP_PID=`ps -ef | grep -v grep | grep $APP_NAME | awk '{print $2}'`
    }
    getPid

    # 启动前检查应用是否启动,如果已经启动则先停止再重新启动
    while [ ${APP_PID} ]
    do
        echo -e "App ${APP_NAME} is still RUNNING! PID:$APP_PID";
        echo "stop ${APP_NAME} ...";
        kill -9 ${APP_PID};
        sleep 2;
        getPid
    done

    # 运行jar包
    echo "start ${APP_NAME} ...."
    cd ${APP_PATH}
    nohup ${JAVA_PATH} -server -Xms256m -Xmx512m  -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=768m -Xss256k -jar ./${APP_NAME} --spring.profiles.active=$JAVA_RUN_ENV > /dev/null & 2>&1 &

    # 请求测试接口,判断服务是否正常启动
    getHttpCode () {
        http_code=`curl -Is -m 10 -w %{http_code} -o /dev/null $PING_URL`;
    }

    # 检查服务启动状态
    sleep 2s;
    count=0;
    # 获取接口状态码
    getHttpCode
    while [ $http_code -ne 200 ]
    do
        if (($count == 8));then
            echo "${APP_NAME}: $(expr $count \* 5)秒内未启动,请检查!";
            msg="start ${APP_NAME} failed!\n";
            echo -e $msg;
            APP_START_RESULT=$APP_START_RESULT$msg;
            APP_START_FAIL="YES";
            return;
        fi
        count=$(($count+1));
        echo "${APP_NAME} waiting to start ...";
        # sleep 1s;
        sleep 5s;
        # 获取接口状态码
        getHttpCode
        echo "http_code --> $http_code";
    done

    getPid
    msg="start ${APP_NAME} success! PID=$APP_PID\n";
    echo -e $msg;
    APP_START_RESULT=$APP_START_RESULT$msg;
}
#----------------------- 函数定义结束 end -----------------------

#----------------------- commit log 判断 start-----------------------
# 根据commit日志参数判断启动哪个工程
if [[ $PARAM == *'[CI API]'* ]]; then
    echo -e "Commit参数指定启动API工程...\n";
    APP_API_RUN="true"
fi

if [[ $PARAM == *'[CI USER]'* ]]; then
    echo -e "Commit参数指定启动用户端工程...\n";
    APP_USER_RUN="true"
fi

if [[ $PARAM == *'[CI BACK]'* ]]; then
    echo -e "Commit参数指定启动BACK工程...\n";
    APP_BACK_RUN="true"
fi

if [[ -z "$APP_API_RUN" ]] && [[ -z "$APP_USER_RUN" ]] && [[ -z "$APP_BACK_RUN" ]]; then
    echo -e "Commit未指定参数,准备启动所有工程...\n";
    APP_API_RUN="true"
    APP_USER_RUN="true"
    APP_BACK_RUN="true"
fi
#----------------------- commit log 判断结束 -----------------------

# 启动运营端服务
if [ $APP_API_RUN ]; then
    echo "Run api service ..."
    startAPP $APP_API_NAME $APP_API_PATH $APP_API_PING_URL
fi
# 启动用户端服务
if [ $APP_USER_RUN ]; then
    echo "Run user service"
    startAPP $APP_USER_NAME $APP_USER_PATH $APP_USER_PING_URL
fi
# 启动商家端服务
if [ $APP_BACK_RUN ]; then
    echo "Run back service"
    startAPP $APP_BACK_NAME $APP_BACK_PATH $APP_BACK_PING_URL
fi


# 删除drone构建缓存
rm -rf $DRONE_JAR_PATH/*.jar

# 打印运行结果
echo "------------ service start status ------------"
echo -e $APP_START_RESULT
echo "----------------------------------------------"

# 如果有启动失败的应用,则退出状态码为1,用于drone标记构建失败
if [ $APP_START_FAIL ]; then
    exit 1;
fi

以上脚本在实际生产环境已经稳定运行几个月了,经过如上配置可以很方便的去控制多工程项目下某一个工程的自动构建部署,只需在Commit日志中指定构建参数即可,在实际开发中无疑是解放了双手,能让人更加专注于业务代码实现上(摸鱼) 滑稽.png

Demo演示

最后附上我一个测试环境工程的Drone自动构建日志的Demo演示
Commit log未指定参数,默认自动构建并部署所有工程:
QQ截图20210812134905.jpg

Commit log指定参数,只自动构建并部署指定工程:
QQ截图20210812200148.jpg

]]>
2 https://ffis.me/archives/2213.html#comments https://ffis.me/feed/
Typecho 极验滑动验证码插件 Geetest https://ffis.me/archives/2200.html https://ffis.me/archives/2200.html Thu, 15 Apr 2021 00:05:00 +0800 饭饭 其实网站很久之前就用上了极验的滑动验证码;
不过之前的验证码是基于我自己写的API实现的,并且是纯前端实现,无法屏蔽机器自动发的辣鸡评论...
偶尔看到一个Typehco插件Geetest for Typecho --> http://zsduo.com/archives/56.html
感觉写的还不错,不过没有评论的验证码功能,就自己改了改,
保留了插件原来的后台登陆验证码,新增了评论验证码,并且适配了网站的主题 滑稽.png

普通评论

2021-04-14T15:59:36.png

后台登陆

2021-04-14T16:02:22.png
这次更新对于用户是无感知的更新,但是验证码从前端验证改到了后端验证,这样基本能够杜绝垃圾消息的产生;

使用方法

使用也很简单,下载插件后,解压,将文件夹名称改为 Geetest,上传到 /usr/plugins 目录下,在插件面板启用插件并配置即可使用;
极验验证码的 ID 和 KEY 需要到极验官网https://www.geetest.com/注册并创建应用获取;
并且极验的基础版是可以免费使用的,对于我这种小网站来说是完全够用的;
2021-04-14T16:01:26.png

插件详见Github:https://github.com/noisky/typecho-plugin-geetest

]]>
25 https://ffis.me/archives/2200.html#comments https://ffis.me/feed/
动手制作一个“价值200万”的网站图标 https://ffis.me/archives/2185.html https://ffis.me/archives/2185.html Sat, 10 Apr 2021 15:14:00 +0800 饭饭 小米新LOGO发布了,从方形变成椭圆形;
竟是花200w请日本的设计大师原研哉耗时两年制作的 滑稽.png
2021-04-10T06:47:01.png

本站网站图标已经使用好多年了;
既然小米图标值两百万,那咱们也来动手制作一个“价值两百万”的网站图标; 太开心.png

小米新图标的椭圆是由函数:
2021-04-10T07:12:47.png
将n代入3得到的
2021-04-10T07:13:13.png

由于我没有找到小米新图标的矢量图,又懒得画函数图象,
所以就采用最简单的方法,直接把官网的logo下载下来,以此为模板制作新LOGO;

打开小米新官网:www.mi.com
2021-04-10T06:55:52.png

按下浏览器的F12 使用选择工具选择新图标,即可获得图标的链接
2021-04-10T06:57:02.png

下载新图标后,打开PS,新建一个120x120像素的画布,分辨率72,背景色透明
2021-04-10T07:00:29.png

将刚刚下载的图标直接拖进新建的画布中
2021-04-10T07:02:24.png

将调色板中前景色调为小米图标的背景橙色,然后使用矩形选取,选中中间的MI字,填充为刚刚选取的背景色
2021-04-10T07:05:12.png

现在就得到一个纯色的椭圆图标,我们可以使用油桶工具将该背景改为任何颜色,这里我改为我原网站图标的蓝色:#27a7dc
2021-04-10T07:08:21.png

导入之前网站图标的文字图层,调整大小,填充为白色,即可得到一个“价值200万”的新图标辣 你懂的.png
2021-04-10T07:10:32.png

]]>
2 https://ffis.me/archives/2185.html#comments https://ffis.me/feed/
年轻人第一台DIY主机,拒绝光污染! https://ffis.me/archives/2143.html https://ffis.me/archives/2143.html Wed, 07 Apr 2021 13:25:00 +0800 饭饭 终于终于要开始DIY自己的第一台主机辣~ 太开心.png
其实从准备上大学的时候,就写好了配置单准备DIY自己的主机了,但是因为当时学习宿舍给分到了6人间,无奈只能买了个联想的笔记本去上大学,一用就是四年;毕业后工作拿到第一个月的工资后,又准备DIY主机来着,但是由于当时的工作环境,一年得搬家几次,无奈又买了一台小米笔记本... 不高兴.png
现在工作相对稳定了,再不DIY自己的主机,以后怕是没得机会了...
其实现在的大环境下,DIY主机并不是很好的选择,因为新显卡太难买了,所以想着先配一台i7的主机,显卡以后再说吧...
IMG_8371.JPG

装机视频

最终DIY的配置单如下:
2021-04-06T04:22:30.png

配置选择

CPU:CPU选用的i7-10700散片,本来10700的放到购物车的时候最低价已经1700+了,但是intel的11代CPU发布后,牙膏倒吸... 导致10代CPU竟然涨价了,回头再看购物车,竟然一天一个价了...
其实CPU选用AMD的5800性能会更好,但是无奈小时候被农企坑过,就再也不敢用农企的CPU了,不过还是精神上支持下,毕竟么有Zen系列的CPU,说不定我们到现在还用着四核八线程的intel呢~
IMG_8441.JPG

主板:主板方面选用微星的B460M迫击炮,这个主板带i7不超频完全够用了,毕竟迫击炮是yyds!
IMG_8337.JPG

内存:内存选用的金士顿的骇客神条16G单条3200,其实两条8G组成双通道速度肯定比单条好得多,但是考虑到16G可能不够我用,以后准备组2x16的双通道,就先上了一条单条,等内存如果降价了就再搞一条~
IMG_8437.JPG

机械硬盘:机械硬盘选用东芝P300 2TB,选它主要是因为产品页直接标注了是PMR垂直盘,而不是坑爹的叠瓦盘...
某些品牌不标出来不明摆着让用户抽奖的吗?
IMG_8400.JPG

固态硬盘:固态硬盘选用三星PM981A工包,虽然没有官方质保,可是这年头谁还用固态存重要的东西呢 滑稽.png
并且这个硬盘的性价比,配上个散热片直接起飞~
IMG_8440.JPG

显卡:显卡本来打算上RTX3060,但是从首发到现在我是一张正常的显卡都没有抢到...我还专门写了个脚本去抢也没抢到,真实堪称大型耍猴现场,罢了,不买了,电子产品终归是电子产品,总有一天会买到的...
最后找矿老板收了个1063矿渣,凑合先用了...
伊拉克战损版GTX1060 3G版
IMG_8453.JPG
拆开清灰换硅脂
IMG_8459.JPG
变身咸鱼女生自用版
IMG_8461.JPG

机箱:机箱选用的九州风神的魔方110,本来想上先马的小坦克,但是经郭大佬 @xiazhanjian 推荐到这个,感觉还不错;
QQ截图20210406130515.jpg

电源:电源选用全汉GE650 650W 全模组,主要是老板给便宜了100块钱,并且是金牌全模组,感觉应该差不了;
IMG_8357.JPG

散热器:散热器本打算上240冷排,看中了九州风神水元素,但是最后还是选用了利民的FS140风冷;主要是水冷只质保3年,还有漏液的可能性,我自己也懒得折腾,搞个风冷装上基本上以后都用管了,并且我不超频风冷肯定够用了,其实主要还是不喜欢光污染,感觉已经过了折腾的年纪了,这次DIY的主机是完全无光污染的。
IMG_8359.JPG

机箱风扇:机箱风扇选用了先马冰风大风量风扇无光版,支持PWM调速;
QQ截图20210406131028.jpg

装机过程很顺利,机器是一次点亮的,就是排线有点难度,看着这乱糟糟的线缆,头皮发麻;
IMG_8378.JPG

不过经过一番努力,还是勉强难看了吧~
IMG_8433.JPG

最终成色如下:
IMG_8443.JPG

跑分烤鸡测试

AIDA64 FPU单烤十分钟,CPU稳定在72°C,全核频率4.6Ghz,全程无跳频,功耗在155w左右,可见微星迫击炮主板的做工用料还是扎实的多,带i7-10700是完全无压力的,感觉带i9-10900不超频也是够用的;
IMG_8466.jpg

鲁大师跑分总分40w+
鲁大师带显卡.png

国际象棋跑分3.2w+
国际象棋.png

R15跑分1946分
R15.png

R20跑分4767分
R20.jpg

游戏测试

英雄联盟,刚出生泡温泉的时候500+帧
QQ图片20210407131742.jpg

发生激烈战斗时160帧左右
2021-04-06 18-48-23.mov_20210407_131923.831.jpg

绝地求生,高画质80帧左右
2021-04-06 20-19-04.mov_20210407_132158.308.jpg

赛博朋克2077,翻车,不到30帧
2021-04-06 20-19-04.mov_20210407_132313.298.jpg

整体来说很满意,日常使用完全够用,经常玩的游戏也都能畅玩,可惜无法畅玩2077,也算是个小遗憾吧,等啥时候能买到显卡了再战夜之城吧~

]]>
12 https://ffis.me/archives/2143.html#comments https://ffis.me/feed/
记录一次在CentOS上升级OpenSSL版本 https://ffis.me/archives/2142.html https://ffis.me/archives/2142.html Tue, 30 Mar 2021 23:39:00 +0800 饭饭 最近在公司测试服务器上使用acme.sh申请ZeroSSL证书,在使用EAB注册ZeroSSL账户时一直不成功,
提示Register account Error: {"type":"urn:ietf:params:acme:error:malformed","status":400,"detail":"[External Account Binding] The JWS Signature MUST be present"}
在打开Debug模式后,看到报错:Usage: _hmac hashalg secret [outputhex]
推测是OpenSSL版本过低导致算法兼容问题,遂准备升级机器上的OpenSSL版本

开始升级

1 系统环境

系统:CentOS release 6.8 (Final)
OpenSSL版本:OpenSSL 1.0.1e-fips 11 Feb 2013

2 下载最新版本OpenSSL

打开OpenSSL官网,发现最新版本为openssl-1.1.1k

//下载最新版本
wget https://www.openssl.org/source/openssl-1.1.1k.tar.gz
//解压
tar -zvxf openssl-1.1.1k.tar.gz
cd openssl-1.1.1k

3 安装依赖

//安装依赖包
yum install -y zlib zlib-devel
//安装gcc
yum install -y gcc

4 编译安装

//配置openssl安装目录和openssldir
./config --prefix=/usr/local/openssl --openssldir=/usr/local/ssl
//使用2个线程同时编译
make -j 2
make install

5 软连接到新版本

//备份当前openssl
mv /usr/bin/openssl /usr/bin/openssl.bak
mv /usr/include/openssl /usr/include/openssl.bak //这个有些场景不存在
//配置软连接到新版本
ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl
ln -s /usr/local/openssl/include/openssl /usr/include/openssl
//更新动态链接库数据并重新加载
echo "/usr/local/openssl/lib" >> /etc/ld.so.conf
ldconfig -v
//查看是否升级成功
[root@salve openssl-1.1.1k]# openssl version
OpenSSL 1.1.1k  25 Mar 2021

PS:openssl升级会影响较多软件,谨慎升级!

]]>
4 https://ffis.me/archives/2142.html#comments https://ffis.me/feed/
放弃Let's Encrypt证书,全站更换ZeroSSL证书 https://ffis.me/archives/2110.html https://ffis.me/archives/2110.html Wed, 02 Dec 2020 00:00:00 +0800 饭饭 网站一直以来都是使用的Let's Encrypt SSL证书,主要是因为Let's Encrypt浏览器兼容性较好,支持ACME自动化部署,支持泛域名证书等,但是今天起网站开始放弃Let's Encrypt证书,全站更换ZeroSSL提供的SSL证书。

为什么放弃Let's Encrypt证书?

由于Let's Encrypt证书的OCSP验证域名由于未知原因无法访问,不过网站前一阵子给服务器开启了OCSP装订,变相解决了部分浏览器访问缓慢的问题;
但是经过测试,发现有些浏览器,比如IOS端 Chromium 系的浏览器:Chrome、新版Egde等,就算服务器开启了OCSP装订,还是会去强制访问OCSP验证证书有效性,虽然超时时间只有3s,但是问题没有得到解决就很不爽; 不高兴.png
不过也没有更好的解决办法了。

并且Let's Encrypt的X3根证书也将到期,之后会使用自己签发的根证书,虽然根证书更换后OCSP无法访问的问题会得到解决,但是新的根证书时间太短导致安卓7以下以及16年以前的一些设备没办法信任这个证书导致老设备的兼容性问题;

看来只能更换SSL证书来彻底解决该问题了。 挖鼻.png

为什么选择ZeroSSL?

经过大佬Luminous推荐,ZeroSSL家的SSL证书可支持ACME自动化部署,并且支持申请泛域名证书,所以准备考虑切换到这家CA的证书;
ZeroSSL的证书之前也听说过,没有考虑的原因是之前我点开价格后发现免费用户只能签3个单域名证书,其他的类型证书都是收费的(贫穷限制了我的想象力);
2020-12-01T15:14:29.png

不过也怪我研究不够深入,在ACME文档的介绍中发现,通过ACME自动部署的方式,可以进行无限制的签发普通域名、多域名证书、甚至通配证书等,并且可以acme.sh脚本官方也支持直接将CA切换到ZeroSSL,直接一键就可以完成证书的切换! 太开心.png
2020-12-01T16:04:01.png

既然更换证书的成本这么低了,那何乐而不为呢? 哈哈.png

接下来就准备直接将acme.sh的证书由默认的Let's Encrypt CA切换到ZeroSSL CA;

更换ACME.sh的CA为ZeroSSL

这里我的证书是使用acme.sh统一管理的,并且acme.sh官方也支持切换CA到ZeroSSL;
2020-12-01T15:52:06.png

acme.sh 的安装可参考官方文档:https://github.com/acmesh-official/acme.sh/wiki/How-to-install

下文主要是通过DNS的方式进行证书的签发,该方式在签发证书的时候会自动在dns解析中添加验证域名需要的解析,并且在验证完毕后会自动删除解析;
acme.sh 目前支持 cloudflare, dnspod, godaddy 以及 ovh 等数十种解析商的自动集成
配置很简单,只需将自己dns服务商提供的dnsapi配置到环境变量中即可
由于之前在使用Let's Encrypt的时候已经配置过,在此就不进行过多阐述
dnsapi 具体配置可参考官方文档:
传送门:https://github.com/acmesh-official/acme.sh/wiki/dnsapi

下面将acme.sh申请的证书由默认的Let's Encrypt更改为ZeroSSL

1.访问账户注册页面注册一个ZeroSSL账户

传送门:https://app.zerossl.com/signup
2020-12-01T15:28:22.png

2.获取账户的EAB凭证,用来注册acme帐户

传送门:https://app.zerossl.com/developer
2020-12-01T15:31:26.png

点击生成会生成你的eab-kid和eab-hmac-key,复制保存下来;

3.注册ACME帐户

acme.sh  --register-account  --server zerossl \
        --eab-kid  你的eab-kid \
        --eab-hmac-key  你的eab-hmac-key

2020-12-01T15:35:14.png

4.切换默认CA

接下来你就可以添加--server zerossl指令来签发新的证书了,如下:

acme.sh --server zerossl --issue --dns dns_dp -d ffis.me -d *.ffis.me

或者直接切换acme.sh的默认CA为ZeroSSL

acme.sh --set-default-ca  --server zerossl

2020-12-01T15:38:14.png

5.签发新证书,这里我签发的是泛域名证书

acme.sh --issue --dns dns_dp -d ffis.me -d *.ffis.me

签发完毕后证书会保存在/root/.acme.sh目录下,我们一般不直接访问此目录

6.安装新证书

acme.sh --install-cert -d ffis.me \
--key-file       /usr/local/nginx/conf/ssl/ffis.me.key \
--fullchain-file /usr/local/nginx/conf/ssl/ffis.me.crt \
--reloadcmd     "systemctl force-reload nginx.service"

以上命令会将证书复制到nginx指定目录下,并且强制重新加载nginx,并且以后每次自动续期都会自动执行以上逻辑;
安装完毕后,我们修改网站nginx配置文件中ssl证书文件地址为上面的安装地址即可。

经过如上操作,我的SSL证书就更换为ZeroSSL家的证书了!
2020-12-01T15:57:01.png

2020-12-02T05:38:13.png

这样OCSP无法访问的问题也就迎刃而解了,当然也可以自己去配置OCSP装订和手动预缓存,来提高加载速度

]]>
51 https://ffis.me/archives/2110.html#comments https://ffis.me/feed/