├── .github ├── ISSUE_TEMPLATE │ └── ----------issue.md └── workflows │ └── generate_readme.yml ├── .gitignore ├── README.md ├── backup ├── 10_GitHub.Actions.实战之Chrome书签同步.md ├── 11_同一浏览器不同用户登录冲突问题探究.md ├── 12_5分钟快速理解Redis的内存回收机制.md ├── 13_关于Redis缓存穿透、缓存雪崩、缓存击穿问题探究.md ├── 14_[笔记]失明症漫记.md ├── 15_常用linux进程查询命令.md ├── 16_主流分布式id方案总结.md ├── 17_redis大key内存分析.md ├── 18_咕咚和keep跑步数据导入Nike.Run.Club.md ├── 19_GitHub.Actions.实战之监控梯子流量.md ├── 1_Java泛型的回顾之旅.md ├── 20_为Git和Maven设置代理加速.md ├── 22_Maven中关于SNAPSHOT版本的总结.md ├── 23_[笔记]最好的告别.md ├── 24_关于prometheus无法采集服务指标的问题总结.md ├── 25_[笔记]人生海海.md ├── 26_关于多表关联查询的优化思路.md ├── 27_k8s实现Spring.Cloud服务平滑升级解决方案.md ├── 28_[笔记]指数基金投资指南.md ├── 29_为Docker.Alpine添加中文字体.md ├── 2_Java8函数式编程中比较实用的操作语法.md ├── 30_我的跑步感悟.md ├── 31_聊聊我整牙的那些事儿.md ├── 32_业务数据脱敏解决方案探究.md ├── 33_家常菜谱.md ├── 34_[笔记]半小时漫画经济学4:理财篇.md ├── 35_Java空指针避坑指南.md ├── 36_对k8s中Service的理解.md ├── 37_深漂5年随想.md ├── 38_基于Github.Issues的博客搭建.md ├── 39_我的2021年跑步报告.md ├── 3_白话解说之.BIO、NIO、AIO、异步阻塞的区别.md ├── 40_[译]如何阅读Apple开发文档.md ├── 42_技术周刊分享.md ├── 43_2022年个人周报.md ├── 44_[笔记]精力管理.md ├── 48_我为什么不能放弃跑步.md ├── 49_[笔记]盐糖脂:食品巨头是如何操纵我们的.md ├── 4_nginx基础指令及初始配置解析.md ├── 50_[笔记]神的九十亿个名字.md ├── 51_解决.IDEA.因为.Clash.代理问题引起的疑难杂症.md ├── 52_2023年个人周报.md ├── 53_2022年终总结之我的买房经历.md ├── 54_我的跑步数据流.md ├── 55_2023上海之旅.md ├── 56_Postman如何配置动态端口和IP.md ├── 57_2024年个人周报.md ├── 58_2024香港银行卡办卡之旅.md ├── 59_8款高效又好用的安卓.TV.App.推荐.md ├── 5_nginx负载均衡原理之ip_hash哈希算法探究.md ├── 60_2024.装修开支清单.md ├── 61_我的.Apple.设备.md ├── 62_我的老婆.md ├── 63_iPad.Pro秒变Mac.mini显示器教程.md ├── 6_Hackintosh黑苹果折腾之旅.md ├── 7_[笔记]睡眠革命.md ├── 8_基于Docker实现nginx-keepalived双机热备机制.md └── 9_5分钟快速理解Redis的持久化.md ├── feed.xml ├── images ├── 1700479996197.jpeg ├── 1702553954147.png ├── 1702554291804.png ├── 1702554459873.png ├── 20240225204100.jpg └── 20240225205428.jpg ├── img ├── 17262840412011726284040702.png ├── 1727318774261.png ├── 20201122173132.png ├── 20201122173313.png ├── 20201122173523.png ├── 20201122173737.png ├── 20201207155506.png ├── 20201216170122.png ├── 20201216172630.png ├── 20201216172847.png ├── 20201216193720.png ├── 20201222220143.png ├── 20210109162855.png ├── 20210109171707.png ├── 20210110153502.png ├── 20210127153147.png ├── 20210127155351.png ├── 20210127155423.png ├── 20210131124723.png ├── 20210203160705.png ├── 20210203161326.png ├── 20210223170842.png ├── 20210706222729.png ├── 20210906215515.png ├── 20210906215624.png ├── 20210906220109.png ├── 20210906220253.png ├── 20210906220916.png ├── 20210906222239.png ├── 20210906222437.png ├── 20210906222539.png ├── 20220114091234.png ├── 20220928133622.png ├── 20220928133650.png ├── 20220928135119.png ├── 202302270955561.png ├── 202303061028280.png ├── 202303130921928.png ├── 202303200907410.png ├── 202303270920240.png ├── 202304030911311.png ├── 202304031746128.png ├── 202304100921196.png ├── 202304171004893.png ├── 202304242004525.png ├── 202305150929276.png ├── 202305221120720.png ├── 202305290932443.png ├── 202306050916167.png ├── 202306120955363.png ├── 202307170931794.png ├── 202307240939919.png ├── 202308070934355.png ├── 202308141105270.png ├── 202308280903055.png ├── 202309040937748.png ├── 202309110914741.png ├── 202309180926390.png ├── 202402241953884.png ├── 202402251421855.png ├── 202402251422582.png ├── 202402251425702.png ├── 202402251427187.png ├── 202402251431170.png ├── 202402251433087.png ├── 202402251436421.png ├── 202402251444325.png ├── 202402252012668.png ├── 202402252013950.png ├── 20240225205754.jpg ├── 20240225211001.jpg ├── 20240225212619.jpg ├── 20240225213650.jpg ├── 20240225213825.jpg ├── IMG_216.JPEG ├── IMG_6487.JPEG ├── IMG_904.JPEG ├── IMG_908.JPEG ├── hash.jpg ├── hash2.jpg ├── iShot2020-09-19上午09.24.29.png ├── iShot2020-11-05下午02.05.03.png ├── iShot2020-11-05下午12.22.28.png ├── iShot20201115.png ├── iShot2022-01-13 17.42.06.png ├── running.jpeg ├── telegram-cloud-photo-size-5-6089317861500758841-y.jpg ├── telegram-cloud-photo-size-5-6089317861500758842-y.jpg └── telegram-cloud-photo-size-5-6089317861500758843-y.jpg ├── main.py └── requirements.txt /.github/ISSUE_TEMPLATE/----------issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 个人博客, 请不要提ISSUE 3 | about: 该仓库为个人博客, 请不要提issue, 谢谢! 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 个人博客, 请**不要**提issue, 谢谢! 11 | ==================== 12 | This repository is for personal blogging, please **DO NOT** submit issue, thanks! 13 | -------------------------------------------------------------------------------- /.github/workflows/generate_readme.yml: -------------------------------------------------------------------------------- 1 | name: Generate GitBlog README 2 | 3 | on: 4 | workflow_dispatch: 5 | issues: 6 | types: [opened, edited] 7 | issue_comment: 8 | types: [created, edited] 9 | push: 10 | branches: 11 | - main 12 | paths: 13 | - main.py 14 | 15 | jobs: 16 | sync: 17 | name: Generate README 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | - name: Set up Python 23 | uses: actions/setup-python@v4 24 | with: 25 | python-version: 3.8 26 | cache: pip 27 | cache-dependency-path: "requirements.txt" 28 | 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | python -m venv venv 33 | source venv/bin/activate 34 | pip install -r requirements.txt 35 | 36 | - name: Generate new md 37 | run: | 38 | source venv/bin/activate 39 | python main.py ${{ secrets.G_T }} ${{ github.repository }} --issue_number '${{ github.event.issue.number }}' 40 | 41 | - name: Push README 42 | run: | 43 | git config --local user.email "action@github.com" 44 | git config --local user.name "GitHub Action" 45 | git add backup/*.md 46 | git commit -a -m 'update new blog' || echo "nothing to commit" 47 | git push || echo "nothing to push" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | nohup.out 3 | .git-credentials 4 | venv/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea 8 | *.iws 9 | *.iml 10 | *.ipr 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **

[Leeyom's Blog](https://blog.leeyom.top)

** 2 | ==== 3 | 4 | **

用于记录一些幼稚的想法和脑残的瞬间

** 5 | [![](https://raw.githubusercontent.com/superleeyom/blog/main/img/IMG_216.JPEG)](https://blog.leeyom.top) 6 | 7 | ## 联系方式 8 | - Twitter:[@super_leeyom](https://twitter.com/super_leeyom) 9 | - Telegram:[@super_leeyom](https://t.me/super_leeyom) 10 | - Email:[leeyomwang@163.com](mailto:leeyomwang@163.com) 11 | - Blog:[https://blog.leeyom.top](https://blog.leeyom.top) 12 | - RSS:[RSS Feed](https://raw.githubusercontent.com/superleeyom/blog/master/feed.xml) 13 | 14 | ## 最近更新 15 | - [iPad Pro秒变Mac mini显示器教程](https://github.com/superleeyom/blog/issues/63)--2025-03-11 16 | - [我的老婆](https://github.com/superleeyom/blog/issues/62)--2025-01-03 17 | - [我的 Apple 设备](https://github.com/superleeyom/blog/issues/61)--2024-12-26 18 | - [2024 装修开支清单](https://github.com/superleeyom/blog/issues/60)--2024-12-17 19 | - [8款高效又好用的安卓 TV App 推荐](https://github.com/superleeyom/blog/issues/59)--2024-12-13 20 | - [2024年个人周报](https://github.com/superleeyom/blog/issues/57)--2024-01-08 21 | - [2023年个人周报](https://github.com/superleeyom/blog/issues/52)--2023-01-03 22 | - [2022年个人周报](https://github.com/superleeyom/blog/issues/43)--2022-01-17 23 | - [iPad Pro秒变Mac mini显示器教程](https://github.com/superleeyom/blog/issues/63)--2025-03-11 24 | - [我的老婆](https://github.com/superleeyom/blog/issues/62)--2025-01-03 25 | - [我的 Apple 设备](https://github.com/superleeyom/blog/issues/61)--2024-12-26 26 | - [2024 装修开支清单](https://github.com/superleeyom/blog/issues/60)--2024-12-17 27 | - [8款高效又好用的安卓 TV App 推荐](https://github.com/superleeyom/blog/issues/59)--2024-12-13 28 |
显示更多 29 | 30 | - [2024香港银行卡办卡之旅](https://github.com/superleeyom/blog/issues/58)--2024-02-25 31 | - [2023上海之旅](https://github.com/superleeyom/blog/issues/55)--2023-11-21 32 | - [我的跑步数据流](https://github.com/superleeyom/blog/issues/54)--2023-02-18 33 | - [2022年终总结之我的买房经历](https://github.com/superleeyom/blog/issues/53)--2023-01-08 34 | - [我为什么不能放弃跑步](https://github.com/superleeyom/blog/issues/48)--2022-03-27 35 | - [我的2021年跑步报告](https://github.com/superleeyom/blog/issues/39)--2022-01-01 36 | - [深漂5年随想](https://github.com/superleeyom/blog/issues/37)--2021-08-18 37 | - [家常菜谱](https://github.com/superleeyom/blog/issues/33)--2021-07-11 38 | - [聊聊我整牙的那些事儿](https://github.com/superleeyom/blog/issues/31)--2021-07-05 39 | - [我的跑步感悟](https://github.com/superleeyom/blog/issues/30)--2021-06-30 40 | - [咕咚和keep跑步数据导入Nike Run Club](https://github.com/superleeyom/blog/issues/18)--2021-01-27 41 | - [Hackintosh黑苹果折腾之旅](https://github.com/superleeyom/blog/issues/6)--2020-11-15 42 |
43 | 44 | - [Postman如何配置动态端口和IP](https://github.com/superleeyom/blog/issues/56)--2023-12-14 45 | - [解决 IDEA 因为 Clash 代理问题引起的疑难杂症](https://github.com/superleeyom/blog/issues/51)--2022-09-28 46 | - [技术周刊分享](https://github.com/superleeyom/blog/issues/42)--2022-01-13 47 | - [[译]如何阅读Apple开发文档](https://github.com/superleeyom/blog/issues/40)--2022-01-08 48 | - [基于Github Issues的博客搭建](https://github.com/superleeyom/blog/issues/38)--2021-09-06 49 |
显示更多 50 | 51 | - [对k8s中Service的理解](https://github.com/superleeyom/blog/issues/36)--2021-07-25 52 | - [Java空指针避坑指南](https://github.com/superleeyom/blog/issues/35)--2021-07-22 53 | - [业务数据脱敏解决方案探究](https://github.com/superleeyom/blog/issues/32)--2021-07-08 54 | - [为Docker Alpine添加中文字体](https://github.com/superleeyom/blog/issues/29)--2021-04-26 55 | - [k8s实现Spring Cloud服务平滑升级解决方案](https://github.com/superleeyom/blog/issues/27)--2021-04-16 56 | - [关于多表关联查询的优化思路](https://github.com/superleeyom/blog/issues/26)--2021-03-26 57 | - [关于prometheus无法采集服务指标的问题总结](https://github.com/superleeyom/blog/issues/24)--2021-03-12 58 | - [Maven中关于SNAPSHOT版本的总结](https://github.com/superleeyom/blog/issues/22)--2021-02-24 59 | - [为Git和Maven设置代理加速](https://github.com/superleeyom/blog/issues/20)--2021-02-04 60 | - [GitHub Actions 实战之监控梯子流量](https://github.com/superleeyom/blog/issues/19)--2021-02-01 61 | - [redis大key内存分析](https://github.com/superleeyom/blog/issues/17)--2021-01-19 62 | - [主流分布式id方案总结](https://github.com/superleeyom/blog/issues/16)--2021-01-10 63 | - [常用linux进程查询命令](https://github.com/superleeyom/blog/issues/15)--2021-01-03 64 | - [关于Redis缓存穿透、缓存雪崩、缓存击穿问题探究](https://github.com/superleeyom/blog/issues/13)--2020-12-23 65 | - [5分钟快速理解Redis的内存回收机制](https://github.com/superleeyom/blog/issues/12)--2020-12-16 66 | - [同一浏览器不同用户登录冲突问题探究](https://github.com/superleeyom/blog/issues/11)--2020-12-13 67 | - [GitHub Actions 实战之Chrome书签同步](https://github.com/superleeyom/blog/issues/10)--2020-12-07 68 | - [5分钟快速理解Redis的持久化](https://github.com/superleeyom/blog/issues/9)--2020-12-02 69 | - [基于Docker实现nginx-keepalived双机热备机制](https://github.com/superleeyom/blog/issues/8)--2020-11-23 70 | - [nginx负载均衡原理之ip_hash哈希算法探究](https://github.com/superleeyom/blog/issues/5)--2020-11-05 71 | - [nginx基础指令及初始配置解析](https://github.com/superleeyom/blog/issues/4)--2020-10-20 72 | - [白话解说之 BIO、NIO、AIO、异步阻塞的区别](https://github.com/superleeyom/blog/issues/3)--2020-10-17 73 | - [Java8函数式编程中比较实用的操作语法](https://github.com/superleeyom/blog/issues/2)--2020-10-09 74 | - [Java泛型的回顾之旅](https://github.com/superleeyom/blog/issues/1)--2020-10-09 75 |
76 | 77 | - [[笔记]神的九十亿个名字](https://github.com/superleeyom/blog/issues/50)--2022-06-25 78 | - [[笔记]盐糖脂:食品巨头是如何操纵我们的](https://github.com/superleeyom/blog/issues/49)--2022-04-02 79 | - [[笔记]精力管理](https://github.com/superleeyom/blog/issues/44)--2022-01-17 80 | - [[笔记]半小时漫画经济学4:理财篇](https://github.com/superleeyom/blog/issues/34)--2021-07-12 81 | - [[笔记]指数基金投资指南](https://github.com/superleeyom/blog/issues/28)--2021-04-19 82 |
显示更多 83 | 84 | - [[笔记]人生海海](https://github.com/superleeyom/blog/issues/25)--2021-03-20 85 | - [[笔记]最好的告别](https://github.com/superleeyom/blog/issues/23)--2021-03-10 86 | - [[笔记]失明症漫记](https://github.com/superleeyom/blog/issues/14)--2020-12-31 87 | - [[笔记]睡眠革命](https://github.com/superleeyom/blog/issues/7)--2020-11-22 88 |
89 | 90 | -------------------------------------------------------------------------------- /backup/10_GitHub.Actions.实战之Chrome书签同步.md: -------------------------------------------------------------------------------- 1 | # [GitHub Actions 实战之Chrome书签同步](https://github.com/superleeyom/blog/issues/10) 2 | 3 | 之前对 `GitHub Actions` 不是特别熟悉,以为它适合于跑类似于脚本语言 `Python`,不太适合与 `Java` 这类需要借助于 JVM 的语言,恰好最近有一个简单的想法就是想把 `Chrome` 书签同步到 `Github`,并将书签生成 `README.md` 文件,就尝试下用 `GitHub Actions` 去构建 `Java`,实际验证了其实是可行的,`GitHub Actions` 完全可以跑 `Java`做一些自动化操作。 4 | 5 | ## 什么是 GitHub Actions 6 | 7 | 官网的定义就是: 8 | 9 | > 在 GitHub Actions 的仓库中自动化、自定义和执行软件开发工作流程。 您可以发现、创建和共享操作以执行您喜欢的任何作业(包括 CI/CD),并将操作合并到完全自定义的工作流程中。 10 | 11 | 做 Java 的其实都知道 `Jenkins`,其实就是和 `Jenkins`差不多,用于自动化构建的,只不过 `GitHub Actions`基于 Github 平台。 12 | 13 | 你只要在你的仓库下,创建`.github/workflow`目录,并在此目录下创建`*.yml`的文件,就可以开启 `GitHub Actions`,`yml` 文件主要用于配置自动化构建,这里我就拿我的这次实践的`chrome_bookmarks_sync.yml`示例: 14 | 15 | ```yml 16 | # 此 action 的名字 17 | name: ChromeBookmarksSyncApplication 18 | 19 | on: 20 | # 开启手动执行 21 | workflow_dispatch: 22 | # 触发条件,当有代码push到master分支的时候,就触发一次构建 23 | push: 24 | branches: [ master ] 25 | # 触发条件,当有pr发起的时候,就触发一次构建 26 | pull_request: 27 | branches: [ master ] 28 | 29 | # 自定义的环境变量,实际需要换成你自己的 30 | env: 31 | GITHUB_NAME: superleeyom 32 | GITHUB_EMAIL: 635709492@qq.com 33 | 34 | # 任务 35 | jobs: 36 | build: 37 | # 设置系统环境 38 | runs-on: ubuntu-latest 39 | steps: 40 | # 检出代码 41 | - uses: actions/checkout@v2 42 | # 设置jdk版本号 43 | - name: Set up JDK 1.8 44 | uses: actions/setup-java@v1 45 | with: 46 | java-version: 1.8 47 | 48 | # 执行maven命令,进行编译,并执行脚本,生成 README.md 49 | - name: execute application 50 | run: mvn -B clean compile exec:java --file pom.xml 51 | 52 | # 提交代码 53 | - name: update README.md 54 | uses: github-actions-x/commit@v2.6 55 | with: 56 | github-token: ${{ secrets.G_TOKEN }} 57 | commit-message: ":memo: update README.md" 58 | files: README.md 59 | rebase: 'true' 60 | name: ${{ env.GITHUB_NAME }} 61 | email: ${{ env.GITHUB_EMAIL }} 62 | ``` 63 | 64 | 更多的 `GitHub Actions`用例,可以参考官方的[文档](https://docs.github.com/cn/free-pro-team@latest/actions/guides/building-and-testing-java-with-maven)。 65 | 66 | ## 实现思路 67 | 68 | 其实思路很简单,首先使用 Chrome 插件「[书签同步](https://chrome.google.com/webstore/detail/%E4%B9%A6%E7%AD%BE%E5%90%8C%E6%AD%A5/fbcbemgibdnpboehnfcnkegefaomnlbk)」,将书签信息(`bookmark.json`)上传到 Github 仓库,然后通过 `github action` 去读取书签数据,然后生成` README.md` 文件。 69 | 70 | 没法科学上传的前提下,可以通过[CrxDL.COM](https://crxdl.com/)去下载该插件,关键字搜索「书签同步」进行下载安装,设置流程的话,参考插件使用指南: 71 | 72 | - 登录Github,在 `Settings->Personal access tokens->Generate new token` 生成一个访问 token 73 | 74 | - 生成的 token 需要勾选 repo 权限,保存生成的 token 75 | 76 | - 点击插件 icon,依次输入用户名、凭据、仓库名、文件存放路径(在仓库提前创建好`*.json`文件) 77 | 78 | - 如果需要记住用户数据,需要打开 `Remember Me` 开关 79 | 80 | - 填写完用户数据后,便可以进行「上传」或「下载」操作 81 | 82 | ## 自动化构建 83 | 84 | 85 | 由于项目是用 `Maven` 构建的,所以我当时的想法是通过用 `mvn clean package` 命令,写个单元测试方法,去触发并执行 Java 类方法,后面经过试验发现是可行的,但是觉得此方法比较 `low` 啊,应该是还有其他方法的,后面经过查询资料,其实 `Maven` 是可以通过插件 `exec-maven-plugin`,运行 Java main 方法: 86 | 87 | ```xml 88 | 89 | org.codehaus.mojo 90 | exec-maven-plugin 91 | 1.2.1 92 | 93 | 94 | com.bookmark.action.ChromeBookmarksSyncApplication 95 | 96 | 97 | ``` 98 | 99 | 对应的本地测试命令:`mvn clean compile exec:java`,实际的 `github action` 的 yml 文件里的写法有点区别:`mvn -B clean compile exec:java --file pom.xml`,需要指定 pom 文件。另外如果你想执行 `mvn` 命令的时候传递命令参数到 main 方法,可以这样:`mvn clean compile exec:java -Dexec.args="arg0 arg1 arg2"`,这样在就可以接收到自定义参数了: 100 | 101 | ```java 102 | public class ChromeBookmarksSyncApplication { 103 | public static void main(String[] args) { 104 | // 打印:[arg0 arg1 arg2] 105 | System.out.println("打印接收到的参数:"+JSONUtil.toJsonStr(args)); 106 | GenerateReadmeUtil.generateReadme(); 107 | System.exit(0); 108 | } 109 | } 110 | ``` 111 | 112 | 这样是不是我们可以在 `yml` 配置中自定义的参数,就可以通过 `mvn` 命令传递进来呢?对吧? 113 | 114 | ## 文件路径问题 115 | 116 | 关于文件读取和写入的路径问题,实际我们在**本地测试**的时候,对于 `bookmark.json`和`README.md`应该取绝对路径,在`GenerateReadmeUtil.java`类中: 117 | 118 | ```java 119 | private static final String BOOKMARK_JSON_PATH = "/Users/leeyom/workspace/github/chrome-bookmarks-sync/bookmark.json"; 120 | private static final String README_PATH = "/Users/leeyom/workspace/github/chrome-bookmarks-sync/README.md"; 121 | ``` 122 | 123 | 但是实际在 `github action` 中,取的是相对地址,如果取绝对地址,会报文件找不到的问题: 124 | 125 | ```java 126 | private static final String BOOKMARK_JSON_PATH = "bookmark.json"; 127 | private static final String README_PATH = "README.md"; 128 | ``` 129 | 130 | ## 如何使用 131 | 132 | 1. fork 仓库 [chrome-bookmarks-sync](https://github.com/superleeyom/chrome-bookmark-sync)仓库 133 | 134 | 2. 修改`chrome_bookmarks_sync.yml`文件的环境变量: 135 | 136 | ```yml 137 | env: 138 | GITHUB_NAME: 改成你自己的github用户名 139 | GITHUB_EMAIL: 改成你自己的github邮箱 140 | ``` 141 | 142 | 设置 `G_TOKEN`常量,复制你创建的 `github token`,在该仓库下:`Settings-->Secrets-->New repository secret`,将此常量填入进去,变量名设置为`G_TOKEN`即可。 143 | 144 | 3. 安装 `Chrome` 插件「[书签同步](https://chrome.google.com/webstore/detail/%E4%B9%A6%E7%AD%BE%E5%90%8C%E6%AD%A5/fbcbemgibdnpboehnfcnkegefaomnlbk)」,依次输入用户名、凭据、仓库名、文件存放路径 145 | 4. 填写完用户数据后,便可以进行「上传」或「下载」操作,然后借助 `github action`,就可以自动生成 `README.md` 146 | 147 | ## 参考文档 148 | 149 | - [使用Maven运行Java main的3种方式](https://blog.csdn.net/qbg19881206/article/details/19850857) 150 | - [使用 Maven 构建和测试 Java](https://docs.github.com/cn/free-pro-team@latest/actions/guides/building-and-testing-java-with-maven) 151 | - [Chrome 书签同步到GitHub](https://www.cnblogs.com/gongkiro/p/13221739.html) 152 | - [Java-Markdown-Generator](https://github.com/Steppschuh/Java-Markdown-Generator) -------------------------------------------------------------------------------- /backup/11_同一浏览器不同用户登录冲突问题探究.md: -------------------------------------------------------------------------------- 1 | # [同一浏览器不同用户登录冲突问题探究](https://github.com/superleeyom/blog/issues/11) 2 | 3 | ## 问题 4 | 5 | 由于业务扩展问题,目前公司有 a 和 b 两个账号中心服务,分别对应的是运营端和服务商端,这两个账号系统的访问域名分别是`a.aqara.cn`和`b.aqara.cn`,其中 b 账号中心由其他的团队负责开发,用户登录成功后,会返回用户的信息(`userInfo`)和访问令牌(`token`),前端会将他们缓存在客户端的 `Cookies`里面,由于共用同一个二级域名(`.aqara.cn`),前端`Cookies` 里面缓存的数据是共用的。 6 | 7 | 就会存在这种问题:同一个浏览器,用户在标签 A 登录A用户,然后又重新打开标签页 B,登录用户 B,这样就会导致,第二个用户会把第一个用户的信息覆盖掉,但是此时用户无感知,`Cookies` 里面存储的令牌和用户信息就会被覆盖掉。这样的话假如请求的数据(比如查看个人信息)是基于 token 拿用户信息的话,由于后台的网关层,有把 token 作为键,用户信息作为 value,缓存用户用户信息,有时候就会导致 A 用户拿到 B 用户的数据。如果恰好 a 用户和 b 用户都有访问某接口的权限,就会造成,怎么我操作后,显示的操作人确实另外一个人的名字。 8 | 9 | ## 解决方案 10 | 11 | - 简单粗暴 12 | 13 | - 修改 a 和 b 两个账号中心服务的访问域名,访问域名分别是`a.aqara.cn`和`b.aqara.com`,由于是不同的二级域名,这样前端的 `Cookies`就是隔离开来的,相关之间不会有任何的影响。 这种得确认更换域名后,会不会影响其他的业务。 14 | 15 | - 服务端 16 | 17 | - 在用户登录的时候,返回用户的数据,如果不想被覆盖,只能换成不一样的,可以设置为用户名` (username)+sessionkey`使每一个用户的 `sessionkey` 都不一样,但是由于 b 账号中心的登录接口不是我们掌控的,此方案不太好实施。 18 | 19 | - 用户登陆后分配一个临时标识(sid),所有的请求和响应均携带此标识,后台用来区分用户,实际就是将判断上移到应用层面。一边这个标识生成后会放到 redis,设置一定的有效时间。伪代码如下: 20 | 21 | ```java 22 | String sid = request.getReuqestParam("sid"); 23 | String token = request.getReuqestParam("token"); 24 | String userId = redis.get("sid"); 25 | if(StrUtil.isBlank(userId)){ 26 | throw new BizException("当前用户不存在"); 27 | } 28 | String userIdFromToen = JwtUtil.parseToken("token"); 29 | if(!userIdFromToen.equal(userId)){ 30 | throw new BizException("当前用户已被替换"); 31 | } 32 | ``` 33 | 34 | - 用户在登出后,把服务端要及时的缓存的用户信息给清除掉。 35 | 36 | - 前端: 37 | 38 | - 登陆成功后将 `sid` 后存储到本地,在每个需要验证的页面加上这个参数,当用户刷新页面时与本地存储的值进行比较,不符合就跳转登陆页(或弹出提示框,提醒当前用户已更换用户,是否继续执行此操作,用户刷新页面后,就会刷新当前用户的菜单权限,用户信息等等)。 39 | 40 | - 后登陆的用户会覆盖上一个用户的本地值,而` url` 里的参数不会变所以会导致 `URL` 中获取的值和本地不一致,目前 qq 邮箱采用也是这种方式。 41 | 42 | - 示例:http://xxx.aqara.cn/retail/index.html?&sid=al5JTWX_6AzBKw5R#/provider/list 43 | 44 | - 前端伪代码: 45 | 46 | ```javascript 47 | router.beforeEach((to, from, next) => { 48 | // 工具方法,获取url地址中sid的值 49 | let urlSid = getPop('sid'); 50 | // 获取localStorege中sid的值 51 | let localSid = getLocalStorage('sid').sid 52 | console.log('url=%s,local=%s ', urlSid, localSid) 53 | // 先判断两个参数是否存在 54 | if (urlSid && localSid) { 55 | if (urlSid == localSid) { 56 | next() 57 | } else { 58 | let url = window.location.href.replace(/[\?,\#]\S*/g, ''); 59 | window.location.href = url; 60 | next({ 61 | path: "/" 62 | }); 63 | } 64 | } 65 | }) 66 | ``` 67 | 68 | ## 参考 69 | 70 | - [java 怎么让同一浏览器登录多个用户但是不共用session?](https://segmentfault.com/q/1010000011501894) 71 | - [有关 Session 的那些事儿](https://blog.by24.cn/archives/about-session.html) -------------------------------------------------------------------------------- /backup/12_5分钟快速理解Redis的内存回收机制.md: -------------------------------------------------------------------------------- 1 | # [5分钟快速理解Redis的内存回收机制](https://github.com/superleeyom/blog/issues/12) 2 | 3 | ## 设置键的生存时间 4 | 5 | - `EXPIRE key seconds`:用于设置秒级精度的生存时间,它可以让键在指定的秒数之后自动被移除 6 | - `PEXPIRE key milliseconds`:用于设置毫秒级精度的生存时间,它可以让键在指定的毫秒数之后自动被移除 7 | - `EXPIREAT key timestamp`:将键 key 的过期时间设置为 timestamp 所指定的秒数时间戳 8 | - `PEXPIREAT key timestamp`:将键 key 的过期时间设置为 timestamp 所指定的毫秒数时间戳 9 | 10 | 虽然有多种不同单位和不同形式的设置命令,但实际上`EXPIRE`、`PEXPIRE`、`EXPIREAT` 三个命令都是使用`PEXPIREAT`命令来实现的:无论客户端执行的是以上四个命令中的哪一个,经过转换之后,最终的执行效果都和执行`PEXPIREAT`命令一样。 11 | 12 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20201207155506.png) 13 | 14 | 在使用键过期功能时,组合使用 `SET `命令和 `EXPIRE/PEXIRE` 命令的做法非常常见: 15 | 16 | - `SET key value [EX seconds] [PX milliseconds]`:在设置 key 的时候,同时设置过期时间,此命令等价于两条命令: 17 | 18 | ``` 19 | SET key value 20 | EXPIRE key seconds 21 | ``` 22 | 23 | 使用带有 `EX` 选项或 `PX` 选项的` SET` 命令除了可以减少命令的调用数量并提升程序的执行速度之外,更重要的是保证了操作的**原子性**,使得「为键设置值」和「为键设置生存时间」这两个操作可以一起执行。如果拆分成 `SET` 和 `EXPIRE` 两条命令执行的话,如果 Redis 服务器在成功执行 `SET` 命令之后因为故障下线,导致 `EXPIRE` 命令没有被执行,那么 `SET` 命令设置的缓存就会一直存在,而不会因为过期而自动被移除。 24 | 25 | `EXPIREAT/PEXPIREAT`,还是 `EXPIRE/PEXIRE`,它们都只能对整个键进行设置,而无法对键中的某个元素进行设置,比如,用户只能对整个集合或者整个散列设置生存时间/过期时间,但是却无法为集合中的某个元素或者散列中的某个字段单独设置生存时间/过期时间,这也是目前 Redis 的自动过期功能的一个缺陷。 26 | 27 | ## 移除过期时间 28 | 29 | 能设置过期时间,自然也就能移除过期时间,`PERSIST`命令就是`PEXPIREAT`命令的反操作:`PERSIST key`命令在过期字典中查找给定的键,并解除键和值(过期时间)在过期字典中的关联。 30 | 31 | ## 计算并返回剩余时间 32 | 33 | `TTL` 命令以秒为单位返回键的剩余生存时间,而`PTTL`命令则以毫秒为单位返回键的剩余生存时间: 34 | 35 | ``` 36 | TTL key 37 | PTTL key 38 | ``` 39 | 40 | ## Redis如何保存过期时间 41 | 42 | Redis 里面有一个过期字典 expires,专门存储 Redis key 的过期时间,一个键的 key,有两个指向,一个指向实际的 value,一个指向它的的过期时间,如下图所示 43 | 44 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20201216170122.png) 45 | 46 | 判定一个键是否过期,主要分为两步: 47 | 48 | 1. 检查给定键是否存在于过期字典:如果存在,那么取得键的过期时间。 49 | 2. 检查当前 UNIX 时间戳是否大于键的过期时间:如果是的话,那么键已经过期;否则的话,键未过期。 50 | 51 | ## 过期键删除策略 52 | 53 | - 定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作。 54 | - 定时删除占用太多 CPU 时间,影响服务器的响应时间和吞吐量。因为在过期键比较多的情况下,删除过期键这一行为可能会占用相当一部分 CPU 时间。 55 | - 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。 56 | - 惰性删除浪费太多内存,有内存泄漏的危险。因为当过期键一直没有访问将无法得到及时删除,从而导致内存不能及时释放。 57 | - 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。 58 | - 定期删除策略是前两种策略的一种整合和折中,如果采用定期删除策略的话,服务器必须根据情况,合理地设置删除操作的执行时长和执行频率,否则到头来,还是会搞得跟「定时删除」和「惰性删除」一样。 59 | 60 | Redis 服务器实际使用的是**惰性删除**和**定期删除**两种策略,其中惰性删除策略会调用 `expireIfNeeded` 函数对键进行检查: 61 | 62 | - 如果输入键已经过期,那么 `expireIfNeeded` 函数将输入键从数据库中删除 63 | - 如果输入键未过期,那么 `expireIfNeeded` 函数不做动作 64 | 65 | 示意图如下: 66 | 67 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20201216172630.png) 68 | 69 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20201216172847.png) 70 | 71 | 而 Redis 的定期删除策略,`activeExpireCycle` 函数就会被调用,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的 expires 过期字典中随机检查一部分键的过期时间,并删除其中的过期键,如下图所示: 72 | 73 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20201216193720.png) 74 | 75 | ## Redis内存回收机制 76 | 77 | Redis 的内存回收机制主要体现在以下两个方面: 78 | 79 | - 删除到达过期时间的键对象,就是上面说的过期键删除策略。 80 | 81 | - 内存使用达到`maxmemory`上限时触发内存溢出控制策略。 82 | 83 | 当 Redis 所用内存达到 `maxmemory` 上限时会触发相应的溢出控制策略。 具体策略受 `maxmemory-policy` 参数控制,Redis 支持 6 种策略: 84 | 85 | - `noeviction`:默认策略,不会删除任何数据,拒绝所有写入操作并返 回客户端错误信息`(error)OOM command not allowed when used memory`,此时 Redis 只响应读操作。 86 | - `volatile-lru`:根据 LRU 算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到 noeviction 策略。 87 | - `allkeys-lru`:根据 LRU 算法删除键,不管数据有没有设置超时属性, 直到腾出足够空间为止。 88 | - `allkeys-random`:随机删除所有键,直到腾出足够空间为止。 89 | - `volatile-random`:随机删除过期键,直到腾出足够空间为止。 90 | - `volatile-ttl`:根据键值对象的 ttl 属性,删除最近将要过期数据。如果没有,回退到 noeviction 策略。 91 | 92 | 内存溢出控制策略可以采用如下命令动态配置: 93 | 94 | ``` 95 | config set maxmemory-policy {policy} 96 | ``` 97 | 98 | 当 Redis 一直工作在内存溢出`(used_memory>maxmemory)`的状态下且设置非 noeviction 策略时,会频繁地触发回收内存的操作,影响 Redis 服务器的性能。频繁执行回收内存成本很高,主要包括查找可回收键和删除键的开销,如果当前 Redis 有从节点,回收内存操作对应的删除命令会同步到从节点,导致写放大的问题。 99 | 100 | ## 资料 101 | - 《Redis开发与运维》 102 | - 《Redis使用手册》 103 | - 《Redis设计与实现》 104 | -------------------------------------------------------------------------------- /backup/13_关于Redis缓存穿透、缓存雪崩、缓存击穿问题探究.md: -------------------------------------------------------------------------------- 1 | # [关于Redis缓存穿透、缓存雪崩、缓存击穿问题探究](https://github.com/superleeyom/blog/issues/13) 2 | 3 | ## 缓存穿透 4 | 5 | 拿一个不存在的 key 去查询数据,如果缓存里面查询不到,就会去数据库里面查询,如果有人恶意拿不存在的 key 疯狂请求,会把数据库压垮,这就是缓存穿透,下面用一段伪代码: 6 | 7 | ```java 8 | List cacheList = redis.get(key); 9 | if(CollUtil.isEmpty(cacheList)){ 10 | List list = mysql.getList(key); 11 | if(CollUtil.isNotEmpty(list)){ 12 | redis.set(key,list,3 * 60); 13 | } 14 | return list; 15 | } 16 | return cacheList; 17 | ``` 18 | 19 | 通常来说,解决缓存穿透有两种方式: 20 | 21 | - 为不存在的 key 设置空值 22 | 23 | - 伪代码如下: 24 | 25 | ```java 26 | List cacheList = redis.get(key); 27 | if(CollUtil.isEmpty(cacheList)){ 28 | // 不管有没有在数据库中查询到数据,都给key设置值 29 | List list = mysql.getList(key); 30 | redis.set(key,list,3 * 60); 31 | return list; 32 | } 33 | return cacheList; 34 | ``` 35 | 36 | - 使用布隆过滤器 37 | 38 | - 之前在一篇公众号上看到的文章,讲解的挺好的:[《布隆过滤器究竟是什么,这篇讲的明明白白的》](https://mp.weixin.qq.com/s/Y7OJ0ntjU0pumWuwFoY8mQ) 39 | - 布隆过滤器就相当于在 Cache 之前,就做了一层过滤,防止恶意请求这种不存在的 key 40 | - 但是布隆过滤器也不是万能的,也会存在误判的可能性 41 | 42 | ## 缓存雪崩 43 | 44 | 在某个时间点,大批的 key 出现过期,导致所有的请求全部打到数据库上,把数据库压垮,这种就是缓存雪崩,通常解决缓存雪崩有如下的几种方案: 45 | 46 | - **永不过期**:设置 key 永不过期,但是这种会占用服务器挺多内存; 47 | - **过期时间错开**:比如这个 key 设置的过期时间是 5 分钟,那另外一个 key 设置的过期时间则为 7 分钟,把过期时间错开,防止在某个时间点同时失效 48 | - **多缓存结合**:在数据库和 Redis 再加一层缓存,比如 Memcache,这样的话,缓存一旦过期,Memcache 里面还可以顶一会儿; 49 | - ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20201222220143.png) 50 | - **采购第三方的 Redis 服务**:现在很多云平台都有推出 Redis 服务,有单机的,集群的,可以根据自己的使用场景去采购,当然人家也帮你处理好了这些问题,有钱啥问题都能解决。 51 | 52 | ## 缓存击穿 53 | 54 | 当前的某个热点 key 缓存过期,同一时间,有大量的请求同时来访问这个 key,导致所有的请求都打到数据库上去了,把数据库压垮。那通常遇到这种问题的话,一般就是使用排斥锁,当然也有一种粗暴的办法,就是设置永不过期,但是这种粗暴方式,大多数情况下不适用。 55 | 56 | 关于排斥锁,可以这样理解,第一个请求达到请求 key 发现缓存里面没有,允许它去数据库查询,同时加锁,这样第二个请求,第三个请求…都会被锁阻塞到当前,当第一个请求从数据库查询到数据后,将数据缓存到 Redis 中,然后释放锁,这样第二个,第三个请求...,就直接可以从缓存中拿数据,就不会再打到数据库,这样就减少了数据库的并发压力。 57 | 58 | ```java 59 | String get(String key) { 60 | String value = redis.get(key); 61 | if (value == null) { 62 | if (redis.setnx(key_mutex, "1")) { 63 | // 给锁设置一个过期时间,防止持有锁的人挂了,导致锁不能释放 64 | redis.expire(key_mutex, 3 * 60) 65 | // 从DB中查询数据并缓存 66 | value = db.get(key); 67 | redis.set(key, value); 68 | // 释放锁 69 | redis.delete(key_mutex); 70 | return value; 71 | } else { 72 | //其他线程休息100毫秒后重试 73 | Thread.sleep(100); 74 | get(key); 75 | } 76 | } 77 | return value; 78 | } 79 | ``` 80 | 81 | 其实对于这些热点 key,最好还是有个独立的服务,去定时的刷新缓存,这样的话,很大的程度上可以避免这种问题。 -------------------------------------------------------------------------------- /backup/14_[笔记]失明症漫记.md: -------------------------------------------------------------------------------- 1 | # [[笔记]失明症漫记](https://github.com/superleeyom/blog/issues/14) 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 | - 我想我们没有失明,我想我们现在是盲人;能看得见的盲人;能看但又看不见的盲人。 -------------------------------------------------------------------------------- /backup/15_常用linux进程查询命令.md: -------------------------------------------------------------------------------- 1 | # [常用linux进程查询命令](https://github.com/superleeyom/blog/issues/15) 2 | 3 | ## ps 命令详解 4 | 5 | [**ps命令详解**](https://wangchujiang.com/linux-command/c/ps.html) 6 | 7 | ## 根据进程名查询进程信息 8 | 9 | ```sh 10 | ps -ef | grep {processName} 11 | ``` 12 | 13 | ## 根据进程pid查询进程信息 14 | 15 | ```sh 16 | ps -ef | grep {pid} 17 | ``` 18 | 19 | ## 根据端口查看对应进程信息 20 | 21 | ### Linux 22 | 23 | ```shell 24 | netstat -tunlp | grep {port} 25 | ``` 26 | 27 | 示例: 28 | 29 | ``` 30 | # netstat -tunlp | grep 8080 31 | tcp6 0 0 :::8080 :::* LISTEN 29150/java 32 | ``` 33 | 34 | 则 29150 为当前端口所对应的进程 pid 35 | 36 | ### MacOS 37 | 38 | ```sh 39 | lsof -i tcp:{port} 40 | ``` 41 | 42 | ## 查看进程pid占用端口情况 43 | 44 | ### Linux 45 | 46 | ```shell 47 | netstat -nap | grep {pid} 48 | ``` 49 | 50 | ### MacOS 51 | 52 | ```sh 53 | lsof -p {pid}|grep LISTEN 54 | ``` 55 | 56 | ## 查询僵尸进程 57 | 58 | ```shell 59 | ps -A -ostat,ppid,pid,cmd | grep -e '^[Zz]' 60 | ``` 61 | 62 | - `-A` 参数列出所有进程 63 | - `-o` 自定义输出字段 我们设定显示字段为 stat(状态), ppid(进程父id), pid(进程id),cmd(命令)这四个参数 64 | - 因为状态为 z 或者 Z 的进程为僵尸进程,所以我们使用 grep 抓取 stat 状态为 z 或者 Z 进程 65 | 66 | ## 查看最消耗CPU和内存的进程 67 | 68 | ```shell 69 | # 查看最消耗CPU的进程 70 | ps -eo pid,ppid,%mem,%cpu,cmd --sort=-%cpu | head 71 | # 查看最消耗内存的进程 72 | ps -eo pid,ppid,%mem,%cpu,cmd --sort=-%mem | head 73 | ``` 74 | -------------------------------------------------------------------------------- /backup/17_redis大key内存分析.md: -------------------------------------------------------------------------------- 1 | # [redis大key内存分析](https://github.com/superleeyom/blog/issues/17) 2 | 3 | 最近 redis 的内存占用比较高,需要分析下哪些 key 内存占用率比较高,所以整理下分析的思路和笔记。 4 | 5 | ## bigkeys 6 | 7 | 使用 redis 自带的查询工具: 8 | 9 | ``` 10 | redis-cli -p 6379 -a 密码 --bigkeys 11 | ``` 12 | 13 | 比如我执行结果: 14 | 15 | ``` 16 | [root@localhost ~]# redis-cli -p 6379 -a pd123456 --bigkeys 17 | 18 | # Scanning the entire keyspace to find biggest keys as well as 19 | # average sizes per key type. You can use -i 0.1 to sleep 0.1 sec 20 | # per 100 SCAN commands (not usually needed). 21 | 22 | [00.00%] Biggest hash found so far 'alarm:monitor:virtual.67481375665548lumi.light8.0.8107' with 1 fields 23 | [27.15%] Biggest hash found so far 'alarmDefinitionCache' with 2 fields 24 | [28.14%] Biggest hash found so far 'AIOT_DEVICE_INFO' with 62867 fields 25 | [45.33%] Biggest hash found so far 'RESOURCE_LAST_TS' with 111030 fields 26 | [56.45%] Biggest set found so far 'SMART_HOTEL_USER_CACHE' with 4 members 27 | [57.53%] Biggest zset found so far '1h~keys' with 2 members 28 | 29 | -------- summary ------- 30 | 31 | Sampled 128971 keys in the keyspace! 32 | Total key length in bytes is 7387660 (avg len 57.28) 33 | 34 | Biggest set found 'SMART_HOTEL_USER_CACHE' has 4 members 35 | Biggest hash found 'RESOURCE_LAST_TS' has 111030 fields 36 | Biggest zset found '1h~keys' has 2 members 37 | 38 | 0 strings with 0 bytes (00.00% of keys, avg size 0.00) 39 | 0 lists with 0 items (00.00% of keys, avg size 0.00) 40 | 1 sets with 4 members (00.00% of keys, avg size 4.00) 41 | 128969 hashs with 337016 fields (100.00% of keys, avg size 2.61) 42 | 1 zsets with 2 members (00.00% of keys, avg size 2.00) 43 | ``` 44 | 45 | 我们可以看到打印结果分为两部分,扫描过程部分,只显示了扫描到当前阶段里最大的 key。summary 部分给出了每种数据结构中最大的 Key 以及统计信息。 46 | 47 | `redis-cli --bigkeys` 的优点是可以在线扫描,不阻塞服务;缺点是信息较少,内容不够精确。扫描结果中只有 string 类型是以字节长度为衡量标准的。List、set、zset 等都是以元素个数作为衡量标准,只能看出来一个数据结构下有多少数据,看不出来到底占多少内存,看着数值大的并不一定有问题,也不一定占用空间很大,所以这个工具只能用来做大致分析。 48 | 49 | ## redis-rdb-tools 50 | 51 | 那其实最好的办法就是离线分析,这里推荐一个工具:[redis-rdb-tools](https://github.com/sripathikrishnan/redis-rdb-tools),整体的思路就是,导出 redis 的 rdb 备份文件,生成内存报告,把所有 key 转换为 JSON,转存别的 DB 等,这里的 DB 就用 sqlite 就行。 52 | 53 | 1. 先用 redis-cli 工具连上 Redis 执行 bgsave,备份完成后,将 rdb 文件下载到本地。 54 | 55 | 2. 安装 `redis-rdb-tools`: 56 | 57 | ``` 58 | pip install rdbtools python-lzf 59 | ``` 60 | 61 | 或者: 62 | 63 | ``` 64 | git clone https://github.com/sripathikrishnan/redis-rdb-tools 65 | cd redis-rdb-tools 66 | sudo python setup.py install 67 | ``` 68 | 69 | 若提示缺少组件,按照提示安装好即可。 70 | 71 | 3. 若没有安装 `sqlite`,先安装 [sqlite](https://www.runoob.com/sqlite/sqlite-installation.html)。 72 | 73 | 4. 然后生成内存快照:`rdb -c memory dump.rdb > memory.csv`,生成 CSV 格式的内存报告,这一步可能会比较久,我处理的 rdb 文件 2 个多 g,跑了有一二十分钟,生成的 CSV 文件有 1个g的大小。包含的列有:数据库 ID,数据类型,key,内存使用量(byte),编码。内存使用量包含 key、value 和其他值。 74 | 75 | 5. 导入 `memory.csv` 到 `sqlite` 数据库,数量量比较大的话,需要等一会儿。 76 | 77 | ``` 78 | $ sqlite3 memory.db 79 | SQLite version 3.32.3 2020-06-18 14:16:19 80 | Enter ".help" for usage hints. 81 | sqlite> create table memory(database int,type varchar(128),key varchar(128),size_in_bytes int,encoding varchar(128),num_elements int,len_largest_element varchar(128)); 82 | sqlite> .mode csv memory 83 | sqlite> .import memory.csv memory 84 | ``` 85 | 86 | 6. 查询内容占用最高的几个 key: 87 | 88 | ``` 89 | sqlite> select key,size_in_bytes from memory order by size_in_bytes desc limit 11; 90 | key,size_in_bytes 91 | RESOURCE_LAST_TS,895383788 92 | AIOT_DEVICE_INFO,228425612 93 | DEVICE_STATUS_LAST_TS_ONLINE,47604980 94 | DEVICE_STATUS_LAST_TS_BIND,44006972 95 | RETAIL:TRADE:TRADE-ORDER-TEMP,22917444 96 | RETAIL:TRADE:TRADE-ORDER-MAPPER,3396012 97 | retail_biz_config_data:ticket_problem,750140 98 | areaTree,655416 99 | retail_biz_config_data:provider_product,486796 100 | ``` 101 | 102 | 经过内存分析,内存占用率排前十的key: 103 | 104 | - `RESOURCE_LAST_TS` 占用 853.9 MB 105 | 106 | - `AIOT_DEVICE_INFO` 占用 217.84 MB 107 | 108 | - `DEVICE_STATUS_LAST_TS_ONLINE` 占用 45.3996 MB 109 | 110 | - `DEVICE_STATUS_LAST_TS_BIND` 占用 41.9683 MB 111 | 112 | - `RETAIL:TRADE:TRADE-ORDER-TEMP` 占用 21.8558 MB 113 | 114 | - `RETAIL:TRADE:TRADE-ORDER-MAPPER` 占用 3.2387 MB 115 | 116 | - `retail_biz_config_data:ticket_problem` 占用 0.715389 MB 117 | 118 | - `areaTree` 占用 0.625053 MB 119 | 120 | - `retail_biz_config_data:provider_product` 占用 0.464245 MB 121 | 122 | - `retail_biz_config_data:config_fault` 占用 0.298893 MB 123 | 124 | 找到了内存占用率比较高的 key 后,就可以去针对此 key 进行下一步的优化。 125 | 126 | ## 一些总结 127 | 128 | 1. **缩短键值对的存储长度**:键值对的长度是和性能成反比的,因此在保证完整语义的同时,我们要尽量的缩短键值对的存储长度,必要时要对数据进行序列化和压缩再存储。 129 | 2. **设置键值的过期时间**:我们应该根据实际的业务情况,对键值设置合理的过期时间,这样 Redis 会帮你自动清除过期的键值对,以节约对内存的占用,以避免键值过多的堆积,频繁的触发内存淘汰策略。 130 | 3. **禁用长耗时的查询命令**:Redis 绝大多数读写命令的时间复杂度都在 O(1) 到 O(N) 之间,其中 O(1) 表示可以安全使用的,而 O(N) 就应该当心了,N 表示不确定,数据越大查询的速度可能会越慢。因为 Redis 只用一个线程来做数据查询,如果这些指令耗时很长,就会阻塞 Redis,造成大量延时。要避免 O(N) 命令对 Redis 造成的影响,可以从以下几个方面入手改造: 131 | - 决定禁止使用 keys 命令; 132 | 133 | - 避免一次查询所有的成员,要使用 scan 命令进行分批的,游标式的遍历; 134 | 135 | - 通过机制严格控制 Hash、Set、Sorted Set 等结构的数据大小; 136 | 137 | - 将排序、并集、交集等操作放在客户端执行,以减少 Redis 服务器运行压力; 138 | 139 | - 删除 (del) 一个大数据的时候,可能会需要很长时间,所以建议用异步删除的方式 unlink,它会启动一个新的线程来删除目标数据,而不阻塞 Redis 的主线程。 140 | 141 | ## 参考资料 142 | 143 | - [吐血整理Redis性能优化的13条军规!史上最全](https://cloud.tencent.com/developer/article/1606303) 144 | - [Redis内存分析方法](https://www.cnblogs.com/aresxin/p/9014617.html) 145 | - [找到 Redis 上大量占用内存的 key](https://ylgrgyq.com/find-big-keys-on-redis.html) -------------------------------------------------------------------------------- /backup/18_咕咚和keep跑步数据导入Nike.Run.Club.md: -------------------------------------------------------------------------------- 1 | # [咕咚和keep跑步数据导入Nike Run Club](https://github.com/superleeyom/blog/issues/18) 2 | 3 | 起初是 [yihong](https://github.com/yihong0618) 在v站推广他的开源跑步项目 [running_page](https://github.com/yihong0618/running_page),我那天在v站刷帖无无意看到了,就点进去了解了下,发现确实挺不错的项目,可以抓取各个平台的跑步数据,汇总聚合在一起,生成一个精美的跑步主页。后面通过 `twitter` 联系上了 yihong,yihong 是个非常热情,乐于助人的人,在他的帮助下,我成功了拿到了咕咚和 keep 上的跑步数据,并且在他的安利下,加上本身实在是受不了国内运动软件上各种广告,正式从 keep 换到了 `Nike Run Club`(后面简称 nrc)。 4 | 5 | 切换到 nrc 后,之前其实我有折腾过想把之前在咕咚和 keep 上的数据导入到 nrc,毕竟积累了好几千公里的跑量,放弃掉实在太可惜。后面通过 yihong 的提供的思路,可以尝试将咕咚、keep 的跑步数据导出 [gpx](https://zh.wikipedia.org/wiki/GPX),然后再把 gpx 导入到类似 6 | `Garmin Connect` 等平台,然后在 nrc 上与佳明进行绑定,通过曲线救国,就可以将数据导入进 nrc。 7 | 8 | > gpx 是一种 XML 格式,专门为应用软件设计的通用 GPS 数据格式,它可以用来描述路点、轨迹、路程,大部分的运动类软件都支持此类通用格式的导入。 9 | 10 | 早在一个月前,我尝试如下的的步骤: 11 | 12 | 1. 利用 [running_page](https://github.com/yihong0618/running_page) 项目,导出 gpx 数据 13 | 2. 创建一个国区 [garmin connect](https://connect.garmin.cn/) 的账号,将 gpx 数据一次性导入 Garmin Connect 14 | 3. 在 nrc 上关联 Garmin,然后数据就会自动同步过来 15 | 16 | 但是很遗憾并没有成功,后面我就没有在弄了。就在这两天,yihong 说他和另外一个网友,搞定了咕咚数据的抓取,所以又开始着手重新尝试。我仔细想了下,我当时的步骤是先创建 `Garmin Connect` 的账号,然后把 gpx 数据上传到佳明,最后再到 `nrc` 上关联 `Garmin`。是不是我的步骤不对?是不是 `Garmin` 是主动把数据推送给 `Nike` 的?所以在我没关联之前,就把数据上传了,没有触发推送?带着这些疑问,所以我又尝试了如下的步骤(最好全程都开启代理的情况下进行): 17 | 18 | 1. 创建一个**国区** [garmin connect](https://connect.garmin.cn/) 的账号,非国区可能不太行,若已有账号不需要重复创建 19 | 2. 在 nrc 上关联 Garmin,出现如下的界面说明绑定成功: 20 | - ![关联成功](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210127153147.png) 21 | - ![garmin](https://raw.githubusercontent.com/superleeyom/blog/main/img/telegram-cloud-photo-size-5-6089317861500758842-y.jpg) 22 | 3. 在 Garmin Connect 上传 gpx 数据 23 | - ![上传 gpx 数据](https://raw.githubusercontent.com/superleeyom/blog/main/img/telegram-cloud-photo-size-5-6089317861500758841-y.jpg) 24 | - ![上传 gpx 数据](https://raw.githubusercontent.com/superleeyom/blog/main/img/telegram-cloud-photo-size-5-6089317861500758843-y.jpg) 25 | 4. 打开 nrc,然后刷新数据,同步的时候可能会费点时间,如果刷新后总里程数增加了,那么恭喜你,同步成功,由于缓存的缘故,nrc 的总里程会显示不对,最好退出重新登录几次。 26 | - ![同步成功](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210127155351.png) 27 | - ![同步成功](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210127155423.png) 28 | 29 | 以上便是我整个同步过程的一些记录,如果导入后,没啥动静,建议在佳明那边删除掉已导入的 gpx 数据,在佳明那边解除 nike 绑定,然后再重新绑定,再重新导入。我觉得要想保证导入成功需要注意如下几点: 30 | 1. 确保 gpx 数据的准确性 31 | 2. 找个好的梯子,在全局代理环境下操作 32 | 3. 注意操作顺序,绑定一定要在导入 gpx 数据之前 33 | 4. 佳明账号选择国区:`connect.garmin.cn` 34 | 35 | 由于在通过 `running_page` 项目生成 keep 和咕咚的 gpx 数据的时候,由于 keep 数据不完整性,实际生成的 gpx 文件不是很完整,丢失了差不多 1000 公里的数据,但是也无所谓了,能拿到 80% 的数据我已经很开心了,哈哈。 36 | 37 | 最后贴下: 38 | - 我的跑步主页:[https://running.leeyom.top/](https://running.leeyom.top/) 39 | - 我的 `Nike Run Club` id:`635709492@qq.com`,欢迎互相关注鼓励 40 | 41 | --- 42 | 43 | > 把用Strava跑步的记录成功导入了NRC!感谢 44 | 45 | 恭喜哇 @zill057 46 | 47 | --- 48 | 49 | > Keep 成功导入了NRC 👍🏻 50 | 51 | Cool 52 | 53 | --- 54 | 55 | @AhianZhang 可以可以,已添加 -------------------------------------------------------------------------------- /backup/19_GitHub.Actions.实战之监控梯子流量.md: -------------------------------------------------------------------------------- 1 | # [GitHub Actions 实战之监控梯子流量](https://github.com/superleeyom/blog/issues/19) 2 | 3 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210131124723.png) 4 | 5 | ## 起因 6 | 7 | 最近也开通了 Netflix,Netflix 其实挺费流量的,为了防止梯子的流量超标,所以打算借助 Github Actions + telegram 做一个简单的监控,整体的思路其实很简单,没啥太大的难度,就是模拟梯子服务网站的登录,然后爬取页面的流量汇总数据,然后每天 9:30 将流量的使用情况发送到 telegram,同时如果可使用的流量少于 20% 的时候,推送报警到 telegram,代码目前放到了 github 上 [proxy-traffic-monitor](https://github.com/superleeyom/proxy-traffic-monitor),实现细节就不讲了,代码比较简单,直接看代码就行。 8 | 9 | ## 开发环境 10 | 11 | - springboot 2.0+ 12 | - jdk 1.8+ 13 | 14 | ## 准备工作 15 | 16 | 1. 创建一个 telegram bot 🤖,如果不会创建的话,参见 telegram 的官方文档:[Creating a new bot](https://core.telegram.org/bots#6-botfather),或者直接谷歌搜下,一大堆的教程,保存 `telegram bot` 的 `token`,这个很重要。 17 | 18 | 2. 创建好机器人🤖后,接下来就是要获取聊天id,也就是 `chatId` 19 | 20 | - 打开你创建的机器人,随便发点啥,比如发个:`hello world` 21 | 22 | - 浏览器输入:`https://api.telegram.org/bot(这里加上你的token)/getUpdates`,会返回如下示例: 23 | 24 | ```json 25 | { 26 | "ok": true, 27 | "result": { 28 | "message_id": 3, 29 | "from": { 30 | "id": 1432925625, 31 | "is_bot": true, 32 | "first_name": "SuperLeeyom", 33 | "username": "SuperLeeyomBot" 34 | }, 35 | "chat": { 36 | "id": 599877436, 37 | "first_name": "Leeyom", 38 | "username": "super_leeyom", 39 | "type": "private" 40 | }, 41 | "date": 1612000615, 42 | "text": "这是一条神奇的消息~" 43 | } 44 | } 45 | ``` 46 | 47 | 取到 chat 下面的 id ,这个就是聊天 id 了,比如我这里的就是 `599877436`。 48 | 49 | - 然后打开浏览器,输入:`https://api.telegram.org/bot(这里加上你的token)/sendMessage?chat_id=(你的chatId)&text=这是一条神奇的消息~`,不出意外你应该能收到一条消息,注意一定要是代理情况下你才能收到,毕竟 telegram 在国内无法使用的。 50 | 51 | 3. 准备`MonoCloud`和`ByWave`这两家的代理的账号和密码,目前我使用时这两家的服务,还行吧,价格比较贵,但是比较稳定吧。 52 | 53 | ## 如何使用 54 | 55 | - fork 项目[proxy-traffic-monitor](https://github.com/superleeyom/proxy-traffic-monitor) 56 | 57 | - 在项目的`Settings-Secrets`选项下,点击`New repository secret`,创建我们准备工作的几个工作常量,如果只用其中一家,另外一家的可以账号密码可设置为空: 58 | - `BY_WAVE_USER_NAME`:bywave 账号 59 | - `BY_WAVE_PASSWORD`:bywave 密码 60 | - `MONO_CLOUD_USER_NAME`:monoCloud 账号 61 | - `MONO_CLOUD_PASSWORD`:monoCloud 密码 62 | - `TG_CHAT_ID`:telegram 聊天 id 63 | - `TG_TOKEN`:telegram bot token 64 | 65 | - 目前有两个定时,分别是`daily.yml`和`warn.yml`,前者是每天 9:30 点执行一次,汇总流量使用情况发送到 telegram,后者是每隔 2 个小时执行一次,监控可用流量的是否已经少于 20%,若少于 20% 会推送到telegram 进行预警,若要调整时间,可以修改这两个 yml 的 `cron` 表达式。 66 | 67 | - 我这里默认关闭`warn.yml`这个自动化任务了,因为我发现,ByWave 好像已经对对 github actions 的 ip做限制了,可能我测试的太频繁了吧😂,自己有需要的再打开这个注释吧 68 | 69 | ```yml 70 | on: 71 | workflow_dispatch: 72 | # schedule: 73 | # - cron: "0 */2 * * *" 74 | ``` 75 | 76 | - ByWave 有防爬虫机制,所以定时任务太频繁,有可能会被限制 ip 地址,导致 github actions 自动化执行的时候,无法登录,如果被限制了,可以通过更换代理 ip 的方式: 77 | 78 | ```java 79 | Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("xxx.xxx.xxx.xxx", 80)); 80 | loginRequest.setProxy(proxy); 81 | ``` 82 | 83 | - 如果喜欢,就点个 star 吧,以上就是这些了!Enjoy! 84 | 85 | ## 声明 86 | 87 | 本源码只用于学习和交流,禁止用于商业目的。 -------------------------------------------------------------------------------- /backup/20_为Git和Maven设置代理加速.md: -------------------------------------------------------------------------------- 1 | # [为Git和Maven设置代理加速](https://github.com/superleeyom/blog/issues/20) 2 | 3 | ## Git 4 | 由于 `GFW` 的缘故,有时候要去 `Github` 上克隆代码,半天 `git clone` 不下来,改过 `host`,设置过代理镜像,发现根本不管用,最后整来整去,花钱买个好点的梯子,设置好 `Git` 代理,要省不少事情。 5 | 6 | ### 全局代理 7 | 8 | 为 `Git` 设置全局代理(前提你已经买了比较好的梯子),根据代理协议的不同,在终端执行如下命令: 9 | 10 | ```sh 11 | # 代理协议是socket5,我这里监听端口是1086,实际改成你自己的监听端口 12 | git config --global http.proxy socks5://127.0.0.1:1086 13 | git config --global https.proxy socks5://127.0.0.1:1086 14 | # 代理协议是http,用这个,实际改成你自己的监听端口 15 | git config --global http.proxy http://127.0.0.1:1080 16 | git config --global https.proxy https://127.0.0.1:1080 17 | ``` 18 | 19 | 在哪里可以查看梯子的代理协议?比如我用的是 [ClashX](https://github.com/yichengchen/clashX),截图如下: 20 | 21 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210203160705.png) 22 | 23 | 如果是 [Shadowsocks](https://github.com/paradiseduo/ShadowsocksX-NG-R8) 截图如下: 24 | 25 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210203161326.png) 26 | 27 | ### 部分代理 28 | 29 | 我们大部分情况下,由于 `GFW` 的缘故,只需要对 `Github` 设置代理,国内的比如 `Gitee` 其实没有必要走代理,推荐这样设置,只针对 `Github` 设置部分代理: 30 | 31 | ```shell 32 | # 代理协议是socket5(推荐) 33 | git config --global http.https://github.com.proxy socks5://127.0.0.1:1086 34 | git config --global https.https://github.com.proxy socks5://127.0.0.1:1086 35 | # 代理协议是http 36 | git config --global http.https://github.com.proxy http://127.0.0.1:1080 37 | git config --global https.https://github.com.proxy http://127.0.0.1:1080 38 | ``` 39 | 40 | ### 取消代理 41 | 42 | 取消 `Git` 的全局/部分代理: 43 | 44 | ```shell 45 | git config --global --unset http.proxy 46 | git config --global --unset https.proxy 47 | ``` 48 | 49 | ### 速度对比 50 | 51 | 没有设置代理前,平均 `6.00 KiB/s`: 52 | 53 | ``` 54 | $ git clone https://github.com/mybatis/mybatis-3.git 55 | Cloning into 'mybatis-3'... 56 | remote: Enumerating objects: 3, done. 57 | remote: Counting objects: 100% (3/3), done. 58 | remote: Compressing objects: 100% (3/3), done. 59 | ^Cceiving objects: 0% (86/352273), 44.00 KiB | 6.00 KiB/s 60 | ``` 61 | 62 | 设置代理后,平均 `6.90 MiB/s`: 63 | 64 | ``` 65 | $ git clone https://github.com/mybatis/mybatis-3.git 66 | Cloning into 'mybatis-3'... 67 | remote: Enumerating objects: 3, done. 68 | remote: Counting objects: 100% (3/3), done. 69 | remote: Compressing objects: 100% (3/3), done. 70 | remote: Total 352273 (delta 0), reused 0 (delta 0), pack-reused 352270 71 | Receiving objects: 100% (352273/352273), 104.22 MiB | 6.90 MiB/s, done. 72 | Resolving deltas: 100% (302817/302817), done. 73 | ``` 74 | 75 | 没有对比就没有伤害,`fuck GFW!!!` 76 | 77 | ## Maven 78 | 79 | `Maven` 也是跟 `Git` 一样,拉取中央仓库的依赖时候,由于 `GFW` 的缘故,不设置代理的情况下,半天依赖是拉取不下来,通过设置 `settings.xml` ,配置代理也可以解决依赖下载速度过慢的问题: 80 | 81 | ```xml 82 | 83 | 84 | ClashX 85 | true 86 | socks5 87 | 127.0.0.1 88 | 1086 89 | 90 | 172.16.xx.xx|maven.aliyun.com 91 | 92 | 93 | ``` 94 | 95 | 设置完毕后,依赖下载丝滑流畅😂,更加具体配置的可以参考 `Maven` 官方配置文档:[Configuring a proxy](https://maven.apache.org/guides/mini/guide-proxies.html),`fuck GFW!!!` 96 | 97 | 98 | -------------------------------------------------------------------------------- /backup/22_Maven中关于SNAPSHOT版本的总结.md: -------------------------------------------------------------------------------- 1 | # [Maven中关于SNAPSHOT版本的总结](https://github.com/superleeyom/blog/issues/22) 2 | 3 | ## Maven中的SNAPSHOT版本 4 | 5 | 假设有两个小组负责维护两个组件,`example-service` 和 `example-ui`,这两个组件不在同一个代码仓库,`example-service` 的版本号信息: 6 | 7 | ```xml 8 | example-service 9 | 1.0 10 | jar 11 | ``` 12 | 13 | 其中 `example-ui` 项目依赖于 `example-service`: 14 | 15 | ```xml 16 | 17 | com.xxx.yyy 18 | example-service 19 | 1.0 20 | 21 | ``` 22 | 23 | 而这两个项目每天都会构建多次,我们知道,**maven 的依赖管理是基于版本管理的,对于发布状态的 artifact,如果版本号相同,即使我们内部的镜像服务器上的组件比本地新,maven 也不会主动下载的。** 假如 `example-service` 增加了一些新的功能,这时候就得升级 `example-service` 的版本号,然后 deploy 到 maven 私服上去,由于升级了 `example-service` 的版本号为 1.1,example-ui 由于是依赖方,开发阶段,它想要使用`example-service`的新功能,则要跟着把 `example-service` 的版本号到 1.1,如果`example-service`更新的很频繁,每次构建你都要升级 `example-service` 的版本,效率就非常低。 24 | 25 | 那引入 `SNAPSHOT` 和 `RELEASE` 版本控制,这两种版本是分别在不同的 maven 仓库,前者是快照版本,用于开发环境,后者是稳定正式版本,用于生产环境,那在开发阶段,我们需要将 `example-service` 的版本号改为: 26 | 27 | ```xml 28 | example-service 29 | 1.0-SNAPSHOT 30 | jar 31 | ``` 32 | 33 | 在该模块的版本号后加上 `-SNAPSHOT `即可(注意这里必须是大写),然后 deploy 到私服,在 `maven-snapshots` 仓库下,`version` 列根据发布时间不同自动在 1.0 后面加上了当前时间,以此区别不同的快照版本: 34 | 35 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210223170842.png) 36 | 37 | `example-ui` 项目里,引入 `example-service` 快照版本: 38 | 39 | ```xml 40 | 41 | com.xxx.yyy 42 | example-service 43 | 1.0-SNAPSHOT 44 | 45 | ``` 46 | 47 | 这样的话,每次 `example-ui` 构建时,会优先去远程仓库中查看是否有最新的 `example-service-1.0-SNAPSHOT.jar`,不需要频繁的去修改`example-service` 的版本号。等到两个组件要正式上线,`example-service` 的版本号改为: 48 | 49 | ```xml 50 | example-service 51 | 1.1-RELEASE 52 | jar 53 | ``` 54 | 55 | 然后 deploy 到私服,`example-ui` 项目里,引入 `example-service` 正式升级版本: 56 | 57 | ```xml 58 | 59 | com.xxx.yyy 60 | example-service 61 | 1.1-RELEASE 62 | 63 | ``` 64 | 65 | 所以总的来说,对于 Maven 版本号,我们最好这样约定: 66 | 67 | 1. 【强制】开发阶段版本号定义为 SNAPSHOT,发布后版本改为 RELEASE。 68 | 2. 【强制】线上应用不要依赖 SNAPSHOT 版本(安全包除外);正式发布的类库必须先去中央仓库进行查证,使 RELEASE 版本号有延续性,版本号不允许覆盖升级。 69 | 70 | ## 参考资料 71 | 72 | - [理解Maven中的SNAPSHOT版本和正式版本](http://www.huangbowen.net/blog/2016/01/29/understand-official-version-and-snapshot-version-in-maven/) 73 | - [maven(15),快照与发布,RELEASE与SNAPSHOT](https://blog.csdn.net/wangb_java/article/details/66000956) 74 | - [MAVEN规约](https://caosg.gitbooks.io/java-devepment-rules/content/project/maven.html) 75 | 76 | -------------------------------------------------------------------------------- /backup/23_[笔记]最好的告别.md: -------------------------------------------------------------------------------- 1 | # [[笔记]最好的告别](https://github.com/superleeyom/blog/issues/23) 2 | 3 | ## 最好的告别:关于衰老与死亡,你必须知道的常识 4 | 5 | > 作者:阿图·葛文德;数量:23个笔记;时间:2021-03-10 10:12:36 6 | 7 | - **01 独立 活到100岁的代价:** 8 | - 这一天,在雅典县展览会的正面看台,在几百个欢呼雀跃的人的注视下,他宣誓成为美国公民。但是,有一个美国人的习俗他没有接纳,那就是对待老人和病弱者的方式——让他们独自生活,或者把他们丢给一系列无名的设备,让他们在生命的最后日子同几乎只知道他们名字的医生、护士一起度过。这是同他的祖国印度最不相同的一点 9 | - **田园牧歌式的老年生活:** 10 | - 健康专业人员有一个系统的标准来评估一个人的身体功能。如果在没有他人帮助的情况下不能如厕、进食、穿衣、洗浴、整容、下床、离开座椅、行走(所谓“八大日常生活活动”),那么,说明你缺少基本的生活自理能力。如果不能自行购物、做饭、清理房间、洗衣服、服药、打电话、独自旅行、处理财务(所谓“八大日常生活独立活动”),那么,你就缺少安全地独自生活的能力。 11 | - **活得久了,问题来了:** 12 | - 历史的发展轨迹非常清楚:一旦人们拥有告别传统生活方式的足够的资源和机会,他们就会义无反顾地拥抱新生活。 13 | - **人如何衰老以及为什么会老:** 14 | - 相比于平均值,寿命长短只有3%取决于父母的寿数,而高矮则90%取决于父母的身高。即便是基因相同的双胞胎,寿命差异也很大:典型的差距在15岁以上。 15 | - **03 依赖 我们为老做好准备了吗:** 16 | - 高龄老人告诉我,他们最害怕的并不是死亡,而是那之前的种种状况——丧失听力、记忆力,失去最好的朋友和固有的生活方式。正如菲利克斯对我说的:“老年是一系列连续不断的丧失。”在小说《每个人》(Everyman)中,作家菲利普·罗思(Philip Roth)说得更加苦涩:“老年不是一场战斗,而是一场屠杀。” 17 | - **老了但对生活的要求不能仅仅是安全:** 18 | - 在我们衰老脆弱、不再有能力保护自己的时候,如何使生活存在价值。 19 | - **什么时候可以考虑去老人院看一看:** 20 | - 一旦衰老导致衰弱,似乎就没人可以活得快乐。 21 | - **有没有一个真正像家的“老年之家”:** 22 | - 人类需求层次。这个理论经常被描述为一个金字塔。塔基是基本需求——生存的必需品(如食物、水、空气)和安全的必需品(如法律、秩序及稳定)。其上一个层次是爱的需求和归属感需求。再其上是成长的愿望——实现个人目标、掌握知识和技能、成就得到承认并获得奖励的机会。 23 | - 当“生命的脆弱性凸显出来”时,人们的日常生活目标和动机会彻底改变。至关紧要的是观念,而不是年龄。 24 | - **用两条狗、4只猫、100只鸟发起的革命:** 25 | - 针对厌倦感,生物会体现出自发性;针对孤独感,生物能提供陪伴;针对无助感,生物会提供照顾其他生命的机会。 26 | - **修复健康,也需滋养心灵:** 27 | - 为什么仅仅存在,仅仅有住、有吃、安全地活着,对于我们是空洞而无意义的?我们还需要什么才会觉得生命有价值? 28 | - 他认为,答案是:我们都追求一个超出我们自身的理由。对他来说,这是人类的一种内在需求。这个理由可大(家庭、国家、原则)可小(一项建筑工程、照顾一个宠物)。重要的是,在给这个理由赋予价值、将其视为值得为之牺牲之物的同时,我们赋予自己的生命以意义。 29 | - **生活中最好的事,就是能自己上厕所:** 30 | - 自主的价值……在于它所产生的责任:自主使得我们每个人负责根据某种连贯的独特的个性感、信念感和兴趣,塑造自己的生活。它允许我们过自己的生活,而不是被生活所驱使,这样,我们每个人都能够在权利框架允许的范围内,成为他塑造的那个自己。 31 | - **战胜老年生活的无聊与无助:** 32 | - 对疾病和老年的恐惧不仅仅是被迫忍受对种种丧失的恐惧,同样也是对孤独的恐惧。当人意识到生命的有限,他们就不再要求太多。他们不再寻求更多的财富,不再寻求更多的权力。他们只要求,在可能的情况下,被允许保留塑造自己在这个世界的生命故事的权利——根据自己的优先顺序作出选择,维持与他人的联系。 33 | - **大限来临该做什么:** 34 | - 问题不是我们如何能够承担这个系统的开支,而是怎样建立一个系统,能够在人们生命终结之时,帮助他们实现其最重要的愿望。 35 | - **尽全力救治也许不是最正确的做法:** 36 | - 只有不去努力活得更长,才能够活得更长。 37 | - **临终讨论专家的话术:** 38 | - 接受个人的必死性、清楚了解医学的局限性和可能性,这是一个过程,而不是一种顿悟。 39 | - **选择可以信任的医生:** 40 | - 他具有那种中西部人的特点,习惯在别人说完话后等一拍,确定别人真的说完了以后,自己才开始说话。 41 | - **三种医患关系:家长型、资讯型、解释型:** 42 | - 人们寻求的首先是信息背后的意义,而不是信息本身。传递意义的最佳途径,他说,是告诉人们信息于你而言的意义。 43 | - **艰难的谈话如何开始:** 44 | - 把今天过到最好、而不是为了未来牺牲现在 45 | - **08 勇气 最好的告别:** 46 | - 在年老和患病的时候,人至少需要两种勇气。第一种勇气是面对人终有一死的事实的勇气——寻思真正应该害怕什么、可以希望什么的勇气。这种勇气已经够难了,我们有很多理由回避它。但是更令人却步的是第二种勇气——依照我们发现的事实采取行动的勇气。 47 | - **选择比风险计算更复杂:** 48 | - 生命之所以有意义乃是因为那是一个故事。一个故事具有整体感,其弧度取决于那些有意义的时刻、那些发生了重要事情的时刻。逐刻评价人们的愉悦水平和痛苦水平忽视了人类存在的这一根本面向。表面看似幸福的生命可能是空虚的,而一个表面看似艰难的生活可能致力于一项伟大的事业。 49 | - **善终不是好死而是好好活到终点:** 50 | - 我们在对待病人和老人方面最残酷的失败,是没有认识到,除了安全和长寿,他们还有优先考虑事项;建构个人故事的机会是维持人生意义的根本;通过改变每个人生命最后阶段的可能性这一方式,我们有机会重塑我们的养老机构、我们的文化和我们的对话。 51 | - **和父亲最后的对话:** 52 | - 结尾不仅仅是对死者重要,也许,对于留下的人,甚至更重要 53 | 54 | -------------------------------------------------------------------------------- /backup/24_关于prometheus无法采集服务指标的问题总结.md: -------------------------------------------------------------------------------- 1 | # [关于prometheus无法采集服务指标的问题总结](https://github.com/superleeyom/blog/issues/24) 2 | 3 | ## 名词解析 4 | 5 | ### context-path 6 | 7 | 对应的 Spring Boot 后台服务,如果增加了 `server.servlet.context-path` 配置,则会指定项目路径,是构成 url 地址的一部分,比如,在没有加此配置前,我们获取用户列表接口是这样访问: 8 | 9 | ``` 10 | http://127.0.0.1:8090/user/list 11 | ``` 12 | 13 | 设定项目路径,`server.servlet.context-path=demo`,则用户访问接口路径变为: 14 | 15 | ``` 16 | http://127.0.0.1:8090/demo/user/list 17 | ``` 18 | 19 | ### prometheus 20 | 21 | 中文名称叫「普罗米修斯」,普罗米修斯主要用于事件监控和警告,它可以和 Spring Boot 的子项目 Spring Boot Actuator 进行整合,它为应用提供了强大的监控能力,目前网上有很多的整合的示例,本文不在这里细讲了: 22 | 23 | - [Spring Boot 2.x监控数据可视化(Actuator + Prometheus + Grafana手把手)](http://itmuch.com/spring-boot/actuator-prometheus-grafana/) 24 | - [SpringBoot集成prometheus](https://www.cnblogs.com/xidianzxm/p/11542135.html) 25 | - [使用 Prometheus & Grafana 监控你的 Spring Boot 应用](https://y0ngb1n.github.io/a/monitoring-your-springboot-app-with-prometheus-grafana.html) 26 | - [基于Prometheus搭建SpringCloud全方位立体监控体系](https://www.cnblogs.com/throwable/p/9346547.html) 27 | 28 | ## 起因 29 | 30 | 在 SkyWalking 上监控到,有很多服务的普罗米修斯监控请求,出现了 404: 31 | 32 | ![](http://image.leeyom.top/blog/20210312103431.png) 33 | 34 | 后面经过排查,就是由于应用设置了 `context-path`的原因造成的,由于普罗米修斯监控站点走的是 35 | `http://${host}:${port}/actuator/prometheus`这种 url,但是实际我们的服务都是加了`context-path`,也就是 36 | `http://${host}:${port}/${context-path}/actuator/prometheus`,就导致普罗米修斯在 fetch 的时候,直接404,无法获取监控信息。 37 | 38 | ## 解决方案 39 | 40 | 由于 prometheus 是通过 Eureka 发现服务的,观察 prometheus 的配置文件 `prometheus.yml`: 41 | 42 | ```yaml 43 | scrape_configs: 44 | - job_name: 'eureka-prometheus' 45 | # 采集的路径 46 | metrics_path: '/actuator/prometheus' 47 | # eureka 注册中心地址 48 | eureka_sd_configs: 49 | - server: http://192.168.100.93:8761/eureka 50 | ``` 51 | 52 | 由于后台服务都是注册在 Eureka 上的,比如我们查看某个服务在 Eureka 上的注册信息,浏览器访问:`http://192.168.100.93:8761/eureka/apps/${application-name}`,例如这个服务返回的注册信息: 53 | 54 | ![](http://image.leeyom.top/blog/20210312110429.png) 55 | 56 | 可以看出我们并没有将服务的指标路径(抓取路径)写入到 Eureka 的元数据(metadata) 中,所以 prometheus 最终发起的获取监控信息请求是`http://ip:port+metrics_path:`,比如:`http://10.233.99.10:9425/actuator/prometheus`,那假设这个服务没有设置 `context-path`,它肯定是可以正常返回监控信息: 57 | 58 | ![](http://image.leeyom.top/blog/20210312111723.png) 59 | 60 | 如果设置了 `context-path`,它最终依旧还是以 `http://10.233.99.10:9425/actuator/prometheus` 去访问,那肯定就会提示 404 了。 61 | 62 | 加了`server.servlet.context-path`以后,抓取的路径就不再是` http://10.233.99.10:9425/actuator/prometheus`了,而是变成了 `http://10.233.99.10:9425/inventory/actuator/prometheus`了。之前我们 `prometheus.yml` 文件里静态配置抓取目标的 `metrics_path`是`/actuator/prometheus`,但是现在不能这样写了,因为加了应用上下文路径,而且每个服务都不一样,所以为了能够根据各服务动态自定义指标路径,需要如下处理: 63 | 64 | 1. 在服务的`application.yml`文件里,增加如下的配置: 65 | 66 | ```yaml 67 | eureka: 68 | instance: 69 | metadata-map: 70 | "prometheus.scrape": "true" 71 | "prometheus.path": "${server.servlet.context-path}/actuator/prometheus" 72 | "prometheus.port": "${server.port}" 73 | ``` 74 | 75 | prometheus 是通过 Eureka 发现服务的,因此只有将服务的指标路径(抓取地址)写到 Eureka 里,prometheus 才能拿到,换言之,只有服务在注册的时候,将自己暴露的端点(endpoint)以元数据的方式写到 Eureka 中, prometheus 才能正确的从目标抓取数据。 76 | 77 | ![](http://image.leeyom.top/blog/20210312121000.png) 78 | 79 | 2. 修改 `prometheus.yml`,去掉指定的`metrics_path`, 改为通过 Eureka 获取抓取目标: 80 | 81 | ```yaml 82 | scrape_configs: 83 | - job_name: 'eureka-prometheus' 84 | eureka_sd_configs: 85 | - server: http://192.168.100.93:8761/eureka 86 | relabel_configs: 87 | - source_labels: [__meta_eureka_app_instance_metadata_prometheus_path] 88 | action: replace 89 | target_label: __metrics_path__ 90 | regex: (.+) 91 | ``` 92 | 93 | 3. 重启对应的后台服务,不出意外,prometheus 就能正常的获取监控信息了。 94 | 95 | ## 参考资料 96 | 97 | - [prometheus 自定义指标](https://www.cnblogs.com/cjsblog/p/14505817.html) 98 | - [prometheus 官方文档](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#configuration) 99 | - [Spring Boot Admin2.X监控的服务context-path问题](https://cloud.tencent.com/developer/article/1422173) 100 | - [解决 Spring Cloud 的服务应用配置 context-path 后 Spring Boot Admin 监控不到信息的问题](https://www.javatt.com/p/16651) 101 | - [使用 Prometheus & Grafana 监控你的 Spring Boot 应用](https://y0ngb1n.github.io/a/monitoring-your-springboot-app-with-prometheus-grafana.html) 102 | - [Eureka详解](https://www.sakuratears.top/blog/Eureka%E8%AF%A6%E8%A7%A3.html#%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90) -------------------------------------------------------------------------------- /backup/25_[笔记]人生海海.md: -------------------------------------------------------------------------------- 1 | # [[笔记]人生海海](https://github.com/superleeyom/blog/issues/25) 2 | 3 | 《人生海海》这本书真的写的太棒了,麦家老师的文笔优美,平易近人,接地气,同时风趣幽默,最后看完本书后,鼻子酸酸的,满心的感受,上校的人生经历扑朔迷离,里面有一句话特别感动: 4 | 5 | > 世上只有一种英雄主义,就是在认清了生活真相后依然热爱生活。 6 | 7 | 真的特别推荐阅读! 8 | 9 | ## 人生海海 10 | 11 | > 作者:麦家;数量:39个笔记;时间:2021-03-20 11:23:07 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 | -------------------------------------------------------------------------------- /backup/26_关于多表关联查询的优化思路.md: -------------------------------------------------------------------------------- 1 | # [关于多表关联查询的优化思路](https://github.com/superleeyom/blog/issues/26) 2 | 3 | ## 问题分析 4 | 5 | 最近在帮同事优化一个慢查询,这张主表的数量在 100w+,它具体的问题就是,查询条件非常多,大约有 30 多个可选的查询条件,这些查询的字段分散在数据库的各个表中,导致 `left join` 的表特别多,大约 `left join` 七八张表,这种情况下分页查询,查询时间在 5~6 秒,非常的影响查询体验。 6 | 7 | ```sql 8 | -- 示例伪 sql 9 | select o.id 10 | t1.name, 11 | t2.name, 12 | ... 13 | from order o 14 | left join table_1 t1 15 | left join table_2 t2 16 | left join table_3 t3 17 | left join table_4 t4 18 | left join table_5 t5 19 | ... 20 | where o.id = 1323 21 | and t1.id = 2323 22 | and t2.name = 'xxx' 23 | ... 24 | ``` 25 | 26 | ## 解决方案 27 | 28 | ### 动态 left join 29 | 30 | 通常情况下,用户使用的查询条件只会有两到三个,所以就可以根据用户实际的查询条件,动态的 `left join` 相关的表,比如 mybatis 里可以这样编写: 31 | 32 | ```xml 33 | 34 | left join ticket_product_detail tpd on t.ticket_id = tpd.ticket_id 35 | 36 | 37 | left join ticket_picking tp on tp.ticket_id = t.ticket_id 38 | 39 | ``` 40 | 这样一来,left join 的表就可以减少不少。 41 | 42 | ### 主查询只返回主表主键 id 43 | 44 | `select id from xxx` 直接使用 index 里面的值就返回结果的。但是一旦用了 `select *`,就会有其他列需要读取,这时在读完 index 以后还需要去读 data 才会返回结果。这两种处理方式性能差异非常大,特别是返回行数比较多,并且读数据需要 IO 的时候,可能会有几十上百倍的差异。主查询只返回主表 id 情况下,充分利用索引的优势,通常我们的主表存放的都是其他表的 id 字段,但是页面展示的都是 name,这时候如果我们为了省事,一次性将所需要的字段的 name `select` 出来,势必会降低查询效率,增加回表的次数,降低索引的命中率,所以大数据量情况下,将查询分散到应用层面,而非数据库层面,整体的效率会提升很大。 45 | 46 | ```sql 47 | -- 示例伪 sql 48 | select o.id 49 | from order o 50 | left join table_1 t1 51 | left join table_2 t2 52 | left join table_3 t3 53 | left join table_4 t4 54 | left join table_5 t5 55 | ... 56 | where o.id = 1323 57 | and t1.id = 2323 58 | and t2.name = 'xxx' 59 | ... 60 | ``` 61 | 62 | ### 复杂查询拆解为多次单表查询 63 | 64 | 核心思路就是,将多表关联查询,拆解为多个单表查询,然后在进行数据整合,由于是分页查询,所以主键 id 数量肯定是有限制的,通常是 10~20 个,所以在代码层面,我们批量查询主表(单表查询): 65 | 66 | ```java 67 | // 以下代码均为伪代码,只讲解下思路 68 | // 分页联合查询,但是 select 的 column 只有主表的主键 id 69 | IPage page = ticketMapper.listByPage(pageParam, ticketWebQueryDto); 70 | List ticketIdList = list.stream().map(TicketWebListDto::getTicketId).collect(Collectors.toList()); 71 | // 批量单表查询主表 72 | List ticketPageList = ticketService.getTicketPageListByTicketIds(ticketIdList); 73 | Map ticketMap = ticketPageList.stream().collect(Collectors.toMap(Ticket::getTicketId, ticket -> ticket)); 74 | ``` 75 | 76 | 收集其他需要 left join 表的主键id,并进行多次单表查询: 77 | ```java 78 | // 收集其他表的主键id 79 | List workerIdList = new ArrayList<>(ticketPageList.size()); 80 | List providerIdList = new ArrayList<>(ticketPageList.size()); 81 | List customerAddressIdList = new ArrayList<>(ticketPageList.size()); 82 | for (Ticket ticket : ticketPageList) { 83 | workerIdList.add(ticket.getWorkerId()); 84 | providerIdList.add(ticket.getProviderId()); 85 | customerAddressIdList.add(ticket.getCustomerAddressId()); 86 | } 87 | 88 | // 单表查询1 89 | List providerWorkerList = CollUtil.emptyIfNull(providerWorkerService.getWorkerInfoByWorkerIds(workerIdList)); 90 | // 单表查询2 91 | List providerObjList =CollUtil.emptyIfNull(providerObjService.getProviderInfoByProviderIds(providerIdList)); 92 | // 单表查询3 93 | List addressList = CollUtil.emptyIfNull(customerRecvAddressService.getCustomerInfoByAddressIds(customerAddressIdList)); 94 | ``` 95 | 96 | 将查询到的单表数据进行内存映射,构建k-v键值对,key 是单表主键id,value 就是我们查询到的数据,这一步的目的是为了接下来循环的构建返回前端视图层 VO 的时候,直接就可以从内存里面获取我们的单表数据: 97 | 98 | ```java 99 | Map workerMap = providerWorkerList.stream().collect(Collectors.toMap(ProviderWorker::getWorkerId, providerWorker -> providerWorker)); 100 | Map providerObjMap = providerObjList.stream().collect(Collectors.toMap(ProviderObj::getProviderId, providerObj -> providerObj)); 101 | Map customerInfoVOMap = addressList.stream().collect(Collectors.toMap(CustomerInfoVO::getAddressId, customerInfoVO -> customerInfoVO)); 102 | ``` 103 | 循环遍历,开始构建视图层 VO : 104 | 105 | ```java 106 | // 构建视图层 107 | List list = page.getRecords(); 108 | list.forEach(t ->{ 109 | 110 | // 从内存中获取构建的数据 111 | Ticket ticketPage = ticketMap.get(t.getTicketId()); 112 | ProviderObj providerObj = ObjectUtil.defaultIfNull(providerObjMap.get(ticketPage.getProviderId()), new ProviderObj()); 113 | ProviderWorker providerWorker = ObjectUtil.defaultIfNull(workerMap.get(ticketPage.getWorkerId()), new ProviderWorker()); 114 | CustomerInfoVO customerInfoVO = ObjectUtil.defaultIfNull(customerInfoVOMap.get(ticketPage.getCustomerAddressId()), new CustomerInfoVO()); 115 | 116 | // 设置value 117 | t.setProviderName(providerObj.getProviderName()); 118 | t.setWorkerName(providerWorker.getWorkerName()); 119 | t.setCustomerName(customerInfoVO.getName()); 120 | // ... 121 | }); 122 | return page; 123 | ``` 124 | 125 | ## 总结 126 | 127 | 1. 在数据量比较大的情况下,多表关联查询,拆解为多个单表查询,可以提高整体的查询效率 128 | 2. for 循环里面,不能有查询 DB 的操作,应该在 for 循环外批量从 DB 取出后,映射到内存中,然后 for 循环从内存中读取 -------------------------------------------------------------------------------- /backup/28_[笔记]指数基金投资指南.md: -------------------------------------------------------------------------------- 1 | # [[笔记]指数基金投资指南](https://github.com/superleeyom/blog/issues/28) 2 | 3 | ## 指数基金投资指南 4 | 5 | > 作者:银行螺丝钉;数量:33个笔记;时间:2021-04-19 22:17:00 6 | 7 | - **序言一:** 8 | - 而且长期定投,天然就是长期持有的。股票市场的收益并不是均匀的,80%的收益出现在20%的时间里。只有长期持有下来,才能等到下一次牛市的出现。定投自带“长期持有”这个特征,也是有利于在股票市场盈利的。 9 | - **找到长期收益率最高的资产:** 10 | - 能产生现金流的资产通常比不能产生现金流的资产长期收益率更高;能产生现金流的资产中,现金流越高,长期收益率更高。 11 | - **看收益,更要看风险:** 12 | - 长期投资股票,不考虑任何策略,只是长期持有,平均可以获得9%~15%的年复合收益率;配合正确的策略,才可以获得20%左右的年复合收益率。 13 | - **投资者笔记:** 14 | - 能够为我们“生钱”的就是资产,现金不是资产。 15 | - ·有的人看起来“富有”,但一旦停止工作,高收入也就戛然而止。 16 | - ·通过定期投资指数基金,业余投资者往往能够战胜大部分专业投资者。 17 | - **什么是指数:** 18 | - 指数是一个选股规则,它的目的是按照某个规则挑选出一篮子股票,并反映这一篮子股票的平均价格走势。 19 | - **谁开发的股票指数:** 20 | - 国内有三大指数系列。上海证券交易所(简称上交所)开发的上证系列指数,深圳证券交易所(简称深交所)开发的深证系列指数,以及中证指数有限公司开发的中证系列指数。 21 | - **指数基金是怎么来的:** 22 | - 而指数基金的业绩跟基金经理的关系不大,主要取决于对应指数的表现。 23 | - **“躺着赚钱”的指数基金:** 24 | - 股神巴菲特也提到过,买指数基金就是买国运。只要相信国家能继续发展,指数基金就能长期上涨,我们就能分享国家经济增长的收益。 25 | - **最适合普通投资者的股票基金——指数基金:** 26 | - 国内大多数的家庭,目前并没有配置多少股票资产。如果想退休后过上体面的生活,必须要配置一定的股票类资产。如果每个月配合工资来定投低估值的指数基金,实际上就是对现有五险一金的一个很好的补充。相当于自制了一个401(k)计划。 27 | - **行业指数基金:** 28 | - 值得投资的行业,主要有两个,一个是天生赚钱更容易的行业,另一个是具有明显强周期性的行业。 29 | - **投资者笔记:** 30 | - 投资的行业上区分,指数基金可以分为宽基和行业指数基金。 31 | - 常见宽基指数基金有:上证50、沪深300、中证500、创业板、红利、基本面、央视50、恒生、H股、上证50AH优选、纳斯达克100、标普500等。 32 | - 常见的行业指数基金有:必需消费行业的指数基金、医药行业的指数基金、可选消费行业的指数基金、养老产业的指数基金、银行业的指数基金、证券业的指数基金、保险行业的指数基金、金融行业的指数基金、地产行业的指数基金等。 33 | - **第4章 如何挑选适合投资的指数基金:** 34 | - 用价值投资的理念挑选出值得投资的指数基金,再用定投的方式去投资它,这是我们投资指数基金的核心 35 | - **价值投资的理念:** 36 | - 最常用的估值指标有哪些呢?主要是4个:市盈率、盈利收益率、市净率、股息率。 37 | - **盈利收益率法挑选指数基金:** 38 | - ·当盈利收益率大于10%时,分批投资。 39 | - ·盈利收益率小于10%,但大于6.4%时,坚定持有已经买入的基金份额。 40 | - ·当盈利收益率小于6.4%时,分批卖出基金。 41 | - 目前适合盈利收益率的品种,国内主要是上证红利、中证红利、上证50、基本面50、上证50AH优选、央视50、恒生指数和恒生中国企业指数等。这几个品种的投资很简单,当盈利收益率大于10%时就可以投资,小于6.4%时就可以卖出。 42 | - **博格公式法挑选指数基金:** 43 | - ·在股息率高的时候买入。 44 | - ·在市盈率处于历史较低位置时买入。(以上这两点往往是同时发生的。) 45 | - ·买入之后,耐心等待“均值回归”,即等待市盈率从低到高。 46 | - **指数基金估值方法小结:** 47 | - 根据指数背后公司的盈利所处的状态,我们可以把指数分为4个类别,分别是: 48 | - (1)盈利稳定的指数。 49 | - (2)盈利呈高速增长态势的指数。 50 | - (3)盈利处于不稳定状态或呈周期性变化,但行业没有长期亏损记录的指数。 51 | - (4)长期亏损的指数。 52 | - **投资者笔记:** 53 | - 格雷厄姆对价值投资有三个非常重要的理论,分别是价格与价值的关系、能力圈,以及安全边际。 54 | - 常见的估值指标有:市盈率、盈利收益率、市净率、股息率。 55 | - 挑选指数基金有两个策略:盈利收益率法和博格公式法。 56 | - 不同类型的指数基金需要使用不同的策略进行投资。比如盈利稳定的价值指数可以采用盈利收益率法来投资;成长指数可以使用博格公式法进行投资;周期指数可以使用博格公式的变种来投资;困境指数建议直接放弃。 57 | - **提高定投收益的5个小技巧:** 58 | - (1)尽量选择费率比较低的场外和场内渠道。 59 | - (2)不要过于频繁交易,以免产生较高的费用。 60 | - (3)成立1年以上的指数基金,在分红税上会更有优势一些。 61 | - 在指数基金的低估区域,分红适合再投入;而当指数基金不低估的时候,分红可以投入到其他低估的品种上。这样就能把指数基金的分红充分利用起来,获得更高的长期收益。 62 | - **投资者笔记:** 63 | - 定投是普通投资者买卖指数基金最合适的方式。 64 | - ·定投需要确定两个因素:定投的时间和定投的金额。 65 | - ·指数基金并不是任何时间都适合定投的,低估时定投才能获取更高的收益。 66 | - ·可以通过降低投资费用、正确处理基金分红、选择合适的定投频率、定期不定额等技巧,进一步提高定投指数基金的收益。 67 | - **投资者笔记:** 68 | - 构建一个适合自己的详细定投计划分为四个步骤:梳理自己的现金流、挑选好基金、构建定投计划、定期检查优化。 69 | - ·制作定投计划表,并将计划表打印出来(尽量大一些),张贴在家中显眼的位置。 70 | - ·通过参考三个定投实例完善自己的定投计划:为父母构建养老定投计划、为自己构建加薪定投计划、为子女构建教育定投计划。 71 | - **做好家庭资产配置:** 72 | - 家庭资产的配置,长期看应该以指数基金为主。指数基金在家庭资产中所占的比例,不应该低于“100-家庭成员平均年龄”%。例如家庭成员平均年龄是40岁,那最好配置60%左右的指数基金。剩余的40%,可以配置债券基金和货币基金。 73 | - **投资者笔记:** 74 | - 家庭资产配置中需要根据资金的不同使用时间来投资不同的投资品种。货币基金适合打理随取随用的资金,债券基金适合打理中短期(1~3年)的资金,而指数基金适合打理长期(3年以上)的资金。 75 | - **看到上涨,定投下不去手怎么办:** 76 | - 记住,“低估是一个区域,而不是一个点”。 77 | - **定投的“双核制”:** 78 | - 定投指数基金其实一直都是一个“双核”制:靠工资、租金等收入提供稳定的现金流,靠指数基金来放大收益。 79 | - **有恒产者有恒心:** 80 | - 有恒产者有恒心,无恒产者无恒心。持有资产,这是我们实现财务自由的必经之路;而拥有“长期持有资产”的信念,是走上财务自由的第一步。 81 | - **投资者笔记:** 82 | - 认真工作,用双手创造价值,把自己打造成“获取稳定提升的现金流”的资产,这是我们的防御武器;再将现金流定投到低估值的指数基金上,依靠低估值的指数基金来放大收益,这是我们的进攻武器。这个“双核”制定投体系,是最适合大多数人的投资思路。 83 | 84 | -------------------------------------------------------------------------------- /backup/29_为Docker.Alpine添加中文字体.md: -------------------------------------------------------------------------------- 1 | # [为Docker Alpine添加中文字体](https://github.com/superleeyom/blog/issues/29) 2 | 3 | 1. 首先提前将需要安装的字体拷贝到 Jenkins 所在的机器上(`/media/front/`目录下) 4 | 5 | ```shell 6 | cd /media/front/ 7 | ls 8 | simhei.ttf simsun.ttc 9 | ``` 10 | 11 | 2. 修改 Jenkins 的自动化配置,这里只展示核心的部分 shell 脚本片段: 12 | 13 | ```shell 14 | # 进入项目编译后的target目录 15 | cd /home/jenkins/data/soft/model/${MODEL_NAME}${BUILD_ID}/${MODEL_NAME}/target 16 | # 创建临时目录,并拷贝字体到临时目录 17 | mkdir front 18 | cp -r /media/front/* front/ 19 | ... 20 | ... 21 | # 安装字体 22 | RUN apk add --update ttf-dejavu fontconfig \ 23 | && rm -rf /var/cache/apk/* \ 24 | WORKDIR /usr/share/fonts/ 25 | COPY front/* /usr/share/fonts/ 26 | WORKDIR / 27 | ``` 28 | 29 | 解析下 shell 脚本,其中安装字体那块,是构建 Docker 镜像的部分 Dockerfile 命令,下面解析这几句命令: 30 | 31 | ```dockerfile 32 | RUN apk add --update ttf-dejavu fontconfig \ 33 | && rm -rf /var/cache/apk/* \ 34 | ``` 35 | 36 | 因为我们使用的基础镜像是`FROM retail-harbor.aqara.com/retail/apline-jdk-iptables:v0.0.1`,基于Linux 发行版`Alpine`,所以安装软件的指令是 `apk`,类似于 CentOS 的 `yum`,Ubuntu 的 `apt-get`。 37 | 38 | 由于安装字体需要安装软件`fontconfig`,所以需要执行`apk add --update ttf-dejavu fontconfig`,为了减少镜像的大小,需要删除安装后的缓存,执行`rm -rf /var/cache/apk/*`,`fontconfig`安装完成后,会自动在`/usr/share/`下创建两个目录,分别是`fontconfig`和`fonts`目录,接下来要做的就是把物理机的字体,拷贝到镜像的`fronts`目录中去。 39 | 40 | > 为了保持 `Dockerfile` 文件的可读性,可理解性,以及可维护性,建议将长的或复杂的 `RUN` 指令用反斜杠 `\` 分割成多行,参考[文档](https://vuepress.mirror.docker-practice.com/appendix/best_practices/#run)。 41 | 42 | 踩了一个坑就是,始终无法将本地的字体文件拷贝到镜像中去,镜像的定制实际上就是定制每一层所添加的配置、文件,每一个 `RUN` 的行为都会建立一层新的镜像,所以如果我没有指定工作目录 `WORKDIR`的话,实际拷贝的时候,是找不到 `/usr/share/fonts/`这个路径。 43 | 44 | >使用 `WORKDIR` 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,`WORKDIR` 会帮你建立目录,更多有关`WORKDIR` 命令,参考[文档](https://vuepress.mirror.docker-practice.com/image/dockerfile/workdir/)。 45 | 46 | ```dockerfile 47 | WORKDIR /usr/share/fonts/ 48 | COPY front/* /usr/share/fonts/ 49 | # 拷贝完字体后,将工作目录切回根目录,因为接下来是要执行根目录下shell脚本entrypoint.sh 50 | WORKDIR / 51 | USER root 52 | ENTRYPOINT ["sh","entrypoint.sh" ] 53 | ``` 54 | 55 | 这里需要注意,`COPY` 这类指令中的源文件的路径都是相对路径,比如`COPY front/* /usr/share/fonts/`,这个 `front`目录就是相对路径,因为我们此时上下文路径就是**项目编译后的target目录**: 56 | 57 | ```shell 58 | # 进入项目编译后的target目录 59 | cd /home/jenkins/data/soft/model/${MODEL_NAME}${BUILD_ID}/${MODEL_NAME}/target 60 | ``` 61 | -------------------------------------------------------------------------------- /backup/2_Java8函数式编程中比较实用的操作语法.md: -------------------------------------------------------------------------------- 1 | # [Java8函数式编程中比较实用的操作语法](https://github.com/superleeyom/blog/issues/2) 2 | 3 | ![java8-stream](https://user-images.githubusercontent.com/22115219/95572264-483a9580-0a5c-11eb-8cc7-0f908e997cc0.png) 4 | 5 | ## 分组(一对多) 6 | 假如有如下的一个数据结构: 7 | ```json 8 | [ 9 | { 10 | "userId": 1, 11 | "name": "王二狗", 12 | "className": "classA" 13 | }, 14 | { 15 | "userId": 2, 16 | "name": "李老四", 17 | "className": "classA" 18 | }, 19 | { 20 | "userId": 3, 21 | "name": "张翠花", 22 | "className": "classB" 23 | }, 24 | { 25 | "userId": 4, 26 | "name": "李雷", 27 | "className": "classB" 28 | } 29 | ] 30 | ``` 31 | 需要将它按班级`className`进行分组,即如下的数据结构: 32 | ```json 33 | { 34 | "classA": [ 35 | { 36 | "userId": 1, 37 | "name": "王二狗", 38 | "className": "classA" 39 | }, 40 | { 41 | "userId": 2, 42 | "name": "李老四", 43 | "className": "classA" 44 | } 45 | ], 46 | "classB": [ 47 | { 48 | "userId": 3, 49 | "name": "张翠花", 50 | "className": "classB" 51 | }, 52 | { 53 | "userId": 4, 54 | "name": "李雷", 55 | "className": "classB" 56 | } 57 | ] 58 | } 59 | ``` 60 | 学生实体类 `Student.java`: 61 | ```java 62 | @Data 63 | @AllArgsConstructor 64 | public class Student { 65 | 66 | private long userId; 67 | 68 | private String name; 69 | 70 | private String className; 71 | } 72 | ``` 73 | 采用Java8函数式编程 `groupingBy` 语法进行快速分组: 74 | ```java 75 | Student s1 = new Student(1, "王二狗", "classA"); 76 | Student s2 = new Student(2, "李老四", "classA"); 77 | Student s3 = new Student(3, "张翠花", "classB"); 78 | Student s4 = new Student(4, "李雷", "classB"); 79 | List list = new ArrayList<>(); 80 | list.add(s1); 81 | list.add(s2); 82 | list.add(s3); 83 | list.add(s4); 84 | Map> map = list.stream().collect(Collectors.groupingBy(Student::getClassName)); 85 | System.out.println(JSONUtil.toJsonStr(map)); 86 | ``` 87 | 88 | ## 分组(一对一) 89 | 还是这个数据结构: 90 | ```json 91 | [ 92 | { 93 | "userId": 1, 94 | "name": "王二狗", 95 | "className": "classA" 96 | }, 97 | { 98 | "userId": 2, 99 | "name": "李老四", 100 | "className": "classA" 101 | }, 102 | { 103 | "userId": 3, 104 | "name": "张翠花", 105 | "className": "classB" 106 | }, 107 | { 108 | "userId": 4, 109 | "name": "李雷", 110 | "className": "classB" 111 | } 112 | ] 113 | ``` 114 | 只不过要按用户的 `userId` 进行分组,分组后的数据格式如下: 115 | ```json 116 | { 117 | "1": { 118 | "className": "classA", 119 | "userId": 1, 120 | "name": "王二狗" 121 | }, 122 | "2": { 123 | "className": "classA", 124 | "userId": 2, 125 | "name": "李老四" 126 | }, 127 | "3": { 128 | "className": "classB", 129 | "userId": 3, 130 | "name": "张翠花" 131 | }, 132 | "4": { 133 | "className": "classB", 134 | "userId": 4, 135 | "name": "李雷" 136 | } 137 | } 138 | ``` 139 | 采用Java8函数式编程的 `toMap` 语法进行快速分组: 140 | ```java 141 | Map studentMap = list.stream().collect(Collectors.toMap(Student::getUserId, student -> student, (k1, k2) -> k1)); 142 | ``` 143 | 144 | ## 对集合中重复的元素进行去重 145 | 对于简单的集合,比如字符串,整型类的集合,采用 `distinct` 进行快速去重: 146 | ```java 147 | List strList = Arrays.asList("a", "b", "c", "c", "d"); 148 | List distinctList = strList.stream().distinct().collect(Collectors.toList()); 149 | System.out.println(JSONUtil.toJsonStr(distinctList)); 150 | ``` 151 | 对于集合元素是对象的,根据对象指定的属性进行去重: 152 | ```java 153 | // 根据className去重 154 | List unique = list.stream().collect(Collectors.collectingAndThen( 155 | Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Student::getClassName))), ArrayList::new)); 156 | System.out.println(JSONUtil.toJsonStr(unique)); 157 | ``` 158 | ```java 159 | // 根据userId和className去重 160 | List unique2 = list.stream().collect(Collectors.collectingAndThen( 161 | Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getUserId() + ";" + o.getClassName()))), ArrayList::new)); 162 | System.out.println(JSONUtil.toJsonStr(unique2)); 163 | ``` 164 | ## 对集合元素进行快速排序 165 | ```java 166 | Student s1 = new Student(1, "王二狗", "classA",10); 167 | Student s2 = new Student(2, "李老四", "classA",9); 168 | Student s3 = new Student(3, "张翠花", "classB",8); 169 | Student s4 = new Student(4, "李雷", "classB", 12); 170 | List list = new ArrayList<>(); 171 | list.add(s1); 172 | list.add(s2); 173 | list.add(s3); 174 | list.add(s4); 175 | // 按照年龄进行升序 176 | list.sort(Comparator.comparing(Student::getAge)); 177 | // 按照年龄进行降序 178 | list.sort(Comparator.comparing(Student::getAge).reversed()); 179 | ``` 180 | 181 | ## 快速判断对象集合中是否存在指定的元素 182 | 183 | ```java 184 | // 查找学生当中是否存在一个叫王二狗的同学 185 | boolean matchResult = list.stream().anyMatch(student -> "王二狗".equals(student.getName())); 186 | ``` 187 | 188 | ## 将List转变为逗号分隔的字符串 189 | 190 | ```java 191 | List cities = Arrays.asList("Milan", "London", "New York", "San Francisco"); 192 | String citiesCommaSeparated = String.join(",", cities); 193 | System.out.println(citiesCommaSeparated); 194 | // 输出: Milan,London,New York,San Francisco 195 | ``` 196 | 197 | ## 参考文章 198 | 199 | - [一文带你玩转 Java8 Stream 流,从此操作集合 So Easy](https://juejin.im/post/6844903830254010381) -------------------------------------------------------------------------------- /backup/30_我的跑步感悟.md: -------------------------------------------------------------------------------- 1 | # [我的跑步感悟](https://github.com/superleeyom/blog/issues/30) 2 | 3 | # 我的跑步感悟 4 | 5 | ## 前言 6 | 7 | 之前有看到 [@yihong](https://github.com/yihong0618) 发表的[《程序员跑步指南》](https://github.com/yihong0618/gitblog/issues/178)博客,突然也想写写最近这几年自己跑步的一些感悟,一直拖到现在,因为这只是我作为一名业余跑步爱好者的一些经验和感悟,这些建议可能缺乏专业性,毕竟每个人所处的环境,身体状况都不一样,所以仅供参考哈。 8 | 9 | ## 个人概况 10 | 11 | |距离| 最好成绩| 12 | |---|---| 13 | |5km|21m| 14 | |10km|45m| 15 | |半马|1h50m| 16 | |全马|暂时还没跑过| 17 | 18 | 19 | 20 | 今年是我今年跑步的第五个年头了,总跑量应该有 5000km+ 了吧,我是从 2017 年正式开始跑步,当时最重的时候应该有 90kg+,变胖带来的烦恼就是,每次去商场买衣服,都只能买xxxl号的,并且任何衣服上身后,那个肚子实在太明显了,但凡有段时间没见到我的人,见到我的第一句话都是:“你咋这么胖了”,所以至此开始,便下定决心开始减肥,开启了我的[跑步之旅](https://running.leeyom.top/)🏃。 21 | 22 | ## 跑步的好处 23 | 24 | 我真切感受到的好处,绝不骗人: 25 | 26 | - 减重 20kg,真是太爽了,现在吃啥都没负担 27 | 28 | - 释放多巴胺,调节情绪,享受内心短暂的平静,尤其是在 996 这种压抑环境下 29 | 30 | - 减掉多余脂肪,让身材变苗条,穿衣更好看,现在可以穿 M/L 的码了 31 | 32 | - 增强免疫力,提高身体素质,普通感冒不吃药也能自愈,脂肪肝也没有了 33 | 34 | - 增强心肺功能,上楼再也不喘了 35 | 36 | ## 跑步时间安排 37 | 38 | 其实也就两个选择,晨跑or夜跑,我自己的话,比较喜欢早上跑,所以晨跑居多,偶尔夜跑,一般尽量保持“跑一休一”的状态,也就是一周保持3到4次的跑步,每次大概在5km~10km,偶尔周末会跑10km以上,晨跑的话,一般早上5:30的闹钟,洗漱30分钟,然后6点出门前往跑步的地方。到底是晨跑好还是夜跑好,这个因人而异,选择自己合适的就好。 39 | 40 | ## 防止受伤 41 | 42 | 我在开始跑步前期,撒腿就跑,什么热身,拉伸都是不存在的,有点儿急功近利,那带来的自然就是伤痛了。所以不要嫌跑前热身/跑后拉伸的那15分钟浪费时间,这十多分钟,可以让我们远离伤痛。 43 | 44 | 我比较推荐 Nike Training 上的 「跑者放松」和「跑者热身」两个课程,跑前和跑后可以跟着做一做。 45 | 46 | ## 如何坚持跑步 47 | 48 | 我就讲讲我自己吧,我有两个方法: 49 | 50 | - 『奖励机制』:比如周末有时候当天跑了10km,那今天就奖励自己去吃顿汉堡王,或者跑步坚持一年了,给自己奖励个 Apple Watch 手表等等,这种奖励能让我的跑步有更多的成就感。 51 | 52 | - 『目标制』:我目前给自己定了的是每个月100km的跑量,其实大概算一下,如果按每次跑5km~10km的距离的话,大概一个月有15次的运动机会,也就是一个月有一半时间是锻炼状态,一般时间是休息状态,我感觉这这种大多数人都能接受吧?不用为每天都跑,而造成心理上的压力,有时候完不成也没事,尽量朝着这个目标努力靠近就行。 53 | 54 | 另外有这么种情况,有时候经常跑一个地方,跑多了会觉得无聊,至少我是这么觉得的,所以我的办法是,有时候在一个地方跑的无聊了,偶尔换个地方跑,今天操场不想跑,就去河道跑,或者干脆不设置目的地,跑到哪里算哪里,久而久之,方圆 10 公里基本上被我摸透了。 55 | 56 | ## 跑步装备和软件 57 | 58 | ### 跑步装备 59 | 60 | 啥钱都可以省,跑鞋的钱咱不能省,毕竟脚是最先个地面接触的,一双好的跑鞋,能很大程度上减少膝盖受伤,同时也能提高跑步成绩。跑鞋的话,目前只穿过 Nike 的飞马系列,其他的品牌没穿过,就不多做啥评价了,不得不说,Nike 家的飞马系列,真的是日常训练的经典跑鞋,跑步小白,不知道买啥跑鞋,可以试试飞马系列,应该不会差多少,如果要跑竞速的话,可以考虑买 Next% 系列,我的下一双鞋就准备打算买 Nike ZoomX Vaporfly Next%,更多 Nike 的跑鞋可以参考网友整理的「[Nike 跑鞋矩阵](https://zhuanlan.zhihu.com/p/188720834)」。 61 | 62 | 关于手表,有时候我的朋友问我:“最近想买个手表用来跑步,有啥推荐的吗?”,我的一般回答是:“你先坚持跑个一个月试试,如果能坚持下来,再考虑要不要买个手表。” 因为大部分人买手表都是一时冲动,买了后过几天,便束之高阁。买手表有用吗?肯定有用,监控心率,不用带手机,在线听音乐等等。我目前自己在用的是 Apple Watch 4 蜂窝版,除了续航,几乎没有啥太大的短板,使用 iPhone 的朋友可以考虑购买。 63 | 64 | ### 跑步软件 65 | 66 | 我跑步前期用的是「咕咚」和「keep」,但是越到后面,这两个 App 广告越来越多,卖货,短视频,直播,我就想跑个步,搞这么多乱七八糟的东西,之前舍不得换软件,是因为上面有太多的跑步数据舍不得,也是机缘巧合,在 V 站上遇到了 [@yihong](https://github.com/yihong0618) 在宣传他的开源项目 [running_page](https://github.com/yihong0618/running_page),在 [@yihong](https://github.com/yihong0618) 的热心帮助下,终于把自己的跑步数据拿到手了,当时还写了篇文章:[《咕咚和keep跑步数据导入Nike Run Club》](https://github.com/superleeyom/blog/issues/18)记录了下,有这个需求的朋友可以试试看。 67 | 68 | 后面就彻底换成 Nike Run Club,就凭它免费、无广告,这两点,足以让我用下去了,整体的这大半年使用下来,感觉还挺不错的,完美配合 Apple Watch。 69 | 70 | ## 关于马拉松 71 | 72 | 其实感觉如果能 10km 进 1 小时的话,我觉得半马应该没啥太大问题,当然如果有更高的追求,跑全马的话,可能就需要系统的训练,引用推友[@gdp8](https://twitter.com/gdp888) 的话: 73 | 74 | > 成绩提升是个系统工程: 跑量、核心、减重、控制饮食、避免受伤等等,如果很难做到,自己跑得开心就好。 75 | 76 | 77 | 我有幸跑过一次马拉松,哇,那现场氛围,太值了,大家一起奋力奔跑的感觉真好!如果可以,请一定要参加一场马拉松试试!不在乎成绩,只在乎过程! 78 | 79 | ## 最后 80 | 81 | 所以,你为啥要跑步?我的答案是: 82 | 83 | > 明明这么痛苦,这么难过,为什么就是不能放弃跑步?因为全身细胞都在蠢蠢欲动,想要感受强风迎面吹拂的滋味。––《强风吹拂》 84 | 85 | 86 | --- 87 | 88 | > 学习了,奖励机制很有启发。 89 | 90 | @franksun2013 跑的更远,才能吃的更多 91 | 92 | --- 93 | 94 | > 学会了把keep的数据导入到nrc中,感谢 95 | 96 | @CNZedChou cool -------------------------------------------------------------------------------- /backup/31_聊聊我整牙的那些事儿.md: -------------------------------------------------------------------------------- 1 | # [聊聊我整牙的那些事儿](https://github.com/superleeyom/blog/issues/31) 2 | 3 | # 聊聊我整牙的那些事儿 4 | 5 | ## 矫正动机 6 | 7 | 马上再过几天,就可以拆牙套了,想记录下自己这快两年整牙的辛酸史,顺便给想矫正牙齿的朋友分享点经验。因为我小时候,门牙就不太整齐,牙齿比较拥挤,并且家里当时条件也不太好,所以就没有去医院矫正,长大后,这也就成了我的一块心病,导致向人微笑的时候,常常抿嘴,不够自信。后面参加工作后,自己有了一定的积蓄后,我便开始准备着手矫正牙齿的事情,我在 19 年的国庆节向父母提了这个事情,他们当时还是挺支持我的,只是表示你要自己能受得了这份罪就去弄,不要后悔就行,就这样,我开始了我的牙齿矫正之路。 8 | 9 | ## 前期准备 10 | 11 | 在决定了准备矫正牙齿的时候,我所在的公司有位女同事,恰好也在带牙套,然后向她咨询了很多关于矫正相关的事情,比如医生的选择、矫正价格、矫正年龄一些疑问等等,了解了一些琐碎的细节后,我便开始了医院的选择,我去实际考察了几家医院,有私立的也有公立的,大概对比了下: 12 | 13 | |医院类型|价格|医生流动性|对比| 14 | |---|---|---|---| 15 | |公立医院|金属自锁25000+| 稳定|医生稳定,基本上会跟着走完整个治疗流程,价格透明,但是价格较贵| 16 | |私立医院|金属自锁15000+,可能存在乱收费现象|可能离职|医生流动性较高,主治医生可能会离职,价格较便宜,可能会有隐形消费| 17 | 18 | 19 | 20 | 最后经过评估,我最终还是选择去公立医院,贵就贵点吧,图个心安,最终选择的是「南方医科大学深圳口腔医院」,主治医生是林宝山医生,林医生人很 nice,没有给人太多的严肃感,就像朋友一样,言谈举止挺幽默的。第一天,首先先开始拍片,照了头部的 CT 扫描,林医生跟我大致跟我聊了下我目前牙齿的情况,问我目前的诉求,以及我的一些疑问,其实回想下,当时我关注的问题有如下几个: 21 | 22 | 1. 我这年龄(当时26岁),现在矫正还是否来的及吗? 23 | 24 |     牙齿矫正跟年龄其实没有太大的关系,**只要牙周健康** ,年龄都没有太大的问题,四五十岁的人都有在矫正。 25 | 26 | 2. 我是否需要拔牙? 27 | 28 | ![](http://image.leeyom.top/blog/20210705221600.png) 29 | 30 |     像我自己的话,经过医生的判断,最终需要拔掉了四颗智齿,上下左右各两颗,分了两次拔,一次两颗,拔牙那滋味,我依然清楚记得拔完牙齿后,我整个背都被汗水浸湿了,拔牙麻药消了后,真是生不如死,感觉咽口水都痛,第二天起来,脸都是肿的,总之矫正牙齿,就得做好拔牙的心里准备。 31 | 32 |     放一张当时拔牙时留下的照片: 33 | 34 | ![](http://image.leeyom.top/blog/20210705232922.png) 35 | 36 | 3. 矫正大概需要花多长时间? 37 | 38 |     一般来说,牙齿情况越复杂,所需时间越久。大部分矫正周期是 1~3 年,比较严重的骨性的,大概要 3年+,我这种由于只是牙齿不齐,医生大致说需要差不多两年(24 个月)时间,实际其实大概花了 21 个月,比预计的稍微要快点。 39 | 40 | 4. 我该选择什么样的牙套? 41 | 42 | ![](http://image.leeyom.top/blog/20210705223407.png) 43 | 44 |     我记得回形针出了一期视频「[如何科学地矫正牙齿](https://www.bilibili.com/video/BV1CE411r7TU)」,里面有讲到牙套的选择,我选择的是金属自锁牙套,没有选择陶瓷和隐适美,主要是考虑价格太贵,且矫正周期太长了,最后经过医生的建议,选择带常见的金属自锁牙套。 45 | 46 | 5. 一般多久复诊一次? 47 | 48 |     通常一个月需要去医院复诊一次,每次复诊医生都需要调整钢丝的力度,来调整牙齿的位置。所以的话,如果要矫正,一定要选择在工作地或者上学的地方矫正,否则异地复诊是很麻烦的。 49 | 50 | ## 开始矫正 51 | 52 | 我是2019年10月20日正式戴上的牙套,刚开始戴上牙套的第一个月,**异物感、口腔溃疡、托槽刮嘴、牙齿酸软无力、扎嘴、拔智齿** ,实在是太痛苦了,由于进食不太方便,整整喝了一个月的流食,那一个月瘦了好几斤。 53 | 54 | 戴上牙套后,吃东西就是个麻烦事了,吃个面条或者青菜,牙套托槽上挂了一嘴的面条和青菜,像苹果、坚果这类比较硬的东西,只能暂时说拜拜了,再个就得经常刷牙,一日三餐,不是在刷牙,就是在刷牙的路上。 55 | 56 | ## 矫正中期 57 | 58 | 大概在适应了几个月后,牙齿基本上就能和牙套友好的和解了,口腔溃疡有了很大的缓解,我也开始习惯了牙套的生活。这个时候,突然疫情就爆发了,大家纷纷都戴上了口罩,所以啊,2020 年真是矫正牙齿的大好时机啊!大家都戴着口罩,人家也看不到你有没有戴牙套啊,就能偷偷摸摸的就完成了矫正,哈哈。 59 | 60 | 矫正中期,我遇到主要三个头疼的问题: 61 | 62 | 1. 吃东西的时候,如果不注意,咬到硬的东西,托槽掉了,这个时候,又得去医院处理,然后面临医生的拷问,所以那段时间,隔三差五的跑医院,最后吃任何东西我小心翼翼的,生怕咬掉托槽。 63 | 64 | 2. 由于医生需要加力调整牙齿的移动,每次复诊完,牙齿都是酸软无力,啥也咬不动,那两三天,就只能吃流食。 65 | 66 | 3. 调整咬合的时候,牙齿垫高后,咬合面积的减小,这个时候吃东西又是个麻烦事了,没办法,咀嚼不了,只能生吞。 67 | 68 | ## 矫正后期 69 | 70 | 到了矫正后期的话,基本上就是微调,比如调中线,调咬合,这个时候,牙齿基本上都已经排齐,托槽基本上也不会掉了,就感觉,牙套跟口腔已经融为一体了,已经完全适应了它的存在,自己也不太在意别人对自己牙齿的看法,总是露出这一口钢牙对别人傻笑。当然了,矫正后期,大家可能都会遇到一个问题,就是「**牙套脸** 」,造成牙套脸的原因如下: 71 | 72 | > 在牙齿矫正期间,饮食方面的改变,通常选择吃一些容易咀嚼的食物。这样会带来咀嚼肌运动减弱,长时间可能会造成咀嚼肌群萎缩。 73 | 74 | 75 | 如果已经出现了所谓的「牙套脸」也不用太担心,**一般咀嚼功能恢复后,面部的状态也会逐渐的恢复起来** ,但是也不一定百分百的恢复,毕竟还要考虑到年龄增长带来的变化。 76 | 77 | ## 一些心得 78 | 79 | **矫正牙齿我觉得是我人生中比较重要的一件事情** ,虽然过程确实挺痛苦的,但是能带来一口整齐的牙齿和灿烂的笑容,我觉得挺值的。人类的可塑性和适应能力是很强的,哪怕是根深蒂固的牙齿,依然借助外力也可以矫正过来。所以如果你有矫正的想法,现在就开始行动吧!因为: 80 | 81 | ![](http://image.leeyom.top/blog/fc61963ce5a39adaf8583a2d30b8c212.JPG) 82 | 83 | 84 | 85 | 86 | --- 87 | 88 | > 我也准备做正畸,请问下博主。正畸中期大概是多久,另外博主现在已经回长沙了,如何处理正畸后期异地检查的问题。谢谢~ 89 | 90 | @javatang 中期可能要1年多的时间,我是取了牙套后回的长沙,基本上不存在异地检查,一般就是保持器坏了的话,就近找附近的口腔医院重新配一副就行了,所以最好还是工作的地方和正畸的医院在一座城市,这样方便些,因为矫正期间你会不停的去医院复诊,如果不在一个城市,交通成本还是蛮大的 -------------------------------------------------------------------------------- /backup/32_业务数据脱敏解决方案探究.md: -------------------------------------------------------------------------------- 1 | # [业务数据脱敏解决方案探究](https://github.com/superleeyom/blog/issues/32) 2 | 3 | # 业务数据脱敏解决方案探究 4 | 5 | ## 使用背景 6 | 7 | - 在实际的业务场景中,业务开发团队需要针对公司安全部门需求,针对涉及客户安全数据或者一些商业性敏感数据,如身份证号、手机号、银行卡号、客户号等个人信息,都需要进行数据脱敏。 8 | 9 | - 搭建和生产环境一模一样的预发布环境,需要把生产环境的**存量原文数据** 加密后存储到预发环境。 10 | 11 | ## 技术调研 12 | 13 | ### 常见的脱敏算法 14 | 15 | 目前常见的脱敏算法包括 AES 加密、K 匿名、加星、屏蔽、洗牌、全保留、格式保留、令牌化等,算法及其主要用途介绍如下: 16 | 17 | |算法|主要用途| 18 | |---|---| 19 | |[K 匿名](https://zhuanlan.zhihu.com/p/50183231)|通过如 K-匿名的算法对原始数据加入扰动和泛化,使得脱敏后的数据无法唯一对应回原数据,用于保留部分统计信息| 20 | |加星|最基础的脱敏方法,保留字段一部分信息的同时通过加星遮蔽局部信息| 21 | |屏蔽|用特殊字符如星号 * 作为掩码来将字段信息屏蔽掉| 22 | |洗牌|将目标字段在样本中洗牌,使得脱敏后的信息无法唯一对应回原始样本,常用于脱敏后作为测试样例或保留部分统计信息| 23 | |全保留|字段全保留,不脱敏,常用于字段无需脱敏的场景,如对主键不脱敏| 24 | |格式保留|保留字段的原格式的前提下将其中一部分信息进行随机化,达到脱敏的同时仍然可以提供一部分该类型的信息,常用于脱敏后作为测试样例| 25 | |令牌化|通过不可逆的算法将目标字段脱敏,但保留一份令牌使得可以在必要时还原| 26 | |AES 加密|一种对称加密算法,必要时可通过 AESKey 对脱敏结果进行还原| 27 | 28 | 29 | 30 | ### Java生态下的脱敏方案 31 | 32 | 目前 Java 生态环境下,数据脱敏中间件这块,做的最好的应该数 [ShardingSphere](https://shardingsphere.apache.org/index_zh.html),引用 ShardingSphere 官方文档关于数据脱敏模块的说明: 33 | 34 | > 数据脱敏模块属于ShardingSphere分布式治理这一核心功能下的子功能模块。它通过对用户输入的SQL进行解析,并依据用户提供的脱敏配置对SQL进行改写,从而实现对原文数据进行加密,并将原文数据(可选)及密文数据同时存储到底层数据库。在用户查询数据时,它又从数据库中取出密文数据,并对其解密,最终将解密后的原始数据返回给用户。 35 | 36 | 37 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210706222729.png) 38 | 39 | 更多关于 ShardingSphere 的数据脱敏原理,可以查阅[官方文档](https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/orchestration/encrypt/)。 40 | 41 | 那如果说,我只是想简单的做一些数据清洗和隐私脱敏,有什么的好的工具类吗?那可以使用 hutool 的 [DesensitizedUtil](https://hutool.cn/docs/#/core/工具类/信息脱敏工具-DesensitizedUtil) 工具类就行,例如: 42 | 43 | ```Java 44 | String phone = "13488883888"; 45 | String encryptPhone = DesensitizedUtil.mobilePhone(phone); 46 | // 打印:134****3888 47 | System.out.println(encryptPhone); 48 | ``` 49 | 50 | 51 | ## 技术演练 52 | 53 | 在 ShardingSphere 的关于数据脱敏的场景分析里,针对已上线的历史数据,需要业务方自己进行清洗。那怎么清洗?简单说下自己的想法: 54 | 55 | 1. 首先数据库对那些需要脱敏的列,新增额外的加密列,比如需要对 email进行脱敏,则新建额外的加密列 encrypt_email。 56 | 57 | 2. 系统接入 sharding-jdbc,并配置好脱敏规则。 58 | 59 | 3. 写个脚本,多线程同时遍历需要脱敏的历史数据,将明文列(email)取出,更新到加密列(encrypt_email),由于sharding-jdbc 会根据脱敏规则,对SQL进行解析、改写,最后加密列存储的其实是加密后的数据。 60 | 61 | 这里将使用 ShardingSphere 的 sharding-jdbc 组件简单的演示如何进行数据脱敏。 62 | 63 | ### 数据库结构 64 | 65 | user 表结构,其中 email 为明文列,encrypt_email 为密文列。 66 | 67 | ```SQL 68 | CREATE TABLE `user` ( 69 | `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', 70 | `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名', 71 | `age` int unsigned NOT NULL COMMENT '年龄', 72 | `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '明文邮箱 ', 73 | `encrypt_email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '加密邮箱', 74 | PRIMARY KEY (`id`), 75 | UNIQUE KEY `uk_email` (`email`) USING BTREE COMMENT '邮箱唯一索引' 76 | ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; 77 | ``` 78 | 79 | 80 | ### 配置解析 81 | 82 | 这里只展示 sharding-jdbc 的配置部分: 83 | 84 | ```YAML 85 | spring: 86 | # sharding-jdbc配置 87 | shardingsphere: 88 | # 数据源配置 89 | datasource: 90 | name: ds 91 | ds: 92 | driver-class-name: com.mysql.cj.jdbc.Driver 93 | type: com.zaxxer.hikari.HikariDataSource 94 | jdbc-url: jdbc:mysql://127.0.0.1:3306/shardingsphere?useUnicode=true&useSSL=true&characterEncoding=utf8 95 | username: root 96 | password: '123456' 97 | max-total: 100 98 | encrypt: 99 | # 加密器配置 100 | encryptors: 101 | # 加密器的名字,这里设置为:email_encryptor 102 | email_encryptor: 103 | # 加密方式,内置MD5/AES 104 | type: aes 105 | props: 106 | # 配置AES加密器的KEY属性 107 | aes.key.value: 123456abc 108 | # 脱敏表配置 109 | tables: 110 | # 脱敏表名 111 | user: 112 | columns: 113 | # 脱敏逻辑列,真实面向用户编写SQL 114 | email: 115 | # 存储明文列 116 | plainColumn: email 117 | # 存储加密列 118 | cipherColumn: encrypt_email 119 | # 使用的加密器 120 | encryptor: email_encryptor 121 | props: 122 | # 是否使用密文列查询 123 | query.with.cipher.column: true 124 | # 是否打印SQL,默认false 125 | sql.show: true 126 | ``` 127 | 128 | 129 | 由于使用 ShardingSphere 的数据源 datasource 后,无需再配置 Spring 自带的 datasource,另外遇到个小问题就是,如果按照 ShardingSphere 官方关于数据脱敏的 [springboot配置](https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/usage/encrypt/#%E5%9F%BA%E4%BA%8Espring-boot%E7%9A%84%E8%A7%84%E5%88%99%E9%85%8D%E7%BD%AE)(官网示例 properties 格式,实际也可以转成yml 格式): 130 | 131 | ```YAML 132 | url: jdbc:mysql://127.0.0.1:3306/shardingsphere?useUnicode=true&useSSL=true&characterEncoding=utf8 133 | ``` 134 | 135 | 136 | 会报错 `jdbcUrl is required with driverClassName`,换成` jdbc-url` 则正常启动: 137 | 138 | ```YAML 139 | jdbc-url: jdbc:mysql://127.0.0.1:3306/shardingsphere?useUnicode=true&useSSL=true&characterEncoding=utf8 140 | ``` 141 | 142 | 143 | ### 实验对比 144 | 145 | 插入一条数据: 146 | 147 | ```Java 148 | userService.save(new User("韩武江", 28, "hanwujiang@163.com")); 149 | ``` 150 | 151 | 152 | 观察控制台打印的两条log: 153 | 154 | ```Java 155 | # 逻辑SQL 156 | Logic SQL: INSERT INTO user ( name,age,email ) VALUES ( ?,?,? ) 157 | # 实际SQL 158 | Actual SQL: ds ::: INSERT INTO user ( name,age,encrypt_email, email ) VALUES (?, ?, ?, ?) ::: [韩武江, 28, dad+OyyGMYILeBGwiWHU+tmzk4uJ7CCz4mB1va9Ya1M=, hanwujiang@163.com] 159 | ``` 160 | 161 | 162 | 再观察数据库: 163 | 164 | ![](http://image.leeyom.top/blog/20210707154620.png) 165 | 166 | 加密列 encrypt_email 被 sharding-jdbc 加密存储。 167 | 168 | 查询数据,并设置 `query.with.cipher.column: true`,开启密文列查询: 169 | 170 | ```Java 171 | User user = userService.getByEmail("jianglanhe@gmail.com"); 172 | System.out.println(JSONUtil.toJsonStr(user)); 173 | ``` 174 | 175 | 176 | 观察控制台打印的log: 177 | 178 | ```Java 179 | # 逻辑SQL 180 | Logic SQL: SELECT id,name,age,email,encrypt_email FROM user WHERE email=? 181 | # 实际SQL 182 | Actual SQL: ds ::: SELECT id,name,age,encrypt_email AS email,encrypt_email FROM user WHERE encrypt_email=? ::: [dad+OyyGMYILeBGwiWHU+tmzk4uJ7CCz4mB1va9Ya1M=] 183 | # 打印结果 184 | {"encryptEmail":"dad+OyyGMYILeBGwiWHU+tmzk4uJ7CCz4mB1va9Ya1M=","name":"韩武江","id":13,"age":28,"email":"hanwujiang@163.com"} 185 | ``` 186 | 187 | 188 | 若设置`query.with.cipher.column: false`,关闭密文列查询: 189 | 190 | 观察控制台打印的log: 191 | 192 | ```Java 193 | # 逻辑SQL 194 | Logic SQL: SELECT id,name,age,email,encrypt_email FROM user WHERE email=? 195 | # 实际SQL 196 | Actual SQL: ds ::: SELECT id,name,age,email AS email,encrypt_email FROM user WHERE email=? ::: [hanwujiang@163.com] 197 | # 打印结果 198 | {"encryptEmail":"dad+OyyGMYILeBGwiWHU+tmzk4uJ7CCz4mB1va9Ya1M=","name":"韩武江","id":13,"age":28,"email":"hanwujiang@163.com"} 199 | ``` 200 | 201 | 202 | 我原本以为在开启密文查询的情况,实际SQL都 `encrypt_email AS email`了,最终返回实体里面的email应该是密文: 203 | 204 | ```Java 205 | SELECT 206 | id, 207 | `name`, 208 | age, 209 | encrypt_email AS email, 210 | encrypt_email 211 | FROM 212 | `user` 213 | WHERE 214 | encrypt_email = 'dad+OyyGMYILeBGwiWHU+tmzk4uJ7CCz4mB1va9Ya1M='; 215 | ``` 216 | 217 | 218 | ![](http://image.leeyom.top/blog/20210707161455.png) 219 | 220 | 但是对比可以发现,开启密文列查询配置后,实际sharding-jdbc会在中间把密文解密后返回,最终返回实体里面的email应该是解密后的明文,密文存在的还是在密文列 encrypt_email 中。 221 | 222 | 假如后期业务上线一段时间后,需要完全删除明文列,只保留密文列,在不改变代码的基础上是否可行?那直接在数据库层,把email列删除: 223 | 224 | ![](http://image.leeyom.top/blog/20210707163823.png) 225 | 226 | 然后修改 sharding-jdbc 配置,去掉明文列,不改写任何其他代码层面的东西: 227 | 228 | ```YAML 229 | # 脱敏表配置 230 | tables: 231 | user: 232 | columns: 233 | email: 234 | # plainColumn: email 235 | cipherColumn: encrypt_email 236 | encryptor: email_encryptor 237 | ``` 238 | 239 | 240 | 然后查询数据: 241 | 242 | ```Java 243 | // 为了演示,这里改用id查询,如果用email查询的话,肯定为空 244 | User user = userService.getById(13); 245 | System.out.println(JSONUtil.toJsonStr(user)); 246 | ``` 247 | 248 | 249 | 观察控制台,发现其实依然能正常执行,且不报错: 250 | 251 | ```Java 252 | # 逻辑SQL 253 | Logic SQL: SELECT id,name,age,email,encrypt_email FROM user WHERE id=? 254 | # 实际SQL 255 | Actual SQL: ds ::: SELECT id,name,age,encrypt_email AS email,encrypt_email FROM user WHERE id=? ::: [13] 256 | # 打印结果 257 | {"encryptEmail":"dad+OyyGMYILeBGwiWHU+tmzk4uJ7CCz4mB1va9Ya1M=","name":"韩武江","id":13,"age":28,"email":"dad+OyyGMYILeBGwiWHU+tmzk4uJ7CCz4mB1va9Ya1M="} 258 | ``` 259 | 260 | 261 | 至于为什么会正常运行,因为: 262 | 263 | > 因为有logicColumn存在,用户的编写SQL都面向这个虚拟列,Encrypt-JDBC就可以把这个逻辑列和底层数据表中的密文列进行映射转换 264 | 265 | 266 | 假如你删除 email列,但是配置中还配置了 `plainColumn: email`,那代码执行的时候,则会报错:`Cause: java.sql.SQLSyntaxErrorException: Unknown column 'email' in 'field list'`。 267 | 268 | ## 总结 269 | 270 | 我个人的感觉是 sharding-jdbc 确实做到了**屏蔽底层对数据的脱敏处理** ,但是要接入 sharding-jdbc 的前提是,**团队有制定严格的SQL规范** ,这样可能接入数据库中间件的时候,才会出现比较少的问题,对于一些老系统,动辄几百行的SQL,各种复杂函数,还是放弃接入的好,到时候只会是一步一个坑。 271 | 272 | 另外如果想要满足文章开头的第二个需求,也就是把生产库的数据同步到预发布,同时要屏蔽部分敏感数据,大部分的云厂商,都有提供脱敏工具,比如我们自己在用的腾讯云的 DBbrain,就可以支持数据脱敏,但是实际使用还不是怎么完善,有待改进。 273 | 274 | 示例代码:[shardingsphere-encrypt-jdbc-demo](https://github.com/superleeyom/shardingsphere-encrypt-jdbc-demo) 275 | 276 | 277 | -------------------------------------------------------------------------------- /backup/33_家常菜谱.md: -------------------------------------------------------------------------------- 1 | # [家常菜谱](https://github.com/superleeyom/blog/issues/33) 2 | 3 | 简单记录下自己做过的一些好吃的菜!等哪天不记得咋做的时候,可以过来翻翻看,回忆下步骤。 4 | 5 | ## 腐竹炒肉 6 | 7 | 1. 猪肉切片备用 8 | 2. 切少许姜蒜粒备用,辣椒切片备用 9 | 3. 提前将腐竹泡水备用,泡发后切小条 10 | 4. 锅中热油,下入姜蒜爆香 11 | 5. 下入切好的猪肉,翻炒一会儿 12 | 6. 加入适量的生抽,蚝油,少量胡椒粉,白糖翻炒 13 | 7. 加入切好的辣椒翻炒一会儿 14 | 8. 最后再下入泡发好的腐竹下锅翻炒,如果腐竹下锅比较早的话,容易炒散开,所以放最后面加入 15 | 9. 最后加入适量的食用盐,即可出锅装盘 16 | 17 | ## 宫保鸡丁 18 | 19 | 1. 鸡胸肉切丁,加入少许料酒,生抽,蚝油,胡椒粉,淀粉,少量食用油腌制入味 20 | 2. 切少许姜蒜粒备用 21 | 3. 半根胡萝卜,半根黄瓜切丁备用 22 | 4. 起锅烧油,姜蒜下锅爆香 23 | 5. 下入腌制好的鸡胸肉丁翻炒 24 | 6. 鸡胸肉变色后,下入少许豆瓣酱翻炒一会儿 25 | 7. 胡萝卜和黄瓜丁下锅翻炒 26 | 8. 加入花生米翻炒 27 | 9. 最后加入少许白糖翻炒一会儿即可出锅装盘 28 | 29 | ## 香菜炒牛肉 30 | 31 | 1. 牛肉切条,加入适量的生抽、酱油、孜然粉、淀粉、少量油腌制一会儿 32 | 2. 切辣椒、姜丝、蒜末备用 33 | 3. 锅中热油,下入姜丝、辣椒、蒜末爆香 34 | 4. 滑入牛肉,快速翻炒变色,加点少量料酒增香 35 | 5. 牛肉变色后,加入香菜、鸡精快速翻炒几秒,即可出锅装盘 36 | 37 | ## 青椒回锅肉 38 | 39 | 1. 五花肉放入冷水中,加点料酒,煮沸后,冷却,切片备用 40 | 2. 青椒、蒜苗、蒜头洗净切条备用 41 | 3. 切片五花肉下锅,爆炒出油,肉片卷曲即可加入豆瓣酱、蒜头 42 | 4. 炒至上色后,加入青椒 43 | 5. 青椒炒至段生后,加入少许白糖、蒜苗提味,即可出锅装盘 44 | 45 | ## 土豆黑椒牛肉 46 | 47 | 1. 牛肉切条,加入适量的生抽、黑胡椒、淀粉、耗油、料酒腌制一会儿 48 | 2. 切点小红椒、蒜头、姜丝备用 49 | 3. 土豆切丝备用 50 | 4. 锅中热油,放入姜丝、辣椒、蒜末爆香 51 | 5. 滑入牛肉,快速翻炒,锅边淋入少许料酒增香 52 | 6. 牛肉差不多后,加入土豆丝翻炒,土豆丝炒熟后,再撒入少许黑胡椒 53 | 7. 加入少许盐,即可出锅装盘 54 | 55 | ## 香菇板栗炖鸡 56 | 57 | 1. 锅中下少许油,下板栗翻炒2分钟,然后盛出备用 58 | 2. 锅中补一些油,下入姜蒜爆香 59 | 3. 然后倒入切好的鸡块翻炒,加入酱油、耗油上色 60 | 4. 加入板栗,香菇,翻炒均匀,然后加入水,盖盖子炖30分钟 61 | 5. 炖的差不多后,加入少许盐、胡椒粉、白糖调味,即可出锅 62 | 63 | ## 金针菇番茄牛肉 64 | 65 | 1. 牛肉切薄片,加入少许生抽、料酒、白糖,鸡蛋清腌制 66 | 2. 金针菇洗净,切掉根部备用,准备蒜头和姜片备用 67 | 3. 番茄去皮,然后切碎,备用 68 | 4. 锅中加入少许油,放入蒜头爆香,番茄下锅炒出汁水 69 | 5. 加入少许切好的洋葱,继续翻炒 70 | 6. 锅中加入适量的水 71 | 7. 加入适量生抽,白糖,蚝油,转中小火把番茄和洋葱煮烂 72 | 8. 加入金针菇,加入适量的盐调味 73 | 9. 最后把腌制好的牛肉加入,等牛肉变色1分钟后即可出锅装盘 74 | 75 | 76 | -------------------------------------------------------------------------------- /backup/34_[笔记]半小时漫画经济学4:理财篇.md: -------------------------------------------------------------------------------- 1 | # [[笔记]半小时漫画经济学4:理财篇](https://github.com/superleeyom/blog/issues/34) 2 | 3 | # 《半小时漫画经济学4:理财篇》读书笔记 4 | 5 | 在地铁上看完的,篇幅不长,算是一个科普理财的小册子,半小时到一个小时就可以看完,整书用通俗的话语解释那些投资和理财的专业名词,风趣幽默,挺有意思,摘抄了几句印象比较深的句子,记录一下。 6 | 7 | ## 二、结构性存款:他们说我是保本理财的替身 8 | 9 | - 银行不管赚钱赔钱,只要理财产品一到期,都必须给大家保本金分利息,这种俗称打肿脸充胖子的行为,就叫—刚性兑付 10 | 11 | ## 四、基金定投:听说没钱没时间的人都喜欢我 12 | 13 | - 有些基金就只照着指数配置股票,这就叫被动型基金,也叫指数基金。 14 | 15 | - 股票上涨时急着抛,下跌时却不撒手,这种行为叫作处置效应。 16 | 17 | - 国家政策、利率变动,甚至自然灾害,都有可能带偏经济大环境,这就是系统性风险。 18 | 19 | - 而且基金定投一般选的是指数基金,投资跟着指数走,降低了基金经理决策跑偏的风险。 20 | 21 | ## 五、“宝宝”理财:虽然不懂我,但你或许在用 22 | 23 | - 货币基金是好东西,但如果它发展过速、不受约束,就可能会变成金融之癌,损害实体经济。 24 | 25 | - 要求限制原先的T+0提现。如果想在T+0的货币基金平台上提取超出规定限额的钱,提取时间就要变成T+N。 26 | 27 | - 限制T+0提现,既可防止挤兑风险,又能引导大家把鸡蛋放在不同的篮子里。 28 | 29 | ## 六、年金险:给你稳稳的幸福 30 | 31 | - 投资这件事:事后诸葛亮,事前猪一样。 32 | 33 | ## 七、股票:想和我一起感受乘风破浪的刺激吗 34 | 35 | - 散户也别泄气,机会还是有的。你可以掐准点,去网上摇号。如果运气好,就能中签。
这就是传说中的打新股,简称“打新”。 36 | 37 | ## 八、债券:欠债还钱,天经地义 38 | 39 | - 这种可以转变成股票的债券,就叫可转债。 40 | 41 | ## 九、期货:征服这货需要一颗强大的心脏 42 | 43 | - 在金融市场上有这样一种投资方式,它原本是保证买卖正常运行,可最后却华丽转身,成为一种投机工具。 它既能让人一夜暴富,也能让人几代贫穷。 44 | 45 | - 期货不是货,而是一张合约,是按标准填写的远期交易合约。 46 | 47 | - 阿蛛觉得涨得差不多了,于是把期货卖了出去,这叫平仓 48 | 49 | -------------------------------------------------------------------------------- /backup/35_Java空指针避坑指南.md: -------------------------------------------------------------------------------- 1 | # [Java空指针避坑指南](https://github.com/superleeyom/blog/issues/35) 2 | 3 | 把阿里巴巴的《Java开发手册》里,关于 NPE异常总结了下,**预防空指针异常,从你我做起!** 4 | 5 | --- 6 | 7 | > 【强制】所有的 POJO 类属性必须使用包装数据类型。 8 | 9 | 10 | 数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。 11 | 12 | --- 13 | 14 | > 【强制】在使用 java.util.stream.Collectors 类的 toMap()方法转为 Map 集合时,一定要注意当 value 为 null 时会抛 NPE 异常。 15 | 16 | 17 | ```java 18 | List> pairArrayList = new ArrayList<>(2); 19 | pairArrayList.add(new Pair<>("version1", 8.3)); 20 | pairArrayList.add(new Pair<>("version2", null)); 21 | // 抛出 NullPointerException 异常 22 | pairArrayList.stream().collect(Collectors.toMap(Pair::getKey, Pair::getValue)); 23 | 24 | ``` 25 | 26 | 27 | 因为 HashMap 的 merge 方法里,若 value 为 null,则会抛 NPE: 28 | 29 | ```java 30 | @Override 31 | public V merge(K key, V value,BiFunction remappingFunction) { 32 | if (value == null) 33 | // 抛出 NullPointerException 异常 34 | throw new NullPointerException(); 35 | //.... 36 | } 37 | ``` 38 | 39 | 40 | 这个问题`java9`已经修复,所以也可以尝试升级jdk,或者把value为null的过滤掉就行。另外注意:**如果key相同也会抛异常** ,改成如下代码相同的key就不会报异常,新key的value会替换旧key的value: 41 | 42 | ```Java 43 | pairArrayList.stream().collect(Collectors.toMap(Pair::getKey, Pair::getValue,(v1, v2) -> v2)) 44 | ``` 45 | 46 | 47 | --- 48 | 49 | > 【强制】使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一 致、长度为 0 的空数组。 50 | 51 | 52 | ```java 53 | List list = new ArrayList<>(2); 54 | list.add("guan"); 55 | list.add("bao"); 56 | // array 的值为:["guan","bao",null,null] 57 | String[] array = list.toArray(new String[4]); 58 | ``` 59 | 60 | 61 | 如果循环遍历 array 的时候,不注意,就有可能抛 NPE: 62 | 63 | ```java 64 | for (String s : array) { 65 | // 抛出 NullPointerException 异常 66 | System.out.println(s.length()); 67 | } 68 | ``` 69 | 70 | 71 | 所以建议数组空间大小的 length 设置为0,动态创建与 list size 相同的数组,性能最好。 72 | 73 | ```java 74 | String[] array = list.toArray(new String[0]); 75 | ``` 76 | 77 | 78 | --- 79 | 80 | > 【强制】在使用 Collection 接口任何实现类的 addAll()方法时,都要对输入的集合参数进行 NPE 判断。 81 | 82 | 83 | ```java 84 | List list = new ArrayList<>(2); 85 | list.add("guan"); 86 | list.add("bao"); 87 | List listSecond = null; 88 | // 抛出 NullPointerException 异常 89 | list.addAll(listSecond); 90 | ``` 91 | 92 | 93 | 观察 ArrayList 的 addAll 方法的源码: 94 | 95 | ```java 96 | public Boolean addAll(Collection c) { 97 | // 若c为null,这里会抛NPE 98 | Object[] a = c.toArray(); 99 | int numNew = a.length; 100 | ensureCapacityInternal(size + numNew); 101 | // Increments modCount 102 | System.arraycopy(a, 0, elementData, size, numNew); 103 | size += numNew; 104 | return numNew != 0; 105 | } 106 | ``` 107 | 108 | 109 | 所以正确的做法是,对输入参数做判空化处理: 110 | 111 | ```java 112 | // CollUtil.emptyIfNull(List set)方法是 hutool里面的对集合null设置为empty的方法,方法内实际为: 113 | // (null == set) ? Collections.emptyList() : set 114 | list.addAll(CollUtil.emptyIfNull(listSecond)); 115 | ``` 116 | 117 | 118 | --- 119 | 120 | > 【推荐】高度注意 Map 类集合 K/V 能不能存储 null 值的情况。 121 | 122 | 123 | ![](http://image.leeyom.top/blog/20210722121222.png) 124 | 125 | --- 126 | 127 | > 【强制】三目运算符(condition? 表达式 1 : 表达式 2) 中,高度注意表达式 1 和 2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常。 128 | 129 | 130 | ```java 131 | Integer a = 1; 132 | Integer b = 2; 133 | Integer c = null; 134 | Boolean flag = false; 135 | // a*b的结果是int类型,那么c会强制拆箱成int类型,抛出NPE异常 136 | Integer result = (flag ? a * b : c); 137 | ``` 138 | 139 | 140 | `a*b` 包含了算术运算,因此会触发自动拆箱过程(会调用 intValue 方法),此时`a*b`的结果是 int 类型,那么c会强制拆箱成 int 类型,以下两种场景会触发类型对齐的拆箱操作: 141 | 142 | 1. 表达式 1 或表达式 2 的值只要有一个是原始类型。 143 | 144 | 2. 表达式 1 或表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。 145 | 146 | --- 147 | 148 | > 【强制】当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题。 149 | 150 | 151 | ![](http://image.leeyom.top/blog/20210722143358.png) 152 | 153 | 可以使用如下方式来避免 sum 的 NPE 问题:`SELECT IFNULL(SUM(column), 0) FROM table;` 154 | 155 | --- 156 | 157 | > 【推荐】方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分说 明什么情况下会返回 null 值。 158 | 159 | 160 | 防止 NPE 是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败、序列化失败、运行时异常等场景返回 null 的情况。 161 | 162 | --- 163 | 164 | > 【强制】当 switch 括号内的变量类型为 String 并且此变量为外部参数时,必须先进行 null 判断。 165 | 166 | 167 | ```java 168 | public static void method(String param) { 169 | switch(param) { 170 | case "sth": 171 | System.out.println("it's sth"); 172 | break; 173 | case "null": 174 | System.out.println("it's null"); 175 | break; 176 | default: 177 | System.out.println("default"); 178 | } 179 | } 180 | 181 | public static void main(String[] args) { 182 | // 会报NPE异常 183 | method(null); 184 | } 185 | ``` 186 | 187 | 188 | --- 189 | 190 | > 【强制】字符串判断相等,常量一定要写在前面「这个手册上没有,我自己加的,哈哈」 191 | 192 | 193 | ```Java 194 | String str1 = null; 195 | String str2 = "hello world"; 196 | // 错误的写法,会抛NPE 197 | str1.equals(str2); 198 | // 正确写法 199 | str2.equals(str1); 200 | 201 | ``` 202 | 203 | 204 | --- 205 | 206 | 其他的可能产生 NPE 的场景: 207 | 208 | 1. 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE: 209 | 210 | ```java 211 | public int f() { 212 | Integer a = null; 213 | // 抛出NPE异常 214 | return a; 215 | } 216 | ``` 217 | 218 | 219 |     另外这种情况也会自动拆箱产生NPE: 220 | 221 | ```java 222 | Integer num = null; 223 | // 抛出NPE异常 224 | if(num > 1) { 225 | // do someing 226 | } 227 | ``` 228 | 229 | 230 | 2. 数据库的查询结果可能为 null: 231 | 232 | ```java 233 | // 数据库查询用户信息 234 | User user = userMapper.getById(123); 235 | // user可能为null,抛出NPE异常 236 | String userName = user.getUserName(); 237 | ``` 238 | 239 | 240 | 3. 远程调用返回对象时,一律要求进行空指针判断,防止 NPE: 241 | 242 | ```java 243 | ApiResponse apiResponse = userFeign.getById(123); 244 | // user可能为null 245 | User user = apiResponse.getData(); 246 | if(user!=null){ 247 | String userName = user.getUserName(); 248 | } 249 | ``` 250 | 251 | 252 | 4. 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。 253 | 254 | 5. 级联调用 `obj.getA().getB().getC();`一连串调用,易产生 NPE。 255 | 256 | 6. 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null,因为 List 里面也可以存 null。 257 | 258 | 可以考虑使用 JDK8 的 Optional 类来防止 NPE 问题。 259 | 260 | --- 261 | -------------------------------------------------------------------------------- /backup/36_对k8s中Service的理解.md: -------------------------------------------------------------------------------- 1 | # [对k8s中Service的理解](https://github.com/superleeyom/blog/issues/36) 2 | 3 | 以下是我基于实际生产实践,对于k8s里关于Service的一些比较粗浅的理解,如果有些不对的地方,欢迎指出,这些理解基于腾讯云的容器服务TKE,之前一直对这个概念比较模糊,结合腾讯云TKE上的文档,后面请教了同事鲲鹏后,总算是有点清晰了,首先先明确Service的基本概念: 4 | 5 | > 用户在 Kubernetes 中可以部署各种容器,其中一部分是通过 HTTP、HTTPS 协议对外提供七层网络服务,另一部分是通过 TCP、UDP 协议提供四层网络服务。而 Kubernetes 定义的 Service 资源就是用来管理集群中四层网络的服务访问。 6 | 7 | 8 | Kubernetes 的 `ServiceTypes` 允许指定 Service 类型,默认为 `ClusterIP` 类型。`ServiceTypes` 的可取值如下: 9 | 10 | ## **ClusterIP** 11 | 12 | 通过集群的内部 `IP` 暴露服务。当我们的服务只需要在集群内部被访问时,可以使用该类型。打个比方吧(实际生产不推荐这样做),比如你的一个服务调用另外一个服务,需要明确知道另外一个服务的ip,那这个时候,就可以为该被调用方Pod创建一个service,固定一个ip,此时这个ip只能是在这个集群内部访问,创建一个Service的时候,无论是那种的`ServiceTypes`都会生成一个虚拟ip,腾讯云TKE上叫服务ip。 13 | 14 | ![](http://image.leeyom.top/blog/20210723175013.png) 15 | 16 | ## NodePort 17 | 18 | 通过每个集群节点上的 `IP` 和静态端口(NodePort)暴露服务。`NodePort` 服务会路由到 `ClusterIP` 服务,该 `ClusterIP` 服务会自动创建。通过请求 `:`,可从集群的外部访问该 `NodePort` 服务。 19 | 20 | ![](http://image.leeyom.top/blog/20210723180336.png) 21 | 22 | 假设现在有一个集群,集群内有四个节点,这个四个节点对外的节点ip分别是:`10.21.2.10、10.21.2.11、10.21.2.12、10.21.2.13`。假设现在为某个工作负载pod 创建了service,设置主机端口为30003,那这个时候,我们可以通过任意一个节点ip+主机端口号,就可以访问该pod上的服务,比如:10.21.2.10:30003、10.21.2.11:30003 都可以访问。这种的使用场景,比如说某个pod上的服务是属于网关类型的,需要将ip+端口号配置到nginx上进行反向代理,则可以考虑使用这种方式。 23 | 24 | ## **LoadBalancer** 25 | 26 | 也叫负载均衡器,可以向公网或者内网暴露服务。负载均衡器可以路由到 `NodePort` 服务,或直接转发到处于 VPC-CNI 网络条件下的容器中。这种类型的使用场景,比如内网需要和公网打通的情况下,即可通过内网ip直接访问到公网后端的pod。创建完成后的服务在集群外可通过**负载均衡域名或 IP + 服务端口** 访问服务,集群内可通过**服务名 + 服务端口** 访问服务。 27 | 28 | ![](http://image.leeyom.top/blog/20210723182111.png) 29 | 30 | 腾讯云上内网和公网的打通是通过构建子网的方式,对应pod的service创建后,会生成一个ipv4地址,在内网直接ping该ipv4地址,是可以ping通的。LoadBalancer 就感觉有点像是 ClusterIP 和NodePort的超集,用这张图可以理解下: 31 | 32 | ![](http://image.leeyom.top/blog/20210723184516.png) 33 | 34 | 35 | -------------------------------------------------------------------------------- /backup/37_深漂5年随想.md: -------------------------------------------------------------------------------- 1 | # [深漂5年随想](https://github.com/superleeyom/blog/issues/37) 2 | 3 | 有时候跟同事们一起吃饭,大家就会聊起深圳去与留的话题,从大家的言语中看出,大家当初来深圳的大多数是想看看外面的世界怎样,但是在经历了社会的毒打,996的煎熬,房价的绝望后,也感受到了大环境下沉重的压力,大家最后也只会在自嘲的笑声结束这个话题。 4 | 5 | 当我自己真实面对这个选择的时候,自己内心有过纠结与彷惶。说实话,我还是挺喜欢深圳的,喜欢深圳的天气,一年9个月可以穿短袖短裤,喜欢深圳的大海,夏天可以去西涌海滩游泳冲浪,喜欢深圳的交通,去哪里都可以坐公交地铁,喜欢深圳的就业环境,工作机会多。 6 | 7 | 可为啥想要离开深圳呢? 8 | 9 | 我是2016年来到深圳的,今年2021年,刚好第5个年头了,我觉得我想离开深圳的最大的一个原因是自己对房价的绝望吧。目前福田区、宝安区、南山区,稍微像样点的小区,二手房价没有低于6w+的,买一套70平两房,总价420万,首付130万,还不算税,普通人估计掏空家里的6个钱包都不够首付。房价构成了阶级的壁垒,人生的难度都不一样,自然就有人要做出选择,能留在深圳扎根的,是社会的精英阶层,当然离开也并不可耻,它是一种生活方式的选择,在认清现实和理想后,选择一条合适自己的路走也不是不行。 10 | 11 | 当我再次回忆深圳的这五年,发现自己的人生轨迹中也留下了一些痕迹: 12 | 13 | - 16年6月成为社会人后找到了第一份正式工作,爬了深圳最高峰梧桐山、七娘山。 14 | 15 | - 17年3月份开始跑步减肥一直坚持到现在,整个人发生了翻天覆地的变化。 16 | 17 | - 17年5月去现场看了老罗锤子科技2017年的春季新品发布会。 18 | 19 | - 17年7月份有幸去了一趟台湾,从台南到台北,感受了下祖国宝岛台湾的风土人情。 20 | 21 | - 18年12月16号,深圳国际马拉松日,完成了人生的第一个半马。 22 | 23 | - 19年10月带上了牙套,开始矫正之旅,到21年的7月,终于矫正完成。 24 | 25 | - 19年11月,跑了深圳龙华区微型公益马拉松比赛,顺利完赛,非常的激动和开心。 26 | 27 | - 20年花了4个半月,经历了科二和科三的两次挂科后终于拿到了驾照。 28 | 29 | - ........ 30 | 31 | 深圳的孤独是刻在骨子里的,在深圳,很多人耐得住工作的压力,却耐不住夜深人静的孤独。熟识的朋友一个个都离开了深圳,周末放假,独自在几平米的小单间,半夜醒来,手机上还播放着昨晚的音乐,合上手机听着老旧风扇的嗡嗡声却是愈加的孤独和疲惫。 32 | 33 | 突然想起了非常喜欢的一首歌曲,4 Non Blondes 的《What's Up》,里面的歌词恰好就映射了我现在的心境: 34 | 35 | > I try all the time, in this institution 36 | 我一直在尽力着,在这样一个笼牢里 37 | And I pray, oh my god do I pray 38 | 我祈祷,我的上帝!我虔诚的祈祷! 39 | I pray every single day,For a revolution 40 | 我每天都在祈求,为了一个彻彻底底的改变 41 | And so I cry sometimes,When I'm lying in bed 42 | 有时候我会躺在床上大哭 43 | Just to get it all out,What's in my head 44 | 企图把脑袋清空 45 | And I am,I am feeling a little peculiar 46 | 但是我总觉得会怪怪的 47 | And so I wake in the morning,And I step outside 48 | 于是我早上醒来,走出门外 49 | And I take a deep breath and I get real high 50 | 深深呼吸一口气,感觉很振奋 51 | And I scream at the top of my lungs 52 | 我用力大声呼喊 53 | What's going on? 54 | 他妈的,搞什么鬼 55 | 56 | 57 | 深圳就像一座围城; 58 | 59 | 里面的人想出去,外面的人拼命的想进来; 60 | 61 | 或许几年后,我再回来看这篇文章; 62 | 63 | 后悔?庆幸?开心?难过? 64 | 65 | 管他呢! 66 | 67 | ![](http://image.leeyom.top/blog/B58F8625-8F9A-4247-AEA7-357641F302D8.jpeg) 68 | 69 | 70 | --- 71 | 72 | @zhengdaguang 深圳是年轻化的大都市,很喜欢的它的包容性,没有像北京上海那种对外地人的排斥 73 | @x1nchen 哈哈,谢谢 74 | @jyrnan thanks bro 75 | @moxuanyuan 是个办法,但是上班通勤成本很大 76 | @stormbuf 希望我们都能实现财务自由 77 | @primeNumberAndMe 我的家乡省会长沙 78 | 79 | --- 80 | 81 | @cashplk 长沙,武汉,成都都不错的 82 | @hizhangbo 你最后去哪个城市了? 83 | 84 | --- 85 | 86 | @chenyukang 原来是老乡,看到你发的邮件了,感谢! 87 | 88 | --- 89 | 90 | @daguc @teeyog 这两位朋友的头像看着眼熟[手动狗头] 91 | 92 | --- 93 | 94 | @kenshinji 现在在哪个城市呢? -------------------------------------------------------------------------------- /backup/38_基于Github.Issues的博客搭建.md: -------------------------------------------------------------------------------- 1 | # [基于Github Issues的博客搭建](https://github.com/superleeyom/blog/issues/38) 2 | 3 | ## 最新更新 4 | - 前端仓库 [superleeyom.github.io](https://github.com/superleeyom/superleeyom.github.io) 增加了一个 [remove_running](https://github.com/superleeyom/superleeyom.github.io/tree/remove_running) 分支,去掉了 running_page 的链接(我的那个跑步记录的入口,有些人用不到),想要去掉这个链接的朋友用这个分支就行 --2022-02-11 5 | 6 | ## 前言 7 | 8 | 应朋友`@凯佬`的要求,特意写一篇基于 `Github Issues` 博客的搭建教程,整体的过程非常简单,后端参考了 [@yihong0618](https://github.com/yihong0618) 的 [gitblog](https://github.com/yihong0618/gitblog) 项目,发布` issues`,做数据备份,前端参考了[@LoeiFy](https://github.com/LoeiFy) 的 [Mirror](https://github.com/LoeiFy/Mirror) 项目,用于做前端可视化界面,感谢二位大佬的开源精神,所有的服务全部免费(ps:如果你需要自定义域名,自定义域名需要自己付费购买),感谢 Github! 9 | 10 | ## 利用 Github Actions 做数据备份 11 | 12 | 首先需要 fork 我的项目 [blog](https://github.com/superleeyom/blog),也可以 fork [@yihong0618](https://github.com/yihong0618) 的 [gitblog](https://github.com/yihong0618/gitblog) 项目,都差不多,然后修改 `generate_readme.yml`文件,这个文件是触发自动备份的 CI/CD 配置文件,修改如下的地方: 13 | 14 | ```yml 15 | env: 16 | GITHUB_NAME: superleeyom 17 | GITHUB_EMAIL: xxx@qq.com 18 | ``` 19 | 20 | 改成你自己的 Github 用户名和邮箱,接着在你自己的这个 blog 仓库下,创建 `Environment secrets`环境变量 `G_T`: 21 | 22 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210906215624.png) 23 | 24 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210906220916.png) 25 | 26 | 这个`G_T`是 Github 的访问授权 Token,注意保密,不要泄漏,Token 的获取如下图,scope 如果不知道选啥,全部勾上: 27 | 28 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210906215515.png) 29 | 30 | 修改 `main.py `脚本,修改你自己的定制化的 `README.md` 的 header: 31 | 32 | ```python 33 | MD_HEAD = """**

[Leeyom's Blog](https://blog.leeyom.top)

** 34 | **

用于记录一些幼稚的想法和脑残的瞬间~

** 35 | ## 联系方式 36 | - Twitter:[@super_leeyom](https://twitter.com/super_leeyom) 37 | - Telegram:[@super_leeyom](https://t.me/super_leeyom) 38 | - Email:[leeyomwang@163.com](mailto:leeyomwang@163.com) 39 | - Blog:[https://blog.leeyom.top](https://blog.leeyom.top) 40 | """ 41 | ``` 42 | 43 | 在 `issues` 列表,提前建好 `labels` 标签,后面一旦提交了新的 `issues`,或者你修改了 `issues`,`README.md` 会根据你给当前 `issues` 所打的` labels` 标签进行分类,比如我建了如下的几个` labels`,其中`TODO`和`Top`,用于生成 TODO List 和置顶,比如你给这个 `issues` 加上了 `Top` 标签,那么他会出现在 `README.md` 置顶分类里面,这两个建议加上,剩下的按你自己的意愿加: 44 | 45 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210906220109.png) 46 | 47 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210906220253.png) 48 | 49 | **如果你是 fork 我的项目,建议先把 backup 文件夹下里面的 md 文件删除!因为那是我的 blog 备份文件!** 50 | 51 | 有了这个项目,我们就可以通过 `Github Actions`,只要有 `issues` 发布或者修改,都会触发自动构建,备份` issues `生产 md 文件,然后刷新 `README.md` 文件。 52 | 53 | 后期你要发布文章,只需要创建一个 `issues`,然后打好标签,点击发布即可,剩下的都是自动化构建,不需要人为参与。 54 | 55 | ## 利用 Github Pages 做可视化界面 56 | 57 | - 首先你得先创建一个 `github用户名.github.io`的仓库,必须是公共仓库,比如我的:[superleeyom.github.io](https://github.com/superleeyom/superleeyom.github.io),然后你把我这个仓库 [superleeyom.github.io](https://github.com/superleeyom/superleeyom.github.io) 的文件全部拷贝到你刚创建的仓库里面,删除 `Archive`文件夹,这个是我以前备份的 md 文件,对你没啥用! 58 | 59 | - 修改 `docs` 目录和根目录下的两个 `CNAME` 文件,里面的内容是你的自定义的域名,比如我的自定义域名是:`blog.leeyom.top`,如果没有自定义域名,默认填:`github用户名.github.io`。 60 | 61 | - 修改 docs 目录下的 `index.html` 文件,比如我的: 62 | 63 | ```js 64 | window.config = { 65 | organization: false,// 默认是 false,如果你的项目是属于 GitHub 组织 的,请设置为 true 66 | order: 'CREATED_AT',// 文章排序,以 创建时间 或者 更新时间,可选值 'UPDATED_AT','CREATED_AT' 67 | title: "Leeyom's Blog",// 博客标题 68 | user: 'superleeyom',// GitHub 用户名,必须 69 | repository: 'blog',// GitHub 项目名,指定文章内容来源 issues,必须 70 | authors: 'Leeyom',// 博客作者,以 ',' 分割,GitHub 用户名默认包含在内 71 | ignores: '',// 文章忽略的 issues ID 72 | hash: 'ghp_VkKID%Qnlgg$SXfIt!UmH&uCLCtHFU$XJHK^YmxvZy5sZWV5b20udG9w',// hash,必须 73 | perpage: 5,// 分页数量 74 | } 75 | ``` 76 | 77 | 其中关于 hash 的获取,参考:[「获取 hash」](https://github.com/LoeiFy/Mirror/wiki/%E8%8E%B7%E5%8F%96-hash) 78 | 79 | - 去阿里云或者腾讯云,申请一个新的域名,将域名解析到你自己刚创建的 github 仓库 `github用户名.github.io`,不需要自定义域名的可忽略这一步: 80 | 81 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210906222239.png) 82 | 83 | - 在 `github用户名.github.io` 仓库下,将 page 的 source 指向到 docs 目录,指定你的自定义域名即可,若没有自定义域名,`Custom domain` 可以不用设置: 84 | 85 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20210906222539.png) 86 | 87 | - 最后改下你的 `README.md`,里面的内容是我自己瞎写的,换成你自己的,然后等个 5 分钟,访问你的域名,比如我的是:[https://blog.leeyom.top](https://blog.leeyom.top)(没有自定义域名默认的是:`https://github用户名.github.io`),看是否能正常访问,如果不能正常访问,请在当前 issues 下留言,我看到会回复。 88 | 89 | ## 总结 90 | 91 | 虽然 Github Issues 的定位就不是为博客而生的,这也注定了它有诸多不足之处,比如无法限制别人发 issue ,但是对于那些不想折腾,内容才是王道的程序员朋友来说,免费、Markdown、代码高亮、标签、评论、图床、备份、Github大厂背书,Github Issues 也不乏是个与自己和他人沟通的好地方。 92 | 93 | ## 参考资料 94 | 95 | - [这个博客开源了](https://github.com/yihong0618/gitblog/issues/177) 96 | - [Mirror中文教程](https://github.com/LoeiFy/Mirror/wiki/%E4%B8%AD%E6%96%87%E6%95%99%E7%A8%8B) 97 | 98 | --- 99 | 100 | > 提示401 这么解决 101 | 102 | @Thomas-Lv 这个应该是你的hash生成的有点问题,是不是域名没有填对,你的默认域名是:thomas-lv.github.io 103 | 104 | --- 105 | 106 | @Thomas-Lv 确认:1、域名是否填对,2、token权限是否设置对 107 | 具体可以仔细看下:https://github.com/LoeiFy/Mirror/wiki/%E8%8E%B7%E5%8F%96-hash 108 | 109 | --- 110 | 111 | 这个token只要查询权限,开放所有的权限可能比较危险,你这样吧,你先重新生成一个token再试试,如果还是不行,你直接在Telegram上联系我吧?我的Telegram id:@super_leeyom 112 | @Thomas-Lv 113 | 114 | --- 115 | 116 | > 你好,关于你说的不足的地方,我做了增强,可以自动根据 label 来选择是否要发布成文章,目前支持 todo top publish标签。 117 | > 118 | > https://github.com/zackzhangkai/zackzhangkai.github.io/blob/master/.github/workflows/generate_markdown_blog.yml 119 | > 120 | > 需要在yml中增加 121 | > 122 | > ``` 123 | > name: Generate GitBlog README 124 | > 125 | > on: 126 | > workflow_dispatch: 127 | > issues: 128 | > types: [opened, edited, labeled] 129 | > 130 | > sync: 131 | > if: ${{ github.event.label.name == 'todo' }} || ${{ github.event.label.name == 'top' }} || ${{ github.event.label.name == 'publish' }} 132 | > name: Generate README 133 | > ``` 134 | 135 | cool 🆒 136 | 137 | --- 138 | 139 | index.html 里面的`repository: 'blog', ` 这个填你的issues的仓库名称 @Salomens 140 | 141 | --- 142 | 143 | @Salomens 等我我看下你的相关仓库的配置 144 | 145 | --- 146 | 147 | @Salomens 你的issues仓库的github的token相关的环境变量创建了吗?也就是这个 `secrets.G_T` 148 | 149 | --- 150 | 151 | @Salomens cool!有问题随时联系我 152 | 153 | --- 154 | 155 | @Salomens 看这步: 156 | ![image](https://user-images.githubusercontent.com/22115219/149603446-0d6b9d3b-aa7d-4469-9f65-10f0ec84d3f2.png) 157 | 158 | 159 | --- 160 | 161 | @Salomen 建议使用原始的 [Mirror](https://github.com/LoeiFy/Mirror),我那个被我魔改,加了我的跑步的链接,那个得修改源码才行 162 | 163 | --- 164 | 165 | > 请教一下,如果要增加一个元素,应该如何操作呢?工作量大吗? ![image](https://user-images.githubusercontent.com/73227570/190609213-ea3c9915-0387-41e9-bcdf-88c2b4c44b47.png) 166 | 167 | 这个比较简单,你可以加我的电报 @super_leeyom 我可以指导下 168 | 169 | --- 170 | 171 | > 这个mirror网站打不开了,是不是无法获取hash了 ![2023-09-25 17 41 11 mirror am0200 com acbe19c6ee72](https://user-images.githubusercontent.com/61244668/270301030-cb471a4c-5b7c-45ca-8bff-beebd9dfe791.jpg) 172 | 173 | 怎么感觉像是你的网络有问题啊,你检查下网络是否通畅 @zqisme 174 | 175 | --- 176 | 177 | 因为后面更新过一版了,现在只需要配置 github的token就行了,不需要配置这两个了,你可以忽略掉 @briteming -------------------------------------------------------------------------------- /backup/39_我的2021年跑步报告.md: -------------------------------------------------------------------------------- 1 | # [我的2021年跑步报告](https://github.com/superleeyom/blog/issues/39) 2 | 3 | 距离我上一次写博客已经三个月了,今天是2022年的第一天,原本想写篇博客总结回顾下2021年,但是想想还是算了,2021年过的并不是很顺利,也不太想用过多的文字来纪念它,过去了也就过去了,但是却特别想写写自己这一年来的跑步过程,看着各大平台都在出各种年度报告,我也给自己写个2021年的跑步报告,以此来激励自己2022年能继续跑下去。 4 | 5 | 6 | 7 | ## 🏃[跑步数据](https://running.leeyom.top/) 8 | 9 | 2021年的年度跑步数据如下所示: 10 | 11 | | 总距离 | 总跑步次数 | 时间 | 平均配速 | 平均心率 | 半马 | 10km+ | 5km+ | 晨跑 | 夜跑 | 12 | | ------ | ---------- | -------- | -------- | -------- | ---- | ----- | ----- | ----- | ---- | 13 | | 1026km | 172次 | 97h37min | 5‘42 | 150 | 2次 | 24次 | 136次 | 121次 | 38次 | 14 | 15 | ![](http://image.leeyom.top/img/IMG_6410.JPEG) 16 | 17 | 其中每个月的具体数据如下: 18 | 19 | | 月份 | 距离 | 次数 | 平均配速 | 时间 | 20 | | ---- | ------- | ---- | -------- | -------- | 21 | | 1月 | 81.8km | 16次 | 6‘11 | 8h26min | 22 | | 2月 | 20.6km | 4次 | 5‘32 | 1h54min | 23 | | 3月 | 102km | 16次 | 5’09 | 8h45min | 24 | | 4月 | 100.6km | 15次 | 5‘15 | 8h48min | 25 | | 5月 | 101.4km | 16次 | 5’32 | 9h20min | 26 | | 6月 | 69.6km | 14次 | 5‘17 | 6h8min | 27 | | 7月 | 90km | 17次 | 5’46 | 8h41min | 28 | | 8月 | 73.6km | 16次 | 5‘28 | 6h43min | 29 | | 9月 | 82km | 14次 | 5‘56 | 8h6min | 30 | | 10月 | 92.8km | 11次 | 5’49 | 9h | 31 | | 11月 | 104.7km | 19次 | 6‘13 | 10h51min | 32 | | 12月 | 106.5km | 14次 | 6’07 | 10h51min | 33 | 34 | 我给自己每个月的目标是100km的跑量,其中只有5个月达成了目标,其他的月份都差了一点,2月份的跑量最低,是因为2月份我不小心意外受伤,下巴缝了六七针,被迫休息了一个月,6789四个月因为在准备换城市和换工作,所以整体的跑量相对少一点,虽然整体的数据跟预期的差了一点(1026km/1200km),但是也还算不错吧,好歹也有1026km的跑量,跟2020年对比要好上一点。 35 | 36 | 更多跑步数据见我的跑步主页:[https://running.leeyom.top](https://running.leeyom.top) 37 | 38 | ## ⏰跑步时间 39 | 40 | 41 | 42 | 2021年我调整了作息时间,将跑步时间改到了早上,从汇总数据可以看出,晨跑的次数占大头。在深圳的时候,天气比较暖和,通常是早上6点起床,然后洗漱简单收拾一下,6点半出门,晨跑的距离比较短,一般也就5km左右,时间大概控制在30min左右,回到长沙后,由于长沙的天气变化莫测,将起床的时间调到了6点30,其他的保持不变,通常情况下跑一休一,有时候状态好,可能跑二休一,如果天天跑,久而久之就会有厌跑的情绪,要学会适当的休息,给身体恢复的时间。晚上一般是11点30前睡觉,也就是保持7个小时的睡眠时间,加上中午午休的1个小时,基本上能保持一整天精神状态的饱满。周末的话,通常会跑多一点,一般会跑个10km+的距离,总的来说,一周的平均跑步次数在3-4次,这个次数不多也不少。 43 | 44 | > 人类行为只有5%是受自我意识支配的。我们是习惯的造物,因而我们的行为有95%都是自动反应或对于某种需求或紧急情况的应激反应。 45 | 46 | 跑步时间这个问题,我觉得还是得根据个人的情况来,比如你像我一样,晚上没有时间,那就早上跑,如果你早上起不来,那就晚上跑,如果你早晚都没有时间,中午休息时间,也可以动一下嘛,所以这个东西,**不在于你有没有时间,而在于你想不想的问题**,时间它就像海绵里的水,挤挤总是会有的。 47 | 48 | 49 | 50 | ## 👟跑步装备 51 | 52 | 整体如下: 53 | 54 | - 手表:Apple Watch Series 4 55 | - 腰包:Keep 跑步腰包 56 | - 耳机:AirPods 2 57 | - APP:Nike Run Club 58 | - 手机:iPhone 13 59 | - 跑鞋:Nike ZoomX Vaporfly Next% 2 60 | 61 | ![IMG_6488](https://user-images.githubusercontent.com/22115219/148321181-85609738-a0cf-457a-8966-1fb282c5f273.JPEG) 62 | 63 | 64 | > 人们可以被物质奖励或外部激励所驱使;但是,只有在自由选择并享受事物本身的情况下人们才会表现出更多热情,从中获得更多乐趣。 65 | 66 | 2021年买了一双新的跑鞋Nike Next% 2,虽然价格略贵,但是真的非常的喜欢,现在基本上就穿着这双鞋跑步,飞马37现在还在鞋架上,洗干净其实也还是能跑的。另外换了新的手机,从iPhone X 换到iPhone 13,也算是对自己年底的奖励,因为想多记录点生活,所以我通常跑步会带上手机,跑完后会随便拍几张,发到推上记录下自己跑步的碎片时光。Apple Watch 主要用来记录跑步的心率,AirPods 用来跑步的时候听播客和音乐,Nike Run Club 无广告,跟Apple Watch搭配,用来记录跑步的数据,腰包用的是keep家的,之前用过他们家的一代腰包,这个是改进款,感觉调节的松紧带反而没有一代的好用了。 67 | 68 | 69 | 70 | ## 🎧跑步听点啥 71 | 72 | 跑步确实挺无聊的,所以跑步的时候一般喜欢听播客和音乐来打发时间,2021年我主要在听这些播客和音乐: 73 | 74 | ![IMG_6424](https://user-images.githubusercontent.com/22115219/147843977-c9dc9c2d-32c1-4511-8c30-ab53ba94cf45.JPEG) 75 | 76 | 77 | ## 🤔跑步的意图 78 | 79 | 2021年读了一本书叫做《精力管理》,对我启发很大,书中讲到人的精力由四个部分组成:**体能,情感,思维和意志**,其中体能是精力的底层基建,建立起体能消耗和恢复的节奏性平衡,能够确保精力储备保持在相对稳定的水平。我现在给自己的定位是,不再以减肥为目的,而是通过跑步,维持一个相对较好的精神状态,让精力维持在相对稳定的水平。跑步除了在精神层面带来的好处,在身体上也有很大的益处,2021年一整年有氧适能在都是高于平均,另外,回长沙后伙食变好了🐶,再个由于自己工种的缘故(长时间坐着),体重较之前略有提升,但是好在通过跑步,还能在可控范围内。 80 | 81 | ![](http://image.leeyom.top/img/IMG_6432.JPEG) 82 | 83 | 84 | 85 | 作家村上春树在《当我谈跑步时,我在谈些什么》说到:“希望一人独处的念头,始终不变地存于心中。所以一天跑一个小时,来确保只属于自己的沉默的时间,对我的精神健康来说,成了具有重要意义的功课。”,确实啊,当代社会太浮躁了,**我们总觉得自己很忙,我们越是忙碌,越会高看自己,认为自己对他人来说不可或缺。我们无法陪伴亲人朋友,不知疲倦,没日没夜,只管四处救火,不给自己留下喘息的时间,这就是现代社会的“成功典型”**,真的,偶尔可以停下来,**跑步1h,灵魂可以喘口气!** 86 | 87 | ## 🏁最后 88 | 89 | 最后,祝大家2022年元旦快乐!新年快乐! 90 | 91 | -------------------------------------------------------------------------------- /backup/3_白话解说之.BIO、NIO、AIO、异步阻塞的区别.md: -------------------------------------------------------------------------------- 1 | # [白话解说之 BIO、NIO、AIO、异步阻塞的区别](https://github.com/superleeyom/blog/issues/3) 2 | 3 | ## 白话解说 4 | 5 | 首先明白这个几个简称的含义: 6 | 7 | - **BIO(Blocking IO)**:同步阻塞 8 | - **NIO(Non-Blocking IO)**:同步非阻塞 9 | - **AIO(Asynchronous IO)**:异步非阻塞 10 | 11 | BIO、NIO、异步阻塞、AIO 这四者之间有啥区别呢?那就拿生活的实例来解释一下: 12 | 13 | - **BIO**:我去上厕所,这个时候坑位都满了,我必须等待坑位释放了,我才能上吧?!此时我啥都不干,站在厕所里盯着,过了一会有人出来了,我就赶紧蹲上去。 14 | - **NIO**:我去上厕所,这个时候坑位都满了,没关系,哥不急,我出去抽根烟,过会回来看看有没有空位,如果有我就蹲,如果没有我出去接着抽烟或者玩会手机。 15 | - **异步阻塞**:我去上厕所,这个时候坑位都满了,没事我等着,等有了新的空位,让他通知我就行,通知了我,我就蹲上去。 16 | - **AIO**:我去上厕所,这个时候坑位都满了,没事,我一点也不急,我去厕所外面抽根烟再玩玩手机,等有新的坑位释放了,会有人通知我的,通知我了,我就蹲上去。 17 | 18 | 从这个生活实例中能可以看得出来: 19 | 20 | - `同步`就是我需要自己每隔一段时间,以轮训的方式去看看有没有空的坑位; 21 | - `异步`则是有人拉完茅坑会通知你,通知你后,你再回去蹲; 22 | - `阻塞`就是你在等待的过程中,你不去做任何的事情,就干等着; 23 | - `非阻塞`就是你在等待的过程中,可以去做其他的事情,比如抽烟、玩手机等; 24 | 25 | 总结就是:异步的优势显而易见,大大优化用户体验, 非阻塞使得系统资源开销远远小于阻塞模式,因为系统不需要创建新的进程(或线程),大大地节省了系统资源。 26 | 27 | ## 专业解说 28 | 29 | 发现一篇解说的很清楚的文章:[《常见的IO模型有哪些?Java中的BIO、NIO、AIO 有啥区别?》](https://www.cnblogs.com/javaguide/p/io.html) 30 | -------------------------------------------------------------------------------- /backup/40_[译]如何阅读Apple开发文档.md: -------------------------------------------------------------------------------- 1 | # [[译]如何阅读Apple开发文档](https://github.com/superleeyom/blog/issues/40) 2 | 3 | 周末外面一直下雨,闲着也是没事,尝试花了几个小时,把 [Hacking with Swift ](https://www.hackingwithswift.com/)的创始人 Paul Hudson 在2019 年写的一篇关于「[How to read Apple’s developer documentation](https://www.hackingwithswift.com/articles/167/how-to-read-apples-developer-documentation)」的文章翻译成了中文,虽然距离这篇文章发表已经过去两三年了,但是里面的思想和方法却永远不会过时,希望能帮助大家,以下便是译文: 4 | 5 | > 作者:Paul Hudson,日期:2019年1月18日 6 | 7 | 8 | 对于很多人来说,这篇文章看起来可能会比较奇怪,因为我们大多数人已经习惯了Apple的API文档的使用方式,并且也能快速的找到我们自己想要的东西。 9 | 10 | 但是有一个有意思的事实是:去年很多朋友希望我写一些关于如何阅读Apple开发文档的文章,比如有:你是如何去查看iOS的API接口的?如何在这些开发文档中找到你想要的东西?如何深入了解这些文档或者接口的底层原理? 11 | 12 | 你是不是曾经也希望有人帮助你去理解Apple的开发文档呢?其实并不只你一个人,有很多人的都有相同的苦恼。所以我希望这篇文章能对你有所帮助:我会尽力去解释它的整体结构,它有什么好的地方和不好的地方,以及我是如何使用Apple的开发者文档的。 13 | 14 | 更重要的一点是,我将向你展示那些有经验的人是如何去搜寻相关资料,并且这些资料要比Apple的在线文档更有价值。 15 | 16 | ## “它是什么?” vs “你怎么用它?” 17 | 18 | 任何的API文档应该有以下5种特性之一: 19 | 20 | 1. 接口代码通常需要展示:方法名称和参数、属性名称和类型等,并带有一小段文字描述它的功能是什么,它是做什么的。 21 | 2. API应该有用例指导的描述 22 | 3. 示例代码应该多多使用这些API,以使得它们更加的有用 23 | 4. 展示如何使用基础的API代码片段 24 | 5. 需要有一套总结常见的问题的方法:比如:如何做 X,如何做 Y,以及如何做 Z,等等。 25 | 26 | 通俗点讲,Apple第1点做的很不错,第2和第3点也做的很多,但是第4点做的相当少,第5点几乎就没有。 27 | 28 | 所以,如果你正在寻找「如何使用Y去做X」的具体示例,你可以尝试从我的「[swift的基础知识](https://www.hackingwithswift.com/example-code)」教程学起,这也正是这篇教程的用途。 29 | 30 | 理解 Apple 的文档所要解决的问题,将帮助你最大限度地利用它。它不是一个结构化的教程——它也不会向你介绍一系列的概念来帮助你实现一个目标,它只是作为苹果支持的数千个API 的参考指南。 31 | 32 | ## 寻找类 33 | 34 | Apple的在线开发文档在:[https://developer.apple.com/documentation/](https://developer.apple.com/documentation/),虽然你有一个Xcode的本地离线版本,但是和我交流过的绝大多数的人都是使用的在线的版本,因为他们可以更容易的找到他们想要的东西。 35 | 36 | Apple绝大多数的文档都有接口描述,这也是你看的最多的。我想用一个实际的例子,所以请从在你的浏览器中打开[https://developer.apple.com/documentation/ ](https://developer.apple.com/documentation/ )(那是所有苹果开发者文档的主页)。 37 | 38 | 你会看到苹果所有的 API 都被分成了App Frameworks、Graphics 、Games等类别,并且你已经看到了一个重要的东西:所有深蓝色的文本都是可点击的,点击后它将带你进入特定框架的API文档。它使用相同的字体和大小,没有下划线,老实说,深蓝色链接和黑色文本之间没有太大区别,但你仍然需要留意这些链接,其中有很多,你将大量使用它们并进行深入研究。 39 | 40 | 现在请从App Frameworks类别中选择UIKit,你会看到它的简要概述(为iOS创建用户界面),一个标记为「重要」的黄色大框,然后是一个类别列表。这些黄色的框框确实值得注意:尽管它们被频繁使用,但它们几乎总能阻止你犯一些基础的错误,从而在以后引发一些奇怪的问题。 41 | 42 | 这个页面描述了UIKit的类别列表,这是也是大多数人通常会迷失的地方:他们想要学习一些类似于UIImage的东西,以至于他们必须查看整个列表,然后在合适的地方找到它。 43 | 44 | 在这种情况下,你可能会查看「Resource Management」这个类别,因为它的副标题写着 「管理存储在主可执行文件之外的图像、字符串、storyboards 和 nib 文件」,这听起来好像很有希望。 但是,你会感到失望,你需要向下滚动页面到「Images and PDF」 部分才能找到 `UIImage`。 45 | 46 | **这就是为什么我交谈过的大多数人只使用他们最喜欢的搜索引擎**,他们从搜索引擎中输入他们关心的类,只要它有一个像「UI」、「SK」或类似的前缀,通常搜索引擎的第一结果就是他们想要的。 47 | 48 | 不要误解我的意思,我知道这种方法并不理想。但是当你要搜索一个类,你要么去搜索引擎搜索,要么去 [https://developer.apple.com/documentation/](https://developer.apple.com/documentation/) 查询,选择一个框架,选择一个类别,然后再选择一个类,很显然第一种方式会更快。 49 | 50 | **重要的是**:无论你选择哪种方法,结果都是一样的,最终都会出现在同一个地方,所以选择最适合你自己的搜索方式就行。现在,请你找到并选择`UIImage`。 51 | 52 | ## 阅读类的接口 53 | 54 | 一旦你选择了你关心的类,页面就会有四个主要组成部分:概述、版本摘要、接口和关系。 55 | 56 | 概述是我前面提到的「描述一个API应该做什么以及用例指导」,我要求你选择 **UIImage**,因为它是文本描述的比较好的一个例子。 57 | 58 | 当这是我第一次使用的类时,尤其是最近才引入的类,我通常会阅读它的概述。 但是对于其他的类,任何我以前至少使用过一次的类,我会直接跳过它,并尝试找我想要的具体类容。 请记住一点,Apple 文档的设计目的并不是作为一种学习工具:当你有特定的目的时,它的效果才是最好的。 59 | 60 | 如果你并不总是为你所选择的Apple 平台的最新版本进行开发,那么页面右侧的「版本摘要」侧边栏就非常重要了。在这种情况下,你会看到 iOS 2.0+, tvOS 9.0+和watchOS 2.0+,这告诉我们 UIImage 这个类何时在这三个操作系统上第一次使用,并且它仍然可用,如果它被弃用(不再可用),你会看到类似 iOS 2.0-9.0 的东西。 61 | 62 | 这个页面上的真正内容,以及苹果开发框架中作为特定类主页的所有页面上的内容,都列在「主题」标题下。这将列出这个类支持的所有属性和方法,再细分为使用类别:「获取图像数据」,「获取图像大小和比例」等等。 63 | 64 | 如果你选择的类有任何自定义初始化器,它们应该总是首先显示。UIImage有很多自定义初始化器,你会看到它们都被列为签名,只是描述它期望的参数的部分。所以,你会看到这样的: 65 | 66 | ```Swift 67 | init?(named: String) 68 | init(imageLiteralResourceName: String) 69 | ``` 70 | 71 | 72 | **Tip**:如果你看到的是Objective-C代码,确保你的语言是Swift。你可以在页面的右上角执行此操作,当重要的 iOS 测试版引入新的变化时,你也可以在此处启用 API 更改选项。 73 | 74 | 请记住,初始化器被写为 `init?`而不是`init`, 是有可能失败的,因为`init?`返回一个可选的,以便在初始化失败时可以返回nil。 75 | 76 | 在初始化器的正下方,你有时会看到一些比较特殊的用于创建类的实例方法。这些不是Swift意义上的初始化方式,但它们确实创建了类的实例。对于UIImage,你会看到这样的东西: 77 | 78 | ```Swift 79 | class func animatedImageNamed(String, duration: TimeInterval) -> UIImage? 80 | ``` 81 | 82 | 83 | `class func`意味着你可以调用`UIImage.animatedImageNamed()`. 84 | 85 | 在初始化器之后,事情变得不那么有组织性了:你会发现属性、方法和枚举全部混合在一起。虽然你可以通过滚动页面找到你要找的东西,但我可以大胆的说,大多数人只是Cmd+F在页面上找到一些文本! 86 | 87 | 有以下三点需要注意: 88 | 89 | - 嵌套类型(类、结构和枚举)与属性和方法一起列出,这需要一点时间来适应。 例如,`UIImage` 包含嵌套的枚举 `ReizingMode`。 90 | - 任何带有一条线的东西则说明被弃用了。 这意味着 Apple 打算在某个时候将其删除,因此你不应将其用于将来的代码,并且你应该开始重写已被已被弃用的代码。 (实际上,大多数 API 在很长一段时间内都处于“弃用”状态,—年复一年) 91 | - 一些非常复杂的类,比如`UIViewController`,会有额外的文档页面和它们的方法和属性混合在一起。你看他们旁边的页面图标,都会加上一个简单的英文标题,比如「定位内容相对于安全区」。 92 | 93 | 在页面底部,你会找到对应的关系,它告诉你它继承自哪个类(在本例中,它直接来自 NSObject),以及它遵循的所有协议。 当你查看协议关系更复杂的 Swift 类型时,本节会更有帮助。 94 | 95 | ## 阅读属性和方法页面 96 | 97 | 你已经选择了一个框架和类,现在是时候查看一个特定的属性或方法了。 查找并选择此方法: 98 | 99 | ```Swift 100 | class func animatedResizableImageNamed(String, capInsets: UIEdgeInsets, resizingMode: UIImage.ResizingMode, duration: TimeInterval) -> UIImage? 101 | ``` 102 | 103 | 104 | 你应该在创建专用图像对象类别中找到它。 105 | 106 | 这不是一个复杂的方法,但它确实展示了这些页面的重要部分: 107 | 108 | - Apple 有几种不同的编写方法名称的方式。 前有`class func animatedResizableImageNamed` , 然后是方法页面标题中显示的表单`(animatedResizableImageNamed(_:capInsets:resizingMode:duration:))`,以及方法页面的声明部分中的表单。 109 | - 正如你在版本摘要中看到的(在右侧),此方法是在 iOS 6.0 中引入的。虽然主要的 UIImage 类从第一天就已经存在,但这种方法是在几年后引入的。 110 | - 方法声明的各个部分,颜色是紫色的都是可点击的。 不过要小心:如果你点击 `UIImage.ResizingMode`,你会去哪里取决于你点击的是`UIImage`还是`ResizingMode`。 (提示:你通常需要单击右侧的那个) 111 | - 你将看到每个参数的含义和返回值的简要说明。 112 | - 「Discussion」部分详细介绍了此方法的具体使用说明。 这是几乎是每个页面中最有用的部分,因为在这里你会看到诸如「不要调用此方法」或「当……时要小心」之类的内容。 113 | - 你可能会发现「See Also」部分,这里的方法列表与我们在前一页中使用的方法相同。 114 | 115 | `UIImage`是一个旧类,它没有太多改变,所以它的文档状态很好。但是一些较新的api,以及许多不像`UIKit`那样受欢迎的老API,仍然没有得到足够的文档支持。例如,来自`SceneKit`的`SCNAnimation`,或来自`UIKit`的`UITextDragPreviewRenderer`:都是在iOS 11中引入的,并且在发布 18 个月后,它们的文档中仍然包含「没有可用的概述」。 116 | 117 | 当你看到「没有可用的概述」时,你的心会沉下去,但不要放弃:让我告诉你我接下来要做什么…… 118 | 119 | ## 查看代码 120 | 121 | 尽管 Apple 的在线文档非常好,但你经常会遇到「没有可用的概述」,或者你发现没有足够的信息来回答你的问题。 122 | 123 | 康威定律指出,「设计系统的架构受制于产生这些设计的组织的沟通结构」,也就是说,如果你以某种方式工作,你也会以类似的方式设计东西。 124 | 125 | Apple 在我们行业的独特地位使他们以一种相当不寻常的方式工作,这几乎可以肯定这与你自己公司的工作方式完全不同。他们有API审查讨论,试图研究在两种语言下API应该是什么样子,他们有专门的团队来制作文档和示例代码。 126 | 127 | 但是他们获得示例代码的门槛非常高:通常需要非常好的代码才能获得示例代码,并且要经过多层审查,例如法律问题。虽然我可以在一个小时内输出一个项目,然后直接把它作为一篇文章实时发布,但 Apple 做同样的事情要花更长的时间,他们非常重视自己的形象。如果你曾经好奇为什么Swift 官方博客上很少有文章出现,现在你知道了! 128 | 129 | 现在我说这些的原因是Apple 有一个被广泛使用的捷径:他们的工程师在他们的代码中留下注释的门槛似乎很低,这意味着你经常会在Xcode中找到有价值的信息。这些评论就像金粉一样:它们直接来自Apple 的开发者而不是他们的开发者发行团队,尽管我非常喜欢devpub,但很高兴直接从源头那里找到。 130 | 131 | 还记得我之前提到 `SceneKit` 的 `SCNAnimation` 在 Apple 的开发者网站上没有记录吗? 好吧,让我们看看 Xcode 可以显示什么:按` Shift+Cmd+O` 调出 `Open Quickly` 菜单,确保右侧的 Swift 图标是彩色的而不是空心的,然后输入`SCNAnimation`。 132 | 133 | 你将看到列出的一些选项,但你正在寻找在 `SCNAnimation.h` 中定义的选项。 如果你不确定,最好选择 `YourClassName.h` 文件。 134 | 135 | 无论如何,如果你打开SCNAnimation.h, Xcode会显示一个生成的SCNAnimation头文件的版本。因为原始版本是Objective-C,所以Xcode为Swift做了一个实时翻译,这就是 Open Quickly 框中带颜色的 Swift 标志的含义。 136 | 137 | 现在,如果你按下 Command+F 并搜索「class SCAnimation」,你会发现: 138 | 139 | ```Swift 140 | /** 141 | SCNAnimation represents an animation that targets a specific key path. 142 | */ 143 | @available(iOS 11.0, *) 144 | open class SCNAnimation : NSObject, SCNAnimationProtocol, NSCopying, NSSecureCoding { 145 | /*! 146 | Initializers 147 | */ 148 | 149 | /** 150 | Loads and returns an animation loaded from the specified URL. 151 | 152 | @param animationUrl The url to load. 153 | */ 154 | public /*not inherited*/ init(contentsOf animationUrl: URL) 155 | ``` 156 | 157 | 158 | 而这仅仅是开始。 是的,该类及其所有内部结构都有文档,包括使用说明、默认值等。 所有这些确实应该在在线文档中,但无论出于何种原因,它仍然没有出现,所以准备好查找代码可以作为一个有用的补充。 159 | 160 | ## 最后的提示 161 | 162 | 此时,你应该能够查找你喜欢的任何代码的在线文档,并查找头文件注释以获取额外的使用说明。 163 | 164 | 但是,在你准备好面对所有 Apple 开发文档之前,你还需要了解两件事。 165 | 166 | 首先,你会经常遇到标记为「已归档」、「旧版」或「已停用」的文档,即使是相对较新的文档。 当它真的很旧时,你会看到这样的消息:「这个文档可能不代表当前开发的最佳实践,下载和其他资源的链接可能不再有效」。 167 | 168 | 尽管Apple 是世界上最大的公司之一,但它的工程和开发团队还没有达到人满为患的地步,他们不可能在更新所有内容的同时还涵盖新的 API。所以,你看到「归档」文档或类似文件时,请进行判断:如果它是Swift 的某个版本,至少你知道它是最近的,即使不是,你可能仍然会发现有很多有价值的信息。 169 | 170 | 其次,Apple 还有一些特别有价值且非常出色的文档。 这些都列在 [https://developer.apple.com](https://developer.apple.com) 的页脚中,但主要的是人机交互界面指南。 这份文档这将带你了解苹果平台应用设计的各个部分,包括用图片来说明关键点,并提供大量具体建议。 尽管此文档是构建 iOS 应用程序时要考虑的非常重要的一个文档,但令人惊讶的是,似乎很少有开发人员阅读过它! 171 | 172 | ## 接下来? 173 | 174 | 我之前写过关于 Apple 文档问题的文章,虽然那里没有鼓励,但至少它可以帮助你在挣扎时感觉不那么孤单。 175 | 176 | 幸运的是,我有很多可能更有用的材料: 177 | 178 | - [我的 Swift 知识库](https://www.hackingwithswift.com/example-code) 包含 600 多个适用于 Swift 和 iOS 开发人员的答案、提示和技术,它可以帮助你更快地解决问题。 179 | - [我的 Swift 术语表](https://www.hackingwithswift.com/glossary) 定义了 Swift 开发中的 100 多个常用术语,全部在一页上。 180 | - [我有一整本书使用项目来教授 Swift 和 iOS](https://www.hackingwithswift.com/read),它专门用于以逻辑流程介绍概念。 181 | 182 | 你认为阅读 Apple 文档最有效的方法是什么? 在 Twitter 上和我交流你的想法:[@twostraws](https://twitter.com/twostraws)。 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /backup/42_技术周刊分享.md: -------------------------------------------------------------------------------- 1 | # [技术周刊分享](https://github.com/superleeyom/blog/issues/42) 2 | 3 | - 分享一些不错的免费开源的技术周刊(更多见:[my-feed-OPML](https://github.com/superleeyom/my-feed-OPML) 项目,每天定时同步我 Feedly 上的订阅源): 4 | - [TNT-Weekly](https://github.com/tnfe/TNT-Weekly):前端 5 | - [13 的 Apple 開發者週報](https://ethanhuang13.substack.com):iOS 开发 6 | - [Java Weekly](https://www.baeldung.com/category/weekly-review/):Java 周报 7 | - [编程狂人](https://www.tuicool.com/mags):综合类,一般在周四下午发布 8 | - [奇舞周刊](https://weekly.75.team/):前端,每周五发布 9 | - [科技爱好者周刊](https://github.com/ruanyf/weekly):科技人文,每周五发布 10 | - [前端精读周刊](https://github.com/ascoders/weekly):前端 11 | - [Go语言爱好者周刊](https://github.com/polaris1119/golangweekly):Go,每周日发布 12 | - [MDH 前端周刊](https://github.com/sorrycc/weekly):前端,每周一发布 13 | - [独立开发变现周刊](https://github.com/ljinkai/weekly):独立开发,每周五发布 14 | - [WecTeam 前端周刊](https://github.com/wecteam/weekly):前端,每周五发布 15 | - [生信爱好者周刊](https://github.com/ShixiangWang/weekly):科技人文,[官网](https://shixiangwang.github.io/weekly/),每周日发布 16 | - [真科技周刊](https://github.com/staringos/tefact-weekly):科技人文,[官网](https://gossamer-kicker-c5a.notion.site/c023be1cbcac4f9f9e96be2ff8927608) 17 | - [美团技术团队](https://tech.meituan.com/):综合 18 | - [老司机 iOS 周报](https://github.com/SwiftOldDriver/iOS-Weekly):iOS 19 | - [iOS摸鱼周报](https://github.com/zhangferry/iOSWeeklyLearning):iOS,[博客](https://www.zhangferry.com/),[掘金](https://juejin.cn/user/2242659450368119/posts) 20 | - [Go 夜读](https://github.com/talkgo/night):Go,[社区](https://talkgo.org/) 21 | - [前端食堂技术周刊](https://github.com/Geekhyt/weekly):前端 22 | - [混沌周刊](https://weekly.love/):科技人文 23 | - [HelloGitHub](https://github.com/521xueweihan/HelloGitHub):开源项目,每月 28 号以月刊的形式分享 24 | - [体验碎周报](https://www.ftium4.com/categories/%E4%BD%93%E9%AA%8C%E7%A2%8E%E5%91%A8%E6%8A%A5/):交互设计、产品 25 | - [codedump的网络日志](https://www.codedump.info/categories/%E5%91%A8%E5%88%8A/):技术、科技人文、生活 26 | - [GeekPlux Lab](https://geekplux.zhubai.love/):技术、金融、设计、区块链、跑步徒步 -------------------------------------------------------------------------------- /backup/44_[笔记]精力管理.md: -------------------------------------------------------------------------------- 1 | # [[笔记]精力管理](https://github.com/superleeyom/blog/issues/44) 2 | 3 | - 4 | > ![精力管理](https://wfqqreader-1252317822.image.myqcloud.com/cover/654/33091654/s_33091654.jpg) 5 | 作者:吉姆·洛尔 托尼·施瓦茨  6 | 时间:2022-01-13 17:33:43 7 | 数量:14 个笔记 8 | 9 | - **第三章 高效表现有节奏——劳逸结合的平衡:** 10 | - 恢复周期对创造力和亲密关系都同等重要。声音通过音符间的停顿空白组成了音乐,如同字母和空白组成了单词。就在工作的空白时段,爱、友谊、深度和维度得以延伸。如果没有恢复的时间,我们的生活将在失衡中变得一片混乱。 11 | - 我们越是忙碌,越会高看自己,认为自己对他人来说不可或缺。我们无法陪伴亲人朋友,不知疲倦,没日没夜,只管四处救火,不给自己留下喘息的时间。这就是现代社会的成功典型。 12 | - 我们必须在四个层面都保持健康波动的节奏,即「效能金字塔」的组成部分:体能,情感,思维和意志。 13 | - **第四章 体能精力——为身体添柴加火:** 14 | - 从生理学的角度看,精力来源于氧气和血糖的化学反应。从实际生活来看,精力储备取决于我们的呼吸模式、进食的内容和时间、睡眠的长短和质量、白天间歇恢复的程度以及身体的健康程度。建立起体能消耗和恢复的节奏性平衡,能够确保精力储备保持在相对稳定的水平。 15 | - 肌肉若缺水 3%就会失去 10%的力量和 8%的速度。喝水不足也会损害大脑的注意力和协调能力。 16 | - 早餐是一天中最重要的一餐,我们之前说过,高质量的早餐不仅能够提高血糖水平,还可以强力推动新陈代谢。 17 | - 如果你摄入的 80%的食物都是健康而高效的,剩下的 20%可以是你喜欢的任何食物,只要份量控制得当。 18 | - **第五章 情感精力——把威胁转化为挑战:** 19 | - 首先真诚地对该员工的良好表现给出正面评价,然后以讨论而非宣讲的形式提出批评意见——因为自己的看法或许并非完全准确,最后以鼓励结尾。 20 | - **第七章 意志精力——活出人生的意义:** 21 | - 生命的终极意义是担起责任,找寻难题的答案,并且完成生命为每个人设定的任务。 22 | - **第八章 明确目标——知道什么最重要才能全情投入:** 23 | - 人们可以被物质奖励或外部激励所驱使;但是,只有在自由选择并享受事物本身的情况下人们才会表现出更多热情,从中获得更多乐趣。 24 | - **第九章 正视现实——你的精力管理做得如何:** 25 | - 「所有形式的上瘾都是有害的,」精神分析专家卡尔·荣格写道,「不论这种致幻剂是酒精、吗啡还是理想主义。」 26 | - 我们忽视或否认的一切,终将在我们的行为中体现出来。如果我们在成长过程中认为表达愤怒是不可取的,会损害个人形象,它就会以伪装后的其他形式出现,比如批判或挑剔,固执或心怀怨恨。 27 | - 邪恶的本质缺陷并非是罪恶本身,而是自我否认。 28 | - **第十章 付诸行动——积极仪式习惯的力量:** 29 | - 人类行为只有 5%是受自我意识支配的。我们是习惯的造物,因而我们的行为有 95%都是自动反应或对于某种需求或紧急情况的应激反应。 -------------------------------------------------------------------------------- /backup/48_我为什么不能放弃跑步.md: -------------------------------------------------------------------------------- 1 | # [我为什么不能放弃跑步](https://github.com/superleeyom/blog/issues/48) 2 | 3 | 4 | > "明明这么痛苦,这么难过,为什么就是不能放弃跑步?因为全身细胞都在蠢蠢欲动,想要感受强风迎面吹拂的滋味。" 5 | 6 | 我非常喜欢[《强风吹拂》](https://book.douban.com/subject/26210487/)里面的这句话,为此我将它写在了我的 [running page](https://running.leeyom.top/) 上,以此来激励自己一直坚持跑下去,当我问我自己,你为什么不能放弃跑步?我思索了一会儿,我将自己这几年跑步心路历程归纳为这几个关键字:**痛苦、恐惧、和解**,下面我来说说这几个关键字的含义。 7 | 8 | ## 痛苦 9 | 10 | 没错我以前是一个「胖子」,我在 2017 年的时候,体重一度达到了巅峰 93 kg,几乎任何一个人看见我,他们的第一句话都是「你怎么这么胖了?」,但是那个时候我的心态倒是蛮好的,整天乐呵呵的,该吃吃该喝喝的生活还是照样继续,并没有把朋友们的话放在心上。但是,你之前有多放肆,后面就有多痛苦,之后大体重带来的副作用也渐渐开始出现了: 11 | 12 | - 体能严重下降,爬楼梯喘不过气 13 | - 身材严重走形,真胖成个球 14 | - 衣服不好买,几乎只能穿 XXXL 型号的衣服 15 | - 心理上对自己越来越不自信 16 | - .... 17 | 18 | 当我脱光衣服,面对着镜子里的自己,那简直是不忍直视,仿佛镜子里的自己对我说:「兄弟,我不想再胖下去了」,确实啊,不能再这样下去了!咔嚓...随着相机快门声的响起,我拍下了这张充满油腻的照片(PS: 这张照片,到现在我一直留在我的相册里,以此来警示现在的我),然后正式开始了减肥之旅,也正式开启了我的跑步之路。 19 | 20 | 刚开始跑步的时候是痛苦的,首先是身体上的,由于身体的负重,跑个 3 公里,6~7 分配速,面红耳赤,喘不上气,迈不开步子,非常的痛苦,其次是心理上的,因为跑步是孤独的,所以你没有任何的鼓励,也没有任何的支持,你只能自己去控制自己的情绪,所以你的心理状态也是很绝望的。 21 | 22 | 前面 1-3 个月非常痛苦,大部分的人都熬不过这段时间,因为我们会在这段时间给自己找各种借口理由,所以通常熬过初始阶段的 3 个月,基本上我们的身体会开始慢慢的适应这种运动的节奏,运动习惯一但养成,这将会是一个非常好的开端。 23 | 24 | 中间也受过几次比较严重的伤,其中比较严重的一次,膝盖痛的走路都成问题,为此被迫休息一两个月,虽然后面陆陆续续的有些小的伤病,但是问题都不大,休息个一两天一般都自己恢复了。 25 | 26 | ## 恐惧 27 | 28 | 随着持续的锻炼,慢慢的也带来了正向的反馈,体重开始下降,精神状态发生了翻天覆地的变化,开始对跑步上瘾,开始享受多巴胺带来的快乐。但是另外一方面,我居然出现了一个「恐惧」心理,我特别害怕自己又胖回去了,以至于我疯狂的加大运动量,有段时间几乎每天都跑,压根不给身体恢复的时间。 29 | 30 | 其实之所以会产生害怕的心理,归根结底还是对自己身材的一种焦虑,这种焦虑是害怕反弹。但是后面想明白了,大多数时候我们没必要对这种心理有太大负担,科学家提出了成功的减肥定义:「人刻意的减重 10% 的体重以上,并且至少保持一年以上」,如果能达到这个标准,加上合理的运动和饮食,也能继续保持现有的身材不反弹。 31 | 32 | ## 和解 33 | 34 | 其实我发现大部分喜欢跑步的人,或者说喜欢运动的人,他们身上大多有一个特点,就是不怎么「卷」,当我们处在一个焦虑倍增的大都市,大家都被迫面临各种压力,而跑步对我最大的影响是慢慢的拥有了一种可以对抗焦虑的力量,当我觉得自己非常的焦虑的时候,我会选择去跑个 20 公里的 LSD(Long Slow Distance,直译为长距离慢跑),20 公里都能跑完,还有什么事情解决不了,每个人都需要有自己的方式来对抗现实的压力。 35 | 36 | 最近两年,我的心态发生了些许的变化,我开始对自己和解了,每天早上 5 公里的晨跑不再是机械式的任务,而是成为了生活的一部分,有时候没状态、想睡个懒觉,那就不跑了,现在的跑步,不再以减肥为目的,而是为了让自己保持一个比较好的精神状态。在 80% 的时间严格限制自己的情况下,剩下的 20% 的时间,我们可以做自己任何想做的事情,并且不需要有任何的心里负担。 37 | 38 | ## 最后 39 | 40 | 跑步其实是一件非常有意思的事情,由其是当你在城市里面跑步的时候,你会看到各种形形色色的人,有行色匆匆从地铁口进出的上班族,有背着书包穿着校服嬉戏打闹的学生,有坐在大排档喝酒撸串吹牛皮的中年大叔,有略显疲惫坐在公交车上刷手机的年轻人,每个角落都在上演着各种悲欢离合,自己也莫名的会被带入别人的生活里,不由自主的去揣测他们背后的人生故事。 41 | -------------------------------------------------------------------------------- /backup/49_[笔记]盐糖脂:食品巨头是如何操纵我们的.md: -------------------------------------------------------------------------------- 1 | # [[笔记]盐糖脂:食品巨头是如何操纵我们的](https://github.com/superleeyom/blog/issues/49) 2 | 3 | > ![盐糖脂:食品巨头是如何操纵我们的](https://wfqqreader-1252317822.image.myqcloud.com/cover/738/847738/s_847738.jpg) 4 | 作者:迈克尔·莫斯 5 | 时间:2022-04-02 09:45:26 6 | 数量:24个笔记 7 | 8 | - **第四章 到底是谷物还是糖?:** 9 | - 他将肥胖病称为“文明的疾病”。通过研究,他发现人们进食的欲望由血液中的葡萄糖含量和大脑下丘脑所控制,而这两者都深受糖的影响。 10 | 11 | - **第五章 我想经常看到运尸袋:** 12 | - “从解剖学上说,我们时常提起气味和口味,”他说道,“但是每个人基本都忽略了味道的第3个方面,那就是体感,或者说是触摸的感觉。这种感觉包括二氧化碳气泡的刺激,或者咬一口辣椒的刺激,抑或是奶油的柔滑感等等。而说到可口可乐,最好玩的事情就是每当你喝可口可乐时,其味道可以满足这3方面的感受。你可以感受到香草和柑橘的芳香以及所有的棕色香料香味,比如肉桂以及肉豆蔻等等。接着你还会感受到甜味,最后还会感受到磷酸和二氧化碳的刺激。在喝可口可乐时,你真的可以从多方面刺激你的味觉神经,给你带来完全不同的味觉享受。 13 | - 世界上80%的可乐被20%的“重度使用者”消费。 14 | - 食品销售的重要性绝对不亚于食品本身。 15 | 16 | - **第八章 液体黄金:** 17 | - 乳制品企业不是普通的企业。它们不受自由市场经济的制约。自20世纪30年代起,联邦政府就认定牛奶对国民健康至关重要,因此,政府会努力确保乳制品企业的繁荣。为维持乳制品价格稳定,联邦政府会给予其必要的财政补贴,并使用纳税人的钱购买所有剩余乳制品。结果,乳制品企业不用担心与正规商业公司竞争销量。它们不需要担心乳制品因产量过剩而滞销,也不需要思考如何针对重度消费者采取相应的措施,它们甚至不需要应对其他食品生产商为促进消费所实施的市场营销战略。它们生产多少奶制品,政府就会购买多少奶制品。 18 | 19 | - **第九章 午餐时间你说了算:** 20 | - 最难的事情是发明能够畅销的产品,如果你的产品畅销,你就能够弄清楚如何获得最佳成本 21 | - 人们对食品的需求主要围绕着吃饭时间或地方是否方便快捷,关注的是食物的味道、价值或营养,可能还包括一些很微妙的细节,比如,人们吃东西的方式、时间、原因、地点以及吃了什么。这是第一点。我们并不创造需求,而是一直去挖掘、寻找需求,直到我们找到客户所需。” 22 | - 万宝路之所以取得成功,并不是因为它是最聪明的香烟制造商,而是因为它能够以最快的速度和最积极的态度去发现消费者不断变化的弱点 23 | - 妈妈与孩子们对于生冷比萨截然相反的看法,与他们饮食模式的不同有很大关系。成年人用嘴吃东西,吃的是味道。相比之下,孩子们更倾向于用眼睛,至少第一眼是先根据外观来判断食物。 24 | 25 | - **第十章 政府传递的信息:** 26 | - 人体内大量的脂肪并不是来源于脂肪本身。因为无论是薯片还是甜点,其大部分的脂肪实际上都是来源于另外两种主要的加工食品原料。实际上,饱和脂肪的最主要来源其实是奶酪及红肉,这也是医生们最为忧心的脂肪来源。 27 | 28 | - **第十一章 无糖、无脂肪、无买卖:** 29 | - 拉夫了解到,营养学的新研究已经发现,人体的重量控制系统更加善于消耗固体食品产生的热量,而不是流质食品产生的热量。 30 | 31 | - **第十二章 人们热爱盐:** 32 | - 食用的钠过多,超出了人体所需的10倍甚至20倍。这个量远远超过了身体的承受能力。大量的钠将人体组织中的液体转移到血液中,增加了血容量,促使心脏更为强劲地跳动,之后就会引起高血压。 33 | - 当我们食用加工食品时,血液会被大量的盐、糖和脂肪重重包围。但是饮食与吸毒的联系中最有趣的部分还是发生在大脑中。毒品和食品,尤其是盐、糖和脂肪含量较多的食品,在大脑中的反应非常相似。一旦摄入这两种物质,它们就会沿着同样的通道,利用神经线路直达大脑的快感区。当我们因为做了正确的事情而感觉良好时,该区域就会给我们提供反馈,或者视情况而定。有时候,大脑也会误导我们,让我们对事情做出错误的判断。 34 | - 如果食用过多的盐就可能与其他因素共同作用致人发病。在这方面,盐和“性爱、自主活动及加工食品中脂肪、碳水化合物和巧克力让人上瘾的特性”有着相似之处。 35 | - 饮食不是为了寻求快乐,而是为了避免痛苦。 36 | 37 | - **第十三章 消费者渴求同样绝妙的咸味:** 38 | - 摄取过量的盐将导致高血压,而高血压是引发心脏病的危险因素。所以,减少钠的摄入可以降低患高血压和心脏病的风险。 39 | 40 | - **第十四章 我对公众深感抱歉:** 41 | - “只有令人感觉良好的食品,人们才会愿意多买。虽然厂商为产品做广告,但它带来的差别是微不足道的。广告90%的目的是使消费者感觉良好,感觉良好的意思就是好吃”。 42 | - 人们对某种食物的偏爱会极大地受到他们所摄入的其他食物的影响。比如,你对糖果味道的偏爱,会因为喝可乐而改变。这意味着,甜味的极乐点是不固定的,它可能上升或下降。这取决于你吃的其他食物。 43 | - 其中一个关键特性是像巧克力一样入口即化的神奇的能力。“这就是所谓的消失的热量,”韦斯利说,“如果食物能快速融化,那么大脑就认为它没有热量,就像爆米花,你可以不停地吃下去。 44 | - 最近的研究表明,血糖升高会使人们的食欲大增。只要吃下能够引起血糖升高的食物,4个小时后,人就会食欲大增。吃完薯片一小时后,会想吃更多的薯片。 45 | 46 | - **后记 我们为廉价食品而着迷:** 47 | - 在参观结束时,我马上意识到在我们有生之年,雀巢根本不可能将世界人民从肥胖病或加工食品所导致的其他疾病中拯救出来。人们在超市中购买的食品全都经过雀巢科学家们的完美设计,目的在于让人们过度消费产品。尽管这些科学家们掌握了先进的技术和食品科学的所有知识,但他们仍旧不可能拿出一个可行的解决方案。 48 | - 如果不打一场硬仗,这些公司是绝不会对这3种成分轻言放弃的。盐、糖和脂肪都是加工食品的基础,这些企业在确定产品配方时考虑的最重要的一个问题就是:多少含量的盐、糖和脂肪,才能让食品的诱惑力最大化。 49 | - 我们迷上了价格低廉的食品,这与我们热衷于使用廉价能源是一个道理,”前皮尔斯伯里总裁詹姆斯·贝克说道,“真正的问题在于价格的敏感性,而且十分不幸的是,富人与穷人之间的收入差距也在日益扩大。食用新鲜、健康的食物开销太大。因此,肥胖病的问题中隐藏了巨大的经济问题。它在那些资源少和知识匮乏的人身上表现得最为明显。 50 | - 他们试图改变自己的饮食习惯,结果又被商标上标着“健康”标签的产品蒙蔽,从而继续购买垃圾食品。这是食品公司一直采用的战术,极大地标榜食品中所含的某种健康成分,期待消费者会因此忽略其他成分。这是书中最古老的技巧,甚至可以追溯到20世纪20年代和30年代。 51 | 52 | -------------------------------------------------------------------------------- /backup/4_nginx基础指令及初始配置解析.md: -------------------------------------------------------------------------------- 1 | # [nginx基础指令及初始配置解析](https://github.com/superleeyom/blog/issues/4) 2 | 3 | ## nginx 常用命令 4 | 5 | - `./nginx -s stop` :强制停止nginx 6 | 7 | - `./nginx -s quit`: 优雅停止nginx,即处理完所有请求后再停止服务 8 | 9 | - `./nginx -t` :检测配置文件是否有语法错误 10 | 11 | - `./nginx -v`: 查看nginx的版本号 12 | 13 | - `./nginx -V` :查看版本号和配置选项信息 14 | 15 | - `./nginx -c`:设置配置文件(默认是:`/etc/nginx/nginx.conf`) 16 | 17 | - `./nginx -s reload`: 重新加载配置文件 18 | 19 | ## nginx docker 相关指令 20 | 21 | - 拉取 nginx 镜像:`docker pull nginx` 22 | - 启动 nginx 容器实例:`docker run -itd --name nginx-demo -p 8080:80 nginx` 23 | - `-itd`:`-t` 选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, `-i` 则让容器的标准输入保持打开,`-d`是后台运行 24 | - `--name nginx-demo`:指定容器实例名称`nginx-demo` 25 | - `-p 8080:80`:将本机 8080 端口映射为容器的 80 端口 26 | - 进入容器:`docker exec -it nginx-demo bash` 27 | - 终止容器:`docker container stop nginx-demo` 28 | - 删除容器:`docker container rm nginx-demo` 29 | - 启动已终止的容器:`docker container start nginx-demo` 30 | - 查询当前运行的容器:`docker container ls` 31 | - 查询所有的容器:`docker container ls -a` 32 | - 更多的docker指令见[《Docker — 从入门到实践》](https://vuepress.mirror.docker-practice.com) 33 | 34 | ## nginx 默认配置文件解析 35 | 36 | ```nginx 37 | # 设置worker进程的用户,指的linux中的用户,会涉及到nginx操作目录或文件的一些权限 38 | user nginx; 39 | # worker进程工作数设置,一般来说CPU有几个,就设置几个 40 | worker_processes 1; 41 | 42 | # 设置日志级别,debug | info | notice | warn | error | crit | alert | emerg,错误级别从左到右越来越大 43 | error_log /var/log/nginx/error.log warn; 44 | # 设置nginx进程 pid 45 | pid /var/run/nginx.pid; 46 | 47 | # 设置工作模式 48 | events { 49 | # 每个worker允许连接的客户最大连接数 50 | worker_connections 1024; 51 | } 52 | 53 | # http 是指令块,针对http网络传输的一些指令配置 54 | http { 55 | # include 引入外部配置,提高可读性,避免单个配置文件过大 56 | include /etc/nginx/mime.types; 57 | # 设置HTTP默认的 content-type 58 | default_type application/octet-stream; 59 | # 设置日志格式,各项含义如下: 60 | # $remote_addr:客户端ip 61 | # $remote_user:远程客户端用户名,一般为:’-’ 62 | # $time_local:时间和时区 63 | # $request:请求的url以及method 64 | # $status:响应状态码 65 | # $body_bytes_send:响应客户端内容字节数 66 | # $http_referer:记录用户从哪个链接跳转过来的 67 | # $http_user_agent:用户所使用的代理,一般来时都是浏览器 68 | # $http_x_forwarded_for:通过代理服务器来记录客户端的ip 69 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 70 | '$status $body_bytes_sent "$http_referer" ' 71 | '"$http_user_agent" "$http_x_forwarded_for"'; 72 | 73 | access_log /var/log/nginx/access.log main; 74 | 75 | # sendfile 使用高效的文件传输,提升传输性能,启用后才能使用tcp_nopush,指当数据表累积到一定的大小后才发送,提高效率 76 | sendfile on; 77 | #tcp_nopush on; 78 | 79 | # 设置客户端与服务端请求的超时时间,保证客户端多次请求的时候不会重复建立新的连接,节约资源损耗 80 | keepalive_timeout 65; 81 | 82 | # 开启gzip压缩功能,提高传输效率,节约带宽 83 | #gzip on; 84 | 85 | # include 引入外部配置,提高可读性,避免单个配置文件过大 86 | include /etc/nginx/conf.d/*.conf; 87 | } 88 | ``` 89 | 90 | 91 | 92 | ## root 与 alias 93 | 94 | 假如服务器路径为:`/home/leeyom/files/img/header.png` 95 | 96 | - root 路径完全匹配访问: 97 | 98 | ```nginx 99 | location /leeyom { 100 | root /home 101 | } 102 | ``` 103 | 104 | 用户访问的请求为:`url:port/leeyom/files/img/header.png` 105 | 106 | - alias 可以为你的路径做一个别名,对用户透明: 107 | 108 | ```nginx 109 | location /hello { 110 | alias /home/leeyom 111 | } 112 | ``` 113 | 114 | 用户访问的请求为:`url:port/hello/files/img/header.png`,相当于给 `leeyom` 目录做一个别名。 115 | 116 | ## location 的匹配规则 117 | 118 | - `空格`:默认匹配,普通匹配 119 | 120 | ```nginx 121 | location / { 122 | root /home 123 | } 124 | ``` 125 | 126 | 用户可以访问 `home` 目录下的所有文件。 127 | 128 | - `=`:精确匹配 129 | 130 | ```nginx 131 | location = /leeyom/files/img/header.png { 132 | root /home; 133 | } 134 | ``` 135 | 136 | 用户只能访问此路径`/home/leeyom/files/img/header.png`下的`header.png`图片。 137 | 138 | - `~*`:匹配正则表达式,不区分大小写 139 | 140 | ```nginx 141 | location ~* \.(GIF|jpg|png|jpeg|gif) { 142 | root /home; 143 | } 144 | ``` 145 | 146 | 用户可以访问 `home` 目录下的只要后缀为`GIF|jpg|png|jpeg|gif`的文件,由于不区分大小写,如果访问的是 `header.GIF`图片,会重定向访问`header.gif`图片。 147 | 148 | - `~`:匹配正则表达式,区分大小写 149 | 150 | ```nginx 151 | location ~ \.(GIF|jpg|png|jpeg|gif) { 152 | root /home; 153 | } 154 | ``` 155 | 156 | 用户可以访问 `home` 目录下的只要后缀为`GIF|jpg|png|jpeg|gif`的文件。 157 | 158 | - `^~`:以某个字符路径开头请求 159 | 160 | ```nginx 161 | location = ^~ /leeyom/files/img { 162 | root /home; 163 | } 164 | ``` 165 | 166 | 用户只能访问此路径`/home/leeyom/files/img/`下的文件。 167 | 168 | ## nginx 跨域配置 169 | 170 | 在 `server` 块里面增加: 171 | 172 | ```nginx 173 | # 允许跨域请求的域,*代表允许所有的域 174 | add_header 'Access-Control-Allow-Origin' *; 175 | # 允许带上cookie请求 176 | add_header 'Access-Control-Allow-Credentials' 'true'; 177 | # 允许请求的header,比如:Authorization,Content-Type,Accept,Origin,User-Agent 等 178 | add_header 'Access-Control-Allow-Headers' *; 179 | # 允许请求的方法,比如:GET、POST、PUT、DELETE 180 | add_header 'Access-Control-Allow-Methods' *; 181 | ``` 182 | 183 | ## nginx 防盗链 184 | 185 | ```nginx 186 | # 对源站点进行验证(白名单),多个域名用空格隔开 187 | valid_referers *.leeyom.com; 188 | # 非法访问则返回403 189 | if($invalid_referer){ 190 | return 403; 191 | } 192 | ``` 193 | 194 | ## nginx 搭建 Tomcat 集群简版配置 195 | 196 | ```nginx 197 | upstream tomcats { 198 | server 192.168.1.174:8080; 199 | server 192.168.1.175:8080; 200 | server 192.168.1.176:8080; 201 | } 202 | server { 203 | listen 80; 204 | server_name www.tomcats.com; 205 | location / { 206 | proxy_pass: http://tomcats; 207 | } 208 | } 209 | ``` 210 | 211 | 访问`www.tomcats.com`,将以轮询方式,分别访问三台 Tomcat,当然也可以使用加权轮询,例如: 212 | 213 | ```nginx 214 | server 192.168.1.174:8080 weight=1; 215 | server 192.168.1.175:8080 weight=2; 216 | server 192.168.1.176:8080 weight=5; 217 | ``` 218 | 219 | `weight`的值越大,当前服务器的 Tomcat 被访问的几率越大。 220 | 221 | ## upstream 指令 222 | 223 | ```nginx 224 | server 192.168.1.174:8080 max_conns=2; 225 | server 192.168.1.175:8080 max_conns=2; 226 | server 192.168.1.176:8080 max_conns=2; 227 | ``` 228 | 229 | - `max_conns`:限制每台server的连接数,用于保护避免过载,可起到限流作用; 230 | 231 | ```nginx 232 | server 192.168.1.174:8080 weight=1; 233 | server 192.168.1.175:8080 weight=2; 234 | server 192.168.1.176:8080 weight=5 slow_start=60s; 235 | ``` 236 | 237 | - `slow_start`:缓慢启动,`weight`逐渐增大,使某台服务器慢慢加入集群,方便该服务器完成一些前置化的操作,该指令需要注意: 238 | - 只能在商业版中使用; 239 | - 该参数不能使用在`hash`和`random load balancing`中; 240 | - 如果upstream中只有一台 server,则该参数无效; 241 | - `down`:标记服务节点不可用 242 | - `backup`:表示当前服务器节点是备用机, 只有在其他的服务器都宕机以后, 自己才会加入到集群中, 被用户访问到 243 | - `backup`参数不能使用在`hash`和`random load balancing`中; 244 | - `max_fails`:表示失败几次,则标记 server 已宕机,踢出服务,默认值为1 245 | - `fail_timeout`:表示失败的重试时间,默认值 10s 246 | - 示例:`max_fails=2 fail_timeout=15s `:15 秒内,请求某一 server 失败达 2 次后,则认为此 server 已经宕机,随后再过 15 秒,这 15 秒内不会有新的请求到达刚宕机的节点,会请求到正常的运行的 server,15秒后会有新请求再次请求挂掉的 server,如果还是失败,重复之前的操作; 247 | 248 | ## Keepalived 提高吞吐量 249 | 250 | ```nginx 251 | upstream tomcats { 252 | server 192.168.1.174:8080; 253 | server 192.168.1.175:8080; 254 | server 192.168.1.176:8080; 255 | # 设置长连接处理的数量 256 | keepalive 32; 257 | } 258 | server { 259 | listen 80; 260 | server_name www.tomcats.com; 261 | location / { 262 | proxy_pass: http://tomcats; 263 | # 设置长连接http的版本号 264 | proxy_http_version 1.1; 265 | # 清除 connection header 信息 266 | proxy_set_header Connection ""; 267 | } 268 | } 269 | ``` 270 | 271 | ## nginx的反向代理缓存 272 | 273 | ```nginx 274 | # proxy_cache_path 设置缓存目录 275 | # keys_zone 设置共享内存以及占用空间大小 276 | # max_size 设置缓存大小 277 | # inactive 超过此时间则被清理 278 | # use_temp_path 临时目录,使用后会影响nginx性能 279 | proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=1m use_temp_path=off; 280 | ``` 281 | 282 | ```nginx 283 | location / { 284 | proxy_pass http://tomcats; 285 | # 启用缓存,和keys_zone一致 286 | proxy_cache mycache; 287 | # 针对200和304状态码缓存时间为8小时 288 | proxy_cache_valid 200 304 8h; 289 | } 290 | ``` 291 | 292 | ## 配置ssl证书 293 | 294 | 1. 安装 `ssl` 模块 295 | 296 | 2. 将 ssl 证书`*.crt`和私钥`*.key`拷贝到`/usr/local/nginx/conf`目录中 297 | 298 | 3. 新增 server 监控 443 端口: 299 | 300 | ```nginx 301 | server{ 302 | listen 443; 303 | server_name www.leeyom.me; 304 | # 开启ssl 305 | ssl on; 306 | # 配置ssl证书 307 | ssl_certificate yourdomain.com.crt; 308 | # 配置证书秘钥 309 | ssl_certificate_key yourdomain.com.key; 310 | # ssl会话cache 311 | ssl_session_cache shared:SSL:1m; 312 | # ssl会话超时时间 313 | ssl_session_timeout 5m; 314 | # 配置加密套件,写法遵循 openssl 标准 315 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-G 316 | } 317 | ``` -------------------------------------------------------------------------------- /backup/50_[笔记]神的九十亿个名字.md: -------------------------------------------------------------------------------- 1 | # [[笔记]神的九十亿个名字](https://github.com/superleeyom/blog/issues/50) 2 | 3 | - 这个是科幻三巨头之一的阿瑟.克拉克的作品,整书是由多篇科幻短篇小说组成,故事有趣又引人思考,其中最有意思的一篇就是本书的名字,非常推荐。 4 | - 如果能穷尽所有字母,完成所有可能的排列组合,那么我们一定能找到那位至高神真正的名字。我们的功课就是要列举出所有的名字。 5 | - 无论是高达两千英尺、直插深谷的巍巍断崖,还是偏远山谷中如棋盘般纵横交错的梯田,都已无法再让他提起兴致。他倚靠在被山风打磨得光滑如镜的石壁上,愁眉苦脸地盯着远处的群峰。 6 | - 对她来说,安静就像一张雪白的画布,必须要用她自己的声音来填满。 7 | - 惊恐如涨潮的海水,渐渐淹没了我的心智,为了对抗神秘莫测的宇宙,每个人都会竖起的理智与逻辑的大坝,这会儿也被冲垮了。 8 | - 一座城市,或者类似的东西。你已经看到它的规模了,所以可以自行判断建造它的文明发达到了什么程度。我们已知的整个世界——包括海洋、大陆和山川——就像是一层薄雾,包裹着这个超出我们理解能力的世界。 9 | - 极目远眺,直至目光所及之处,是一片支离破碎的贫瘠荒原,布满山峦与沟壑、火山口与陨石坑。那些山脉峰峦高耸,直抵下垂的骄阳,仿佛火焰冲天的孤岛,正在黑暗之海中熊熊燃烧——在它们头顶,群星依然夺目,光芒却恒久不变。 10 | - 夕阳西下时天空的色彩,海浪拍打卵石海岸时的低吟,雨滴落地的轻柔,积雪无声的祝福……这些,还有上千种其他的馈赠,原本是属于他的合法遗产,但如今他只能从书籍和古老的影像记录中得知这一切。一想到这里,他的心中充满了被放逐的苦闷与哀伤。 -------------------------------------------------------------------------------- /backup/51_解决.IDEA.因为.Clash.代理问题引起的疑难杂症.md: -------------------------------------------------------------------------------- 1 | # [解决 IDEA 因为 Clash 代理问题引起的疑难杂症](https://github.com/superleeyom/blog/issues/51) 2 | 3 | ## 前言 4 | 5 | 最近我的 IDEA 因为 Clash 的问题,出现了各种奇奇怪的问题,就这些问题的解决做一个简单的记录。 6 | 7 | ## 1、You have JVM property "https.proxyHost" set to "127.0.0.1" 8 | 9 | 由于我在 Mac 上开了 Clash 代理软件,接管了系统代理,打开 IDEA 的 `Appearance & Behavior --> System Settings --> HTTP Proxy` 界面,提示 **You have JVM property "https.proxyHost" set to "127.0.0.1"**,解决方案就是:移除掉 Java 自带的 http 和 socket 代理,采用系统代理,选择 `Help -> Edit Custom VM Options`,增加如下的配置: 10 | 11 | ``` 12 | -Dhttp.proxyHost 13 | -Dhttp.proxyPort 14 | -Dhttps.proxyHost 15 | -Dhttps.proxyPort 16 | -DsocksProxyHost 17 | -DsocksProxyPort 18 | ``` 19 | 重启 IDEA 即可解决。 20 | 21 | ## 2、刷新 Maven 项目依赖,Build 控制台报 status: 502 Bad Gateway 22 | 23 | 因为公司有专门的 Maven 私服,而这个私服是需要通过代理才能访问,无法直接访问,这个只需要在 Maven 的 `setting.xml` 配置文件中,增加 HTTP 代理就行,让 Maven 强制走 Clash 代理,比如我的 Clash 的 HTTP 代理端口是 7890,则配置如下: 24 | 25 | ```xml 26 | 27 | 28 | clash proxy 29 | true 30 | http 31 | 127.0.0.1 32 | 7890 33 | 34 | 35 | ``` 36 | 37 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20220928135119.png) 38 | 39 | ## 3、Maven 依赖包导入错误:GC overhead limit exceeded 40 | 41 | 该问题是于 IDEA 里为 Maven 的 importer 和 runner 设置的 JVM 最大堆内存(-Xmx)过小而导致的,只需要将 Maven 如下的两个地方设置堆内存设置大点就行: 42 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20220928133622.png) 43 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/20220928133650.png) 44 | 然后重新刷新下,就不会报内存不足了。 -------------------------------------------------------------------------------- /backup/53_2022年终总结之我的买房经历.md: -------------------------------------------------------------------------------- 1 | # [2022年终总结之我的买房经历](https://github.com/superleeyom/blog/issues/53) 2 | 3 | - ## 1、前言 4 | - 其实我原本想偷懒不想写这篇文章的,但是后面想想,买房也是人生的一件大事,把这个整个过程记录下来,其实也是蛮有意义的,所以就有了今天的这篇文章,整篇文章记录了我整个买房的心路历程,也借此分享给那些正在考虑买房的朋友们,希望大家都能早日上车。 5 | - ## 2、为什么想要买房 6 | - 我曾在2021年离开深圳的时候,写了一篇[《深漂5年随想》](https://github.com/superleeyom/blog/issues/37) 的文章,里面曾写到:「我觉得我想离开深圳的最大的一个原因是自己对房价的绝望吧」,常年的租房生活,让我实在是受够了那种漂泊和居无定所的状态,我就特别渴望拥有了一套属于自己的房子。机缘巧合,我来到了号称省会房价洼地的城市:长沙,在这工作和生活一段时间后,慢慢的喜欢上了这座城市,所以脑袋里面就萌生了要在这里买房的想法。 7 | - ## 3、买房前期准备 8 | - ### 3.1、户口迁移 9 | - 要在长沙这边买房,首先你得要有购房资格,目前就说最常见的两种,更详细的可以见:[《长沙购房资格》](https://imgbdb4.bendibao.com/csbdb/20228/04/2022804094542_18583.jpg): 10 | - 1、户籍不在长沙,只需要连续缴纳 2 年的长沙社保 11 | - 2、户籍转到长沙,在长沙工作,连续缴纳1年的长沙社保 12 | - 所以很明显我不符合条件1,只能靠条件2,所以在2021年10月国庆节的时候,我把自己的户口从邵阳老家,迁到了长沙。迁长沙户口其实比较简单,直接可以在线操作,通过 APP **我的长沙**:**户口服务-自贸区人才引进落户**,填写相关的信息,即可完成落户,预计等待 1周的时间,就可以收到新的户口页,原本的旧的户口页也就失效了。 13 | - ### 3.2、公积金迁移 14 | - 由于后期买房肯定需要公积金贷款,所以我就计划把深圳的公积金额度全部转移到长沙的公积金中心来,但是由于政策缘故,必须要在你工作的城市工作了6个月后,才能将公积金转移到另外一个城市,所以公积金的迁移,是在 2022年的3、4月份的时候操作的,这个步骤也可以直接在线操作,住建部推出的“全国住房公积金小程序”,手机上点一点,就可以轻松办理公积金转移接续,还是蛮方便的,具体的流程可以见:[公积金转移接续,手机上就能办啦!(附操作指南)](http://www.gov.cn/fuwu/2021-10/15/content_5642820.htm),转移大概花了 10个工作日的样子,就完成了转移。 15 | - ### 3.3、与置业顾问的初次接触 16 | - 在2022年4月份的时候,我的一个大学同学过来长沙玩,从聊天中得知他在长沙买了房子,他的表哥刘哥就是他的置业顾问,恰好那个时候我也初步有打算买房的想法了,然后就加了刘哥的微信,两个人碰面聊了一两个小时,刚开始的时候,对于房地产这块算是一窍不通,通过和置业顾问的简单唠嗑后,我也算是对长沙房地产有了一个初步的了解,但是由于我购房资格最迟都要到2022年的10月份才有,所以刘哥的建议是要我再过几个月后再来,前期可以先明确自己要买的片区和楼盘,等到了大概九、十月份的时候再联系他,期间不懂的都可以问他。 17 | - ### 3.4、购房知识的学习 18 | - 初期我对房地产这块确实是啥都不懂,什么户型图,楼盘,真的是一窍不通,我当时还在推特上专门发了条推,结果一堆的热心的推友来建议: 19 | - ![](http://image.leeyom.top/img/20230108142726.png) 20 | - 后面我也忘了是在哪里看到了(知乎?),有个专门做长沙房地产的公众号:**春小楼**,他写了很多针对小白用户买房的教程,专业术语介绍,楼盘测评,满满的全是干货,对于刚准备买房的小白用户,真的是太有用了,真的非常推荐这个公众号。 21 | - 另外除了公众号,可以在安居客 APP 上查看各种楼盘信息,每天可以没事的时候,随便看看,了解下楼盘信息,**但是千万不要注册手机号**,我之前就是因为不懂,注册了手机号,导致手机号被泄露,天天被一堆的房产中介骚扰,苦不堪言。 22 | - 这里对于长沙房市,可以再推荐一个小程序,叫做:**长沙选房通**,这上面可以了解长沙所有的楼盘信息,非常的实用。 23 | - 已上几件事情准备完毕后,基本上只需要静静等到购买资格到手了。 24 | - ## 4、开始准备买房 25 | - 在储备了部分的购房知识之后,时间也来到了2022年的10月份,这个时候我也有了购房资格了,恰好国家也出台了很多购房利好政策:**公积金最高额度可以到70w和商贷利率降至4.1**,这个时候我觉得这是最近几年最好的购房时机了。 26 | - ### 4.1、明确购房预算 27 | - 首先需要明确自己手里有多少的钱可以准备首付,自己的月银行流水是多少钱。打个比方说,你手里有50万来准备首付,最简单的算法是按首套三成来算,50万除以0.3,得出166万的房款总价。但是这个还不够,你还要把维修基金和契税也算在内,这两者按4万元去预估,也就是你的首付其实只有46万元。另一个方面你可以贷款的金额跟你的银行流水也有一定关系,流水需要覆盖你的月供2.2倍。这样算来,你可以接受的房款总额为153万元,按揭贷款总额为107万元,这样30年贷款月供为5517.31元,你的银行流水需要12000元以上,才能承受这个月供。 28 | - ### 4.2、明确购房需求 29 | - 刚需和改善是现在购房者买房的主要倾向,我给自己的定位就是刚需,主要的需求点如下: 30 | - 1、主要还是自住 31 | - 2、交通便利性,有地铁,上班交通方便,通勤时间在半小时以内 32 | - 3、增值以及流通性,考虑后期置换的可能 33 | - 4、最低要求三室户型 34 | - 5、居住环境安静,楼层考虑中间偏上 35 | - 6、周边有学校,考虑到小孩上学问题 36 | - 所以明确好自己的需求点非常重要,因为:**在预算有限的情况下,真的没有十全十美的房子**,带着自己的需求点,才能后续更好的挑选适合自己的房子。 37 | - ### 4.3、片区的选择 38 | - 由于我自己目前从事IT软件行业,因为河西有麓谷软件园,利好互联网行业,而且我现在上班的公司也在河西,所以我压根不考虑河东,只考虑河西的楼盘。河西这边目前两个区的就是岳麓区和望城区,我个人考虑的是岳麓区,退而求其次再考虑望城区。 39 | - 区域选择好后,进一步就是选择板块,通过我自己的分析,我刚开始初步考虑的几个板块如下: 40 | - **滨江,月亮岛,谷山,麓谷,市政府,梅溪湖,洋湖** 41 | - 后面我觉得梅溪湖和洋湖离我目前上班的地方(湖南省金融中心)还是太远了,就不想考虑,进一步缩减范围: 42 | - **滨江,月亮岛,谷山,麓谷,市政府** 43 | - ### 4.4、楼盘的选择 44 | - 在确定好片区后,基本上就可以根据自己的预算,选择适合自己的楼盘了,按照我自己的经验,通常选择楼盘需要考虑如下的几个点: 45 | - **1、开发商**:在面临各种房地产暴雷事件后,选择**国企&央企**背景的房地产公司真的太重要了,即使暴雷了,好歹也有国家兜底,毕竟大部分的人买房都是掏空了家底,谁不愿意看到自己买的房子无法交付。 46 | - **2、地段**:地段好,以后的房子升值空间大,房子流通性高,地段也是我们要考虑的一个点。 47 | - **3、户型**:户型这个主要就是看自己喜欢了,我的计划是最低要3/4室2厅2卫,这样的话,基本上可以满足以后的三口之家,自己和老婆1个房间,小孩1个房间,要是以后有亲戚或者父母过来住1个房间,所以预算有限的情况下,尽量的考虑3~4室的户型。 48 | - **4、学区**:考虑到以后小孩上学问题,居住的小区有小学和初中也是非常重要的点。 49 | - **5、交通**:地铁一响,黄金万两,所以如果购买的房子有地铁,真的会极大的增加出行的便利性。 50 | - **6、周边配套**:周边生活设施是否齐全,比如超市,商超,菜市场等等这些,不然买个菜都还的去很远的地方。 51 | - **7、价格**:在明确了自己的购房预算后,基本上价格这个事情也就明确了,这里要注意点就是毛坯和精装的区别,目前大部分的楼盘都是精装,极少数的是毛坯,在长沙这边,房地产的精装的预算一般是2500的价格,但是听身边的同事反馈,实际交付的质量都不值2500,所以如果能选择毛坯,尽量还是选择毛坯吧,以后自己花点时间和精力装修。 52 | - **8、其他**:其他的要考虑的点就是比如:交付时间、精装信息、塔楼还是板楼、楼层高度等等,这些点自己可以酌情考虑。 53 | - 我自己比较懒,实地去看的楼盘不多,目前粗略的总结了我目前看过的几个盘,通过Excel 汇总了下,有部分数据可能不准确,仅供参考:[2022购房楼盘总结](https://docs.qq.com/sheet/DR0p3RXFhSElSVWNN?tab=BB08J2) 54 | - ![](http://image.leeyom.top/img/20230108153747.png) 55 | - 其实到最后我只考虑两个盘:中建麓江府和天健云麓府,由于中建麓江府需要认筹抽签,我当时打算是:如果抽中,就买中建麓江府,否则就买天健云麓府,结果认筹当天,中建麓江府的认筹人数没有超过房源,最后采取了按认筹顺序选房,当天和女朋友一起选到了我们还算满意的楼层,最终上车了中建麓江府,我总结了下我选择麓江府的几个主要原因: 56 | - 1、开发商是有国企和央企背景的中建和长沙轨道交通联合打造的,背景还算靠谱 57 | - 2、地段位于长沙二环内,滨江板块,靠着湖南省金融中心,离我上班的地方也挺近的 58 | - 3、118的户型非常不错,可以做四室,类板楼,18层的小高层 59 | - 4、小区自建幼儿园,小区门口紧挨着白马实验小学,初中长郡滨江中学也离的很近 60 | - 5、目前地铁已开通的有福元大桥西地铁站,已规划的有8号线岳华路站,过了福元大桥就可以去开福区中心北辰三角洲 61 | - 6、周边配套有凯德壹、渔人码头、湖南省金融中心、奥克斯广场,都已经非常成熟 62 | - 7、价格的话是 1.43w 的价格买的毛坯,说实话价格确实比较贵,但是还是那句话,一分钱一分货,没有十全十美的房子,你要想它各个方面都满足自己的要求,你就得承受它的高昂的价格 63 | - ### 4.5、贷款的选择 64 | - 楼盘选好了,楼层也确定了,剩下的就是要交首付款了,这里就涉及到一个贷款方式的选择: 65 | - **等额本金**:逐期递减还款,总利息最少,但是每月月供都在变,越来越少 66 | - **等额本息**:每期等额还款,总利息多 67 | - 我咨询了很多的朋友他们目前的还款方式,大部分的是选择等额本息,他们的建议是如果以后有提前还款的想法,就选择等额本金,否则就选择等额本息,考虑到通货膨胀以及我自己后期并无提前还款计划,结合我自身目前的需求,**我最终选择了等额本息的还款方式,首付3.6成,一共61w,总贷款107w,贷款方式采用组合贷,70w的公积金,3.1的利率,37w的商贷,4.1的利率,30年月供4700~4800,总**体在我的承受范围之类。 68 | - ## 5、买房后续 69 | - 基本上交完首付,剩下的就是签合同、公积金面签等一系列的繁琐的手续,需要准备的资料也是一大堆,我粗略整理了下如下的资料: 70 | - ![](http://image.leeyom.top/img/20230108163825.png) 71 | - 合同现在都是线上签署,用的APP叫 **长沙住房**,大家签署的基本上都是一样的,如果不放心可以花点钱让专业的人审一下。 72 | - 再个就是首付款的收据一定要保管好,后期交房的时候,需要拿这个首付款的收据去换那个带税率的发票,所以这个不能搞丢了,后期一定要保管好。 73 | - 基本上这些搞完,公积金贷款放款后,就开始还贷了,30年的换贷生涯就开始了。 74 | - ## 6、总结 75 | - 我在 2022年11月13号的晚上,发了条推总结了自己当时买房后的感触: 76 | - ![](http://image.leeyom.top/img/20230108165450.png) 77 | - 但是现在再仔细想想,我感觉一切都是值得的,对于我们这种农村娃来说,要想在大城市安家,确实挺难的,但是只要迈出第一步,后面的问题自然而然都会慢慢解决。**房子是家的依托**,虽然租房也可以使生活愉快,但从心底里不觉得是家,有一个自己的小窝,给身边人一个稳定的家,这就是我买房最大的动力吧。 78 | - 以上便是我整个2022年的买房经历,希望能帮助到大家!有问题欢迎和我交流! 79 | 80 | --- 81 | 82 | @godvmxi 选择问题罢了,最重要的是在自己能力的可承受范围内,尽量把自己的生活过得更好 83 | 84 | --- 85 | 86 | > 今年计划回长沙发展,提前了解下,感谢楼主❤️ 87 | 88 | 欢迎回家发展 @pengan95 -------------------------------------------------------------------------------- /backup/54_我的跑步数据流.md: -------------------------------------------------------------------------------- 1 | # [我的跑步数据流](https://github.com/superleeyom/blog/issues/54) 2 | 3 | - ## 前言 4 | - 现在大多数的健身类APP,都不愿意把自家的数据开放给普通的用户,作为一名程序员,同时也作为一名跑者,我一直在探索如何才能主动掌握自己的跑步数据,直到现在,我终于可以掌握自己的跑步数据流,今天就来分享下,我是如何管理自己的跑步数据的。 5 | - ## 设备 6 | - Smartisan T2 --> iPhone X --> Apple Watch Series 4 7 | - 我是2017年开始跑步的,那个时候就是用手机(Smartisan T2、iPhone X)记录跑步数据,直到2018年,我的朋友推荐我使用 Apple Watch,当时恰好发布最新款的 Apple Watch Series 4,在京东上首发购入了蜂窝版本的 Apple Watch Series 4,一直使至今。如今四五年过去了,这块手表除了续航,其他的一点问题都没有,对于习惯跑步健身的朋友,Apple Watch 简直是完美的搭档,如果你使用的是 iPhone,真的强烈建议购买一块。 8 | - ## 健身应用 9 | - 咕咚 --> Keep --> Nike Run Club --> Apple Watch 体能训练 10 | - 起初为了能激励自己能更好的坚持跑步,我开始尝试加入国内的跑步社区,我最初使用的是咕咚 APP记录我的跑步数据,后面发现咕咚 APP的广告越来越多,最终弃用,然后又转战 Keep,结果使用了一两年,Keep 也因为业务扩展,APP 内到处充斥着广告、短视频,无奈也最终弃用。后面经过推友的推荐,我开始尝试使用 Nike Run Club,非常的对我口味,无广告,简洁舒适,Apple Watch 也原生支持,可惜22年的时候,Nike关闭了中国大陆地区的 Nike Run Club 运营,最后,我干脆就直接就用 Apple Watch 原生的体能训练,来记录自己的跑步数据,原生自带,简洁干练,无广告,无干扰,唯一的缺点就是对于数据统计这块比较弱。 11 | - ## 数据流 12 | - 为了一直想补齐数据统计这块的缺陷,在偶然的机会下,了解到国外的一款健身APP:Strava,这个不就是我想要的跑步 APP 吗?无广告,开放的 API,丰富的数据统计分析(部分功能需要订阅),毫不犹豫在某宝上,开通了1年的会员(官方自带的订阅比较贵,建议上神奇的某宝,可以省不少钱)。 13 | - ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/IMG_908.JPEG) 14 | - 这样,每次使用 Apple Watch 的原生体能训练跑完步后,数据会自动汇总到 Apple Health(健康)应用,然后 Strava 会自动读取健康应用里面的体能训练记录并上传,当然要实现这个自动化需要完成如下两个设置: 15 | - 1、在iPhone【系统设置-通用-后台App刷新】中给 Strava 开启后台刷新权限。PS:**iPhone的低电量模式会关闭后台 APP 刷新权限** 16 | - 2、在 Strava 应用内【应用程序、服务和设备-健康应用程序】,打开【自动上传】 17 | - ![iPhone设置和Strava设置](https://raw.githubusercontent.com/superleeyom/blog/main/img/IMG_904.JPEG) 18 | - 只要数据上传到了 Strava,我们就可以借助 Strava 开放的 API,拿到我们自己的数据,再借助 [running_page](https://github.com/yihong0618/running_page) 项目上传到其他的平台,比如佳明、Nike Run Club 等等,将数据放到多个平台备份。 19 | - 所以我的整个的跑步数据流如下: 20 | - ![](http://image.leeyom.top/img/my_running_flow.jpg) 21 | - ## 总结 22 | - 以上便是我的整个的跑步数据流,所有的流程都是自动化执行,你要做的就是点开体能训练,开始锻炼就行。在大数据时代,将自己的数据掌握在自己的手里,心里会非常的踏实,也希望能给有相同需求的朋友带来一点启发,如有疑问,有问题欢迎和我交流。 23 | 24 | --- 25 | 26 | > Strava怎么从app watch中拿到数据? 27 | 28 | @JiangLongLiu 实际Strava是从健康应用里面读取的,需要你授权给Strava 29 | 30 | --- 31 | 32 | > 推荐你尝试下WorkOutDoors,补足了体能训练的很多短板,同样可以搭配strava一起使用 33 | 34 | @cuikai2021 还是更喜欢原生的体能训练 -------------------------------------------------------------------------------- /backup/55_2023上海之旅.md: -------------------------------------------------------------------------------- 1 | # [2023上海之旅](https://github.com/superleeyom/blog/issues/55) 2 | 3 | - ## 初衷 4 | - 今年12月份我和女朋友马上就要领证了,想着我们俩自从疫情之后,就从来没有一起出去旅游过,所以女朋友提议,将原本用于求婚和购买钻石的预算,用来作为这次难忘之旅的资金,两人一拍即合,随即便踏上了上海魔都之旅。 5 | - 在做好一系列准备工作后,包括预订酒店、购买机票和火车票、制定详细攻略等,我们于11月18日周六上午9点从长沙出发,飞往了上海。顺利地,我们于10点45分抵达了上海。我们的运气真是不错,这个周末上海的天气异常宜人。而且正值冬季,感受到那抹暖暖的阳光,简直美不胜收。在这个季节里漫步在街头,仿佛是置身于一片宁静而温暖的画卷中,让人感到无比惬意。 6 | - 上海的地铁网络非常发达,整个旅途我们基本上都是乘坐地铁交通。而且我发现一个有趣的细节,就是上海的地铁站似乎没有像其他地方(比如长沙地铁)那样,工作人员拿个金属检测仪在乘客身上扫来扫去,这一点真的是好评啊。只需行李通过安检,就能完成整个安检流程,这样的效率和体验真的提升了不少。 7 | - ## 沪西老弄堂面馆 8 | - 一踏上上海的土地,我们迅速乘坐地铁前往第一站:沪西老弄堂面馆(静安寺店)。在B站上听到不少UP主推荐过这家面馆,加上我自己也是一位面食爱好者,自然是迫不及待地想要品尝一下上海的地道味道。抵达时恰好是饭点,因此排队的人群相对较多,我们排了差不多半个小时的样子才吃上。 9 | - ![202311211608024](https://github.com/superleeyom/blog/assets/22115219/589fd10c-43d7-42d1-913e-5fd01e1f7711) 10 | - ![202311211609220](https://github.com/superleeyom/blog/assets/22115219/39392b4a-1145-48b8-83f7-415e816abd8d) 11 | - 虽然价格相对于外面一般的面馆稍贵,人均消费在40多元,但我们决定尝尝他们家的招牌菜。我们点了一份大肠拌面、一份蛤蜊猪肝拌面,以及一份炸猪排,总共花费了93.5块。面的分量十分充足,浇头和面搅拌均匀后,让人垂涎欲滴。大肠、蛤蜊和猪肝的味道都带有一丝甜味,非常独特。每一口面都充满了风味,吃完后味蕾得到了极大的满足感。至于炸猪排,虽然相对普通,但搭配当地的辣酱油沾食,味道也颇为不错。总的来说,这次的用餐体验是物有所值的。 12 | - ![202311211458811](https://github.com/superleeyom/blog/assets/22115219/da6e3bea-1af5-4fa3-a04f-6de216afddcb) 13 | - ## 外滩 & 东方明珠 14 | - 从沪西老弄堂面馆吃完中饭后,我们搭乘地铁前往上海的著名景点:「外滩」,去看上海的地标:「东方明珠」。当天是周六,天气格外宜人,晴空万里,简直是理想的拍照日。外滩的人流相对较少,主要是一些观光游客和当地居民悠闲漫步。沿着黄埔江畔,走走停停,晒着冬日暖暖的阳光,好是惬意。我们还前往了万国建筑、和平饭店以及南京步行街,沿途漫步在上海的繁华之中,深刻感受上海这座城市的生活气息。 15 | - ![202311211514212](https://github.com/superleeyom/blog/assets/22115219/99e17aa3-26d6-421b-a78b-842c0045cb6d) 16 | - ![](http://image.leeyom.top/img/202311211517629.jpeg) 17 | - ![](http://image.leeyom.top/img/202311211518486.jpeg) 18 | - ## 武康路 19 | - 武康路是我从关注的博主 [hayami](https://t.me/hayami_kiraa) 那里得知的,这次来上海,当然不能错过这个著名的地方,去亲身感受一下武康大楼和武康路的魅力。武康路两旁是各种有着历史沉淀的精致老洋房,整条街道保持着干净整洁。沿途行走,感受到老洋房的宁静与历史沧桑。街道两边都是一些打扮精致的帅哥和美女在散步,沿着武康路一路走走逛逛,体验下上海的city walk文化。 20 | - ![](http://image.leeyom.top/img/202311211531453.jpeg) 21 | - ![](http://image.leeyom.top/img/202311211533149.jpeg) 22 | - ## 上海迪士尼乐园 23 | - 这次上海之行中,最让人难以忘怀的莫过于去上海迪士尼乐园,这简直是无法与其他任何经历相提并论。周日一大早,我们早早地离开了酒店,搭乘去往迪士尼的班车。上午9点,我们抵达了迪士尼乐园,或许是因为周日的天气宜人,游客络绎不绝。在此之前,我们在小红书上查阅攻略,得知周日可能相对人少,然而实际情况却并非如此,人潮涌动,倍感焦虑。不过好在女朋友一路耐心的开导,让我有了更多的期待。即便人再多,也无法减弱我们对迪士尼的期待与热情,哈哈哈。 24 | - ![202311211601424](https://github.com/superleeyom/blog/assets/22115219/9b6da5e0-360b-42b3-8891-1201a5aadea8) 25 | - 在上午9:30左右入园后,我们首先体验的是「雷鸣山漂流」。这个项目相对来说比较温和,八个人一组,全程需要穿上雨衣,以防「湿身」。整个漂流过程中,最为刺激的部分莫过于穿越那个黑色的隧道,一片漆黑之中,突然响起的雷声,隧道内部还有一只巨大的恐龙在咆哮,营造出了一种紧张刺激的氛围。(ps:这个我网上找的图,当时没法带手机拍摄) 26 | - ![202311211936597](https://github.com/superleeyom/blog/assets/22115219/1f531323-f120-41c8-a665-729907144a6c) 27 | - 在完成「雷鸣山漂流」的体验后,我们前往观看了「风暴来临:杰克船长之惊天特技大冒险」舞台表演。这对我来说是在迪士尼见到的最为震撼的演出之一。演员们在游客进入剧场之前就开始与观众互动,引导大家积极参与。整个演出高潮迭起,而最令人难忘的时刻莫过于在一片雾气中,舞台前的视野模糊不清,然后幕布缓缓升起,揭开了整个实景海盗船的神秘面纱。这一刻,我不禁起了鸡皮疙瘩,视觉上的冲击感太过强烈。真心推荐这个项目,是迪士尼乐园中不可错过的精彩表演。 28 | - ![202311211945349](https://github.com/superleeyom/blog/assets/22115219/08173ccd-14b7-43fb-ba71-496da911e78c) 29 | - ![202311211947442](https://github.com/superleeyom/blog/assets/22115219/58d2bd89-2bdb-44df-a243-621113d02a32) 30 | 31 | - 第二个让我深感震撼的项目是「奇幻冬日巡演」。在这个表演中,迪士尼的各位朋友们都纷纷登场,包括米奇、米妮、唐老鸭、白雪公主、七个小矮人等童年时代的动画人物。他们欢快地与游客打招呼,沿着巡演路线边走边跳。看到这些可爱的动画人物真实地出现在眼前,仿佛时光倒流回到了那个纯真的童年时代,让我感到无比怀念。这一刻,迪士尼乐园成为了一个魔法般的世界,带领我重温了那段美好的童年回忆。 32 | - ![](http://image.leeyom.top/img/202311211957522.jpeg) 33 | - ![](http://image.leeyom.top/img/202311211958064.jpeg) 34 | - 后面我们还去体验了「创极速光轮」,这是一个相当刺激的项目,有点类似过山车的感觉。欣赏了「冰雪奇缘:欢唱盛会」表演,这是一场充满歌舞的节目,是个逛累了可以休息的好去处。 35 | - 最后,我们来到了城堡附近拍照,这里的景色非常出色,尤其是与另一半一同来迪士尼时,务必要在这里留下美好瞬间。城堡周围拍照真的特别出片,是个打卡必备的地方。这次在迪士尼的冒险之旅,不仅让我们体验了刺激的项目,还让我们在梦幻般的城堡前留下了珍贵的回忆。 36 | - ![](http://image.leeyom.top/img/202311212007896.jpeg) 37 | - ![](http://image.leeyom.top/img/202311212006536.jpeg) 38 | - 确实,迪士尼的物价确实有些离谱,火鸡腿85元一份,玉米35元一根,的确是比一般地方高出不少。在迪士尼游玩时,可以自备一些小零食和小面包,在排队等待的时候解决一下饥饿感。另外,如果想要吃饭,可以考虑到园区外的迪士尼小镇。那里的物价相对便宜一些,我们两个人晚餐在「大食代」,点了一份酸菜鱼和凤爪,总共花费100出头吧,味道还不错,而且能够吃饱。 39 | - ## 感受 40 | - 这次突发奇想的旅行对我而言太有意义了。自2017年以来,我一直没有好好出去玩过,这次和女朋友一起在上海度过的两天,让我收获了极大的快乐。我们手牵手漫步在上海的街头巷尾,品尝了当地特色美食;一同进入了迪士尼童话世界,体验了各种刺激的项目,观赏了震撼的表演,与可爱的卡通人物亲密接触。上海这座城市充满了独特的魅力,让我留下了美好的回忆,期待着再次踏上这片充满生机的土地。 -------------------------------------------------------------------------------- /backup/56_Postman如何配置动态端口和IP.md: -------------------------------------------------------------------------------- 1 | # [Postman如何配置动态端口和IP](https://github.com/superleeyom/blog/issues/56) 2 | 3 | ## 背景 4 | 5 | 起因是每次使用 Postman 调试服务接口的时候,如果服务一旦被重启,对应服务的ip和端口就会改变,就需要重新配置服务的ip和端口,非常的繁琐以及麻烦。所以就针对这个问题,做了一点研究,简单的记录下。 6 | 7 | ## 步骤 8 | 9 | ### 1、维护多套环境及相应的环境变量 10 | 11 | 实际开发中,我们一般会有多套环境,比如:开发环境dev、测试环境test、生产环境prod等等。每个环境,我们配置好对应的consul(注册中心)环境变量、对应服务名环境变量,如图所示: 12 | 13 | 14 | ![image.png](https://cdn.jsdelivr.net/gh/superleeyom/blog/images/1702553954147.png) 15 | 16 | ### 2、维护collection目录层级以及脚本 17 | 18 | 建议以服务名为目录,在对应的目录层级,维护如下的脚本,动态的获取注册中心上对应的指定服务的ip和端口: 19 | ```js 20 | pm.sendRequest(pm.environment.get("consul_service") + "trade-assets-service", 21 | function(err, res) { 22 | if (err) { 23 | console.log(err); 24 | } else { 25 | pm.environment.set("trade-assets-service", res.json()[0].ServiceAddress + ":" + res.json()[0].ServicePort); 26 | } 27 | }); 28 | ``` 29 | 30 | ![image.png](https://cdn.jsdelivr.net/gh/superleeyom/blog/images/1702554291804.png) 31 | 注: "trade-assets-service" 根据自己目录名变化 32 | 33 | ### 3、在对应的collection目录模拟request请求 34 | 35 | 维护好了动态获取脚本后,就可以正常的模拟对应的request请求,请求里面的ip和端口只需要填充对应的服务的占位符即可: 36 | 37 | ![image.png](https://cdn.jsdelivr.net/gh/superleeyom/blog/images/1702554459873.png) -------------------------------------------------------------------------------- /backup/57_2024年个人周报.md: -------------------------------------------------------------------------------- 1 | # [2024年个人周报](https://github.com/superleeyom/blog/issues/57) 2 | 3 | ## 2024 年第 29 周 4 | - 隔了快一个多月没有更新了,这一个多月里,一直在忙于装修,每周末基本上就是工地和建材市场到处跑,目前水电和瓷砖已经搞完,下一步就是木工,装修真的是一场修行,每天就是在妥协和不将就徘徊,真的希望早点完工,太操心了。 5 | - 周六因为贴的是木纹砖,师傅为了对缝,导致废了很多砖,为了尽量不浪费这些废砖,最终,我们决定把这些废砖,拉到加工厂,花了 138 块,切割加工打磨海棠角,让泥工师傅把它贴在了阳台的窗台上,这样就可以省下一笔窗台石的费用 6 | - 周日就去全屋定制厂家那边,和设计师确定了柜子的内部细节方案,签订了相关的合同,接下来就是要明确相应的电器的尺寸,然后才能柜子下单制作 7 | - 周日跟老婆一起去医院做了一次体检,每年一次的体检还是很有必要的 8 | 9 | ## 2024 年第 23 周 10 | - 本周没咋跑步,跑了两次,依旧是阴雨绵绵的天气 11 | - 终于收到收房通知书了,下周末准备去收房了,太开心了,终于有了自己的小家 12 | - 端午节回老婆家一起过端午节,吃了老丈人做的小龙虾,超级好吃 13 | - 重新用回了网易云音乐,还开了会员,发现之前的听歌记录居然还在 14 | ## 2024 年第 22 周 15 | - 本周只跑步了 2 次,然后周日跟老婆去爬了岳麓山,剩余的时间都是在下雨,真的太讨厌下雨了。 16 | - 本周装修的效果图出来了,周末又去和设计师当面对了下细节,另外这个中旬就要交房了 17 | - 周六下雨,和老婆两个人宅家看了电影《哥斯拉大战金刚 2:帝国崛起》 18 | - 不得不说闲鱼上的东西是真的实惠啊,原本在美团上的要 128 的券,在闲鱼只要 59,9.9 的瑞幸冰美式,只要 5.5,发现了省钱的新方法 19 | 20 | ## 2024 年第 21 周 21 | - 本周跑步 5 次,累计 21.85公里,周六去橘子洲参加了招行的一个小比赛,由于是早上八点开跑,天气是真的热啊,差点中暑了。 22 | - 天气越来越热,换上了凉席,打开了空调 23 | - 极速拍档的《极速之旅-北极篇》拍的真好啊,每周必追,可惜马上就要完结了,美丽的阿拉斯加,真让人向往啊 24 | 25 | ## 2024 年第 20 周 26 | 27 | - 本周跑步 5 次,累计 20.54km,夏天穿运动装上下班的好处就是,跑步前免去了换衣服的麻烦 28 | - 周末去和设计师确定了装修的平面图 29 | - 最近在追一部动漫:《怪兽 8 号》,搞笑又热血,推荐推荐 30 | - 周末又跟老婆去吃了一次转转热卤,真的太好吃了 31 | - 沃尔玛的 9.9 的瑞士碎卷,虽说是边角料,但是 9.9 的价格,还要啥自行车 32 | ## 2024 年第 19 周 33 | - 本周跑步 6 次,累计 23.61km,周末去了一趟迪卡侬,买了几件跑步的衣服和 T 桖 34 | - 周日把装修敲定了,现在就是开始正式进入设计阶段,坐等交房开工了 35 | - 推荐一个音乐播客频道《三分音福》,每天写代码的时候,就把它当白噪音 36 | - 最近喝冰美式比较多,基本上每天的早上都会来上一杯瑞幸的冰美式,一杯才 7 块 8 毛钱,要啥自行车 37 | ## 2024 年第 18 周 38 | 39 | - 五一假期前去医院复查了一次,整体指标一切正常 40 | - 五一前两天假期考察了几家装修公司,其中一家聊到了晚上十一点才回去 41 | - 五一剩下几天回老婆老家休息了几天,买了十斤小龙虾,自制的柠檬可乐,看了想看的电影,安逸的很 42 | 43 | ## 2024 年第 16 周 44 | 45 | - 本周日参加 2024 湘江半马,原计划是跑到 2 小时内,结果我还是高估自己了,最后差不多 2 小时 5 分完赛,最后 5 公里跑蹦了。 46 | - 周末考察了 3 家装修公司,只能说装修公司都是满满的套路,如果不做功课,很容易会被忽悠。 47 | 48 | ## 2024 年第 15 周 49 | 50 | - 本周跑步 3 次,下周末长沙湘江半马,随缘跑吧 51 | - Netflix 的《体能之巅:百人大挑战 2》上线了,感觉还不错,周末看了一集 52 | - 周六又去看了一家封窗,老板人还挺爽快的,聊得也还行,纳入考虑的范围之类 53 | - 最近一直在想装修的一些事情,现在在想到底是自装呢,还是找装修公司,也是比较头痛。 54 | 55 | ## 2024 年第 14 周 56 | 57 | - 感冒了一两周,总算恢复了 58 | - 清明节休息了三天,在家和老婆一起看了阮经天以前的老电影《艋舺》以及最新Netflix上映的韩剧《寄生兽:灰色部队》,都还不错,值得推荐 59 | - 最近在看封窗,最大的感受就是「乱」,定价乱,服务乱,标准乱,不做好功课,分分钟会被坑 60 | 61 | ## 2024 年第 12 周 62 | 63 | - 这周跑步 2 两次,这周工作太忙了 64 | - Netflix 版本的《三体》开播了,看了一集,感觉还不错,会继续追下去 65 | - 周末自己在家做饭:香煎鲈鱼、清炒空心菜、大蒜叶炒腊肠、干煸豆角、红豆粥等等 66 | - 周末这两天长沙温度很高,一到换季,总会感冒一下 67 | 68 | ## 2024 年第 11 周 69 | 70 | - 本周跑步 3 次,最近开始控制饮食了 71 | - 我老婆以前还蛮喜欢吃那种街边的炸淀粉肠,自从这周的 315 曝光了淀粉肠之后,我老婆就说以后再也不吃了 72 | - 最近老婆的奶奶身体不咋好,我们周末又回去了一次,老人在丧失了活动自理的能力后,真的是可怜 73 | ## 2024 年第 10 周 74 | - 本周跑步 4 次,天气放暖了,要继续开干了 75 | - 报名了 2024 年的湘江半马,4 月 21 日开赛,开始认真准备备赛了 76 | - 开始支持国产跑鞋了,入手了李宁的赤兔 7,目前穿了几天,感觉脚感还不错 77 | - 周末和老婆两个人又驱车回株洲了,乡下的生活就是放松 78 | 79 | ## 2024年第9周 80 | - 周末陪老婆回老家休息了两天,周末逃离城市回乡下充充电,短暂的放松一下 81 | - 周末看了最近大火的《周处除三害》,推荐一定要看非删减版的,陈桂林在灵修中心挨个爆头的戏,看的真过瘾呐 82 | - 最近读完了《蛤蟆先生去看心理医生》这本书,让我印象比较深刻的一句话:”所谓活得真实,就是真诚地回应当下的需求。这能打破从童年延续而来的因果循环,让真实的自我摆脱过去经历的束缚,在自由中成为真正的自己。“ 83 | 84 | ## 2024年第8周 85 | - 本周继续开始锻炼了,周末健身房跑了2次,长沙最近天气是真的冷 86 | - 原本计划周末回老婆家的,但是最近冻雨,只能在长沙过元宵 87 | - 周末写了一篇博客:[《2024香港银行卡办卡之旅》](https://github.com/superleeyom/blog/issues/58),记录下去香港办卡的过程 88 | - 开始使用笔记软件 Obsidian,个人感觉挺好用的,目前在 iPadOS 写作感觉很不错 89 | - 周六和老婆第一次去看新房子,有点小激动 90 | - 老婆的小红书粉丝一个星期居然已经超过了我的推特粉丝,有点不可思议 91 | 92 | ## 2024年第7周 93 | - 今年第一次去老婆家过年,也是我第一次去外面过年,非常不一样的体验,哈哈。 94 | - 今年也是老婆第一次来我家拜年,收到了不少的红包,哈哈。 95 | - 基本上整个春节都是摆烂状态,吃喝玩乐,准备收假,开始继续锻炼。 96 | - 辞别玉兔迎金龙,新的一年,继续加油! 97 | 98 | 99 | ## 2024年第 4 周 100 | - 长沙这周下大雪了,本周跑步 3 次,累计 14 公里。 101 | - 周末老婆加班,第一次开车接送她上下班,也是我第一次独立一个人在城市里面开车,感觉良好。 102 | - 周末炖了个萝卜排骨汤。 103 | - 最近看了三部韩国电影:《回家的路》(一部 2013 年的老片,根据真人事件改编,还不错,挺感人的)、《乌有之地》、《混凝土乌托邦》(这两部都是 Netflix 出的,马东石的演技撑起了整部剧,其他的一般般)。 104 | - 《太白金星有点烦》看完了,挺不错的,相当推荐。 105 | 106 | ## 2024年第3周 107 | - 上周没咋跑步,只周末跑步了2次。 108 | - 赶在年前给车子保养了一次,了解并学习了一下事故处理流程。 109 | - 周末天气非常的冷,看天气预报说周一会下雪,所以提前把车子从树下挪到了空旷的地方。 110 | - 周末宅家和媳妇一起看老剧《甄嬛传》,你还别说,还挺好看的,哈哈哈。 111 | - 周末做了两个菜:紫苏牛蛙和香煎鲈鱼。 112 | 113 | ## 2024年第2周 114 | - 上周跑了6次,累计25公里。 115 | - 周末做了几个菜:香菜牛肉、香煎鲈鱼、香煎鸡翅,其中香煎鲈鱼非常的成功,老婆非常喜欢吃,哈哈哈。 116 | - 买了个 CarPlay 便携屏,解决原车机不带 CarPlay 的痛点,但是拿到后发现了点小问题,机器的散热板有松动,又给退回去了,让老板重新发个过来,后面拿到后再看吧。 117 | 118 | 119 | ## 2024年第1周 120 | - 开启新的一年的个人周报啦! 121 | - 本周的天气终于不咋冷了,本周跑步3次,继续加油! 122 | - 最近在看韩剧《死期将至》,和老婆一起连刷了6集,看的真过瘾啦,强烈推荐。 123 | - 小说《太白金星有点烦》马亲王的文笔是真的牛逼,硬是把西游记,写成了当今职场社会现状,牛逼。 124 | - 周末去宜家逛了一下,看下宜家的家具设计,找找装修灵感,长沙的宜家是真的大啊,两层逛完估计得一两个小时。 125 | - 周六去医院复查了下身体,问题都不大,非常开心。 126 | - 我的 iPad Mini 6 被老婆征用了,重新用回了 iPad Pro。 -------------------------------------------------------------------------------- /backup/58_2024香港银行卡办卡之旅.md: -------------------------------------------------------------------------------- 1 | # [2024香港银行卡办卡之旅](https://github.com/superleeyom/blog/issues/58) 2 | 3 | ## 前言 4 | 5 | 因为公司的业务需要,需要办理一张境外的银行卡,恰好我自己一直挺想搞一张境外银行卡,经过相关同事的推荐以及小红书调研,最终决定办理一张招商永隆银行卡(实体银行,大多数同事都是选择的这一家)和一张众安银行卡(虚拟银行),下面就这次的办卡做下简单的记录。 6 | 7 | ## 所需要的资料 8 | 9 | - **1、身份证** 10 | - **2、港澳通行证** 11 | - 保证签注有效,长沙的出入境办事处基本上都支持自助续签 12 | - **3、往返的车票** 13 | - 我是年后 2024.2.20 去的香港,长沙有直达香港西九龙的高铁,所以还算比较方便 14 | - 如果没有直达香港的高铁,可能需要在深圳北进行换乘,这个可以根据个人实际情况来 15 | - **4、人民币现金** 16 | - 招商永隆卡需要账户里面有 1w 的港币,否则每个月会收取 100 港币的管理费,由于招商永隆的 ATM 支持直接存人民币现金,强烈建议直接带人民币过去(不超过 2w,否则过关可能会查),这样可以免去从国内跨境汇款到香港银行卡的手续费,存好后,后面就可以自己直接在招商永隆的 app 上自行进行换汇 17 | - ZA Bank 没有管理费的要求,所以也就不需要带现金 18 | - **5、手机下载招商永隆 app** 19 | - 需要提前在招商永隆 app 里面填单进行预约,然后在预约成功后,会收到预约成功的短信(BK 码),3 个月之内去香港进行面核开户 20 | - ![image.png](https://raw.githubusercontent.com/superleeyom/blog/main/img/202402251436421.png) 21 | - **6、手机下载 ZA Bank app** 22 | - 这个 App 在 App Store,好像需要外区 ID (我用的美区)才能下载,国区 ID 不行 23 | - ZA Bank 的开户必须要求人在香港,由于 ZA Bank 是虚拟银行,所以全程都是线上开户,相对比较简单 24 | - **7、香港数据漫游包** 25 | - 因为要接收手机验证码,所以必须要开通数据漫游包,否则收不到验证码 26 | - 我用的中国联通的境外数据漫游包,25 块钱一天,无限流量,1g 的高速流量,超过后限速,基本上用一天绰绰有余,可以在中国联通的 app 客户端办理,去的当天办理好就行,快要到深圳的时候,手机打开漫游【数据蜂窝漫游选项】,过关的时候,就会自动切换成香港那边的运营商 27 | - ![image.png](https://raw.githubusercontent.com/superleeyom/blog/main/img/202402251444325.png) 28 | - 中国移动的也类似这样开通,这里不赘述 29 | - **8、开通微信搭车码** 30 | - 微信小城程序搜【搭车码】 31 | - 在香港做地铁的时候,需要刷二维码,提前开通搭车码,方便坐地铁 32 | - **9、充电宝** 33 | - 最好备一个充电宝,防止路上手机没电 34 | - **10、若干港币现金** 35 | - 这个我觉得可带可不带,因为大部分的商店都支持微信支付和支付宝支付 36 | - 但是有些比较老的店,只支持现金,这个看个人需要 37 | 38 | ## 交通 39 | 40 | ### 长沙南 - 香港西九龙 41 | 42 | - 当日方案: 早去晚返高铁各有一趟 43 | - 隔日方案: 长沙南 - 深圳北 - 西九龙: 可选车次更灵活, 深圳北九龙车次很多, 不过需要隔日了 44 | ### 香港西九龙 - 招商永隆中环总行 45 | 46 | - 微信搜索「搭车码」小程序,直接乘坐香港地铁,建议提前开通下 47 | - 香港西九龙站出关后,根据导航达到佐敦站「荃湾线」,然后乘坐地铁,佐敦站=》中环站 B 出口 48 | - 中环站 B 出口后,步行 100 米就到了招商永隆银行 49 | ## 港卡 50 | 51 | 香港有很多的银行,比如汇丰、渣打、中银等,我因为自己个人业务需要,最终只办理了招商永隆卡和 ZA Bank 两张卡,就这两张卡做一个简介,其他银行可以自己按需办理。 52 | ### 招商永隆卡 53 | 54 | - 招商永隆是招商银行的香港分公司,在香港也算是一家老牌银行了,如果账户不足 1w 港币的话,需要每个月收取 100 的港币的管理费 55 | - 大陆招行卡转到香港招商永隆卡,国内要收手续费(金葵花免费,金卡5折手续费25 RMB 起)、电报费(100 RMB),香港永隆招行收 50 RMB 一笔,总计:150 RMB(金葵花)/ 175 RMB(金卡) 56 | - 香港招商永隆转大陆招行,招商永隆收 80 RMB 每笔,国内免费,总计:80 RMB 57 | - 港卡与港卡之间互转免费 58 | - 不支持绑定 Apple Pay 59 | - 支持绑定 WeChat Pay(香港钱包) 60 | ### ZA Bank 61 | 62 | - 众安银行是一家虚拟银行,必须要求在港开卡,是香港第一虚拟银行 63 | - 全程线上开户,不需要任何管理费 64 | - 支持绑定 Apple Pay、WeChat Pay(香港钱包)、Google Pay 65 | - 这张卡比较适合用来支付一些海外的订阅服务 66 | - 该卡入金我试了下,跟招商永隆卡互转,不收取任何手续费,基本上是秒到账 67 | - 从内地或者境外银行转入,ZA Bank 这边跨境汇款入金不收取手续费,但是转出银行一般都是需要收费 68 | ## 其他 69 | 70 | ![image.png](https://raw.githubusercontent.com/superleeyom/blog/main/img/202402251431170.png) 71 | 72 | 以上便是我这次去香港办卡的一些总结,这次应该是我第二次去香港了,第一次去是 2018 年的时候,去香港买 iPhone X,不得不感慨香港的物价是真的贵啊,基本上是内地的两到三倍。另外香港的交通也比较复杂,根据导航走,几次都迷失在高楼大厦之间。这次在香港逗留的时间比较短,行程比较匆忙,跟几个同事一起去的,大家在办完卡之后,简单吃了个烧鹅饭(54 港币一个人),当天下午五点便匆匆搭上回长沙的高铁,结束了这次香港之旅。 73 | 74 | 75 | --- 76 | 77 | > 永隆每年要放多少才免保管费 78 | 79 | 1w港币即可,可以放里面理财,港币的活钱宝年化率还可以 @chivaszhu 80 | 81 | --- 82 | 83 | > mark 这个卡是不是可以用于支付某人工智能大模型的服务呀 84 | 85 | 应该可以,实际没有试过 86 | 87 | --- 88 | 89 | > 招商永隆卡开卡大概多久,时现场拿卡吗? 90 | 91 | @imesong 感觉1小时以内就可以了 92 | 93 | --- 94 | 95 | > 请问下柜员有没有问开户理由,怎么回答的?看小红书回答投资理财会被拒绝。 96 | 97 | 我没有问,可能是公司那边打了招呼,去了后直接就办好了,你就说想交易港股,美股入金就行了 -------------------------------------------------------------------------------- /backup/59_8款高效又好用的安卓.TV.App.推荐.md: -------------------------------------------------------------------------------- 1 | # [8款高效又好用的安卓 TV App 推荐](https://github.com/superleeyom/blog/issues/59) 2 | 3 | 4 | ![BFF9D747-5F25-4817-93D3-7130D0CB2BC5_1_105_c](https://github.com/user-attachments/assets/17214986-7acd-4fac-b436-c8418c796b91) 5 | 6 | 好久没更新了,今年下半年基本上都是在忙着新家装修,都忘记我还有个博客了都(开玩笑,哈哈),最近新家装修也总算是告一段落,电视也终于到位了,分享和总结一些我认为比较好用的安卓电视 App 吧,大部分都是免费使用,当然你要是有更好的APP推荐,欢迎评论里面分享。 7 | 8 | ## 1、OK影视 9 | 10 | 没错,这个就是鼎鼎大名的 TVBox,「OK影视」其实就是 TVBox 魔改版本,支持直播多线路、自动换源等各种功能,如果你实在看不惯「爱优腾」的吃相,那就用它,但是它只是一个空壳,你需要自己配置源,得益于 TVBox 开源,目前网上有很多的大佬维护了很多的优质的源,我目前使用的是「饭太硬」大佬的源,整体使用非常不错,这里再说一点,基于 TVBox 魔改的版本太多太多了,大家注意甄别真假; 11 | - OK影视下载地址:http://bbs.qiqiv.cn/thread-11997-1-1.html 12 | - TVBox源汇总1:https://github.com/2hacc/TVBox 13 | - TVBox源汇总2:https://github.com/qist/tvbox 14 | 15 | ## 2、TV 16 | 17 | 这个是我在有天逛 V 站的时候,看到 V 友分享的,也叫「我们的电视」,一款免费的电视直播软件,里面打包内置好了直播源,真正的开箱即用,完全免费,什么 CCTV,地方卫视,基本上都能看,在 GitHub 上可以免费下载,还有一个免费开源的叫「我的电视」,但是这个需要自己配置源,所以看个人需求下载使用; 18 | - 我们的电视:https://github.com/andandroidor/ourtv 19 | - 我的电视:https://github.com/yaoxieyoulei/mytv-android 20 | - [V2EX:现在大家安卓电视都有哪些好用的 app 推荐](https://www.v2ex.com/t/1067021#reply55) 21 | 22 | ## 3、BBLL 23 | 24 | 一个电视上免费的第三方版的 B 站,B 站官方的 TV 版体验实在是太烂了,这个就非常不错,支持弹幕,而且还能和手机上共享大会员,属于闭源产品,在 GitHub 上可以免费下载,推荐安装; 25 | - BBLL:https://github.com/xiaye13579/BBLL 26 | 27 | ## 4、YouTube 28 | 29 | 油管,这个就不用我多说了吧,因为我有 Premium 会员,所以用的官方的版本,没有广告,如果你没有会员的话,可以推荐使用 SmartTube,但是 SmartTube 我没装,所以大家自己体验吧; 30 | - SmartTube:https://github.com/yuliskov/SmartTube 31 | 32 | ## 5、野草助手 33 | 34 | 大屏应用安装专家,主要是应用分享和安装软件,可以快速安装别人分享的软件,还不错; 35 | - 野草助手:https://www.yecao.net/ 36 | 37 | ## 6、FLClash 38 | 39 | 这个就不多说了,懂得都懂,科学上网必备,GitHub 上免费开源,同类型的还有 Surfboard 和 Clash Meta for Android,其中 Surfboard 还有个 TV 版本,但是我觉得太简陋了,还是 FLClash 比较符合我的使用习惯; 40 | - FLClash:https://github.com/chen08209/FlClash 41 | - Clash Meta for Android:https://github.com/MetaCubeX/ClashMetaForAndroid 42 | - surfboard:https://github.com/getsurfboard/surfboard 43 | 44 | ## 7、Simple Live TV 45 | 46 | 「简简单单的看直播」,可以看 B 站、虎牙、斗鱼、抖音直播,TV 版本和手机版都有,GitHub 上免费开源; 47 | - Simple Live:https://github.com/xiaoyaocz/dart_simple_live 48 | 49 | ## 8、当贝市场 50 | 51 | 一款老牌的应用商店,管理、下载、推送安装 App 的,还不错,官网可以根据自己的机型下载; 52 | - 当贝市场:https://www.dangbei.com/ 53 | 54 | ## 安装小贴士 55 | 56 | 这里补充一点,安卓电视在安装 App 的时候,要注意下版本,有 32 位和 64 位之分,如果你的系统芯片支持 64 位架构的话,一般下载带 v8a 后缀,否则就下载 v7a 的(32 位架构),比如 app-arm64-v8a-release.apk 和 app-armeabi-v7a-release.apk,当然有些电视安卓版本更低的,可能就又是另外一种情况了,总之按需安装,App 不在多,适合自己的才是最好的! 57 | 58 | --- 59 | 60 | > 前来感谢,真是好文,下了好几个 61 | 62 | @sorrow233 不客气 63 | 64 | --- 65 | 66 | @kio90 这你要是装了那个当贝市场,里面应该有 67 | 68 | --- 69 | 70 | @SysDQ 这个还真没研究过,当贝市场搜索下 -------------------------------------------------------------------------------- /backup/5_nginx负载均衡原理之ip_hash哈希算法探究.md: -------------------------------------------------------------------------------- 1 | # [nginx负载均衡原理之ip_hash哈希算法探究](https://github.com/superleeyom/blog/issues/5) 2 | 3 | 关于 `ip_hash` `nginx` 官网是这样定义的: 4 | 5 | > Specifies that a group should use a load balancing method where requests are distributed between servers based on client IP addresses. The first three octets of the client IPv4 address, or the entire IPv6 address, are used as a hashing key. The method ensures that requests from the same client will always be passed to the same server except when this server is unavailable. In the latter case client requests will be passed to another server. Most probably, it will always be the same server as well. 6 | 7 | 翻译过来就是: 8 | 9 | > 指定组应使用负载平衡方法,其中根据客户端IP地址在服务器之间分配请求。 客户端IPv4地址的前三个八位位组或整个IPv6地址用作哈希密钥。 该方法确保了来自同一客户端的请求将始终传递到同一服务器,除非该服务器不可用。 在后一种情况下,客户端请求将传递到另一台服务器。 最有可能的是,它也将永远是同一台服务器。 10 | 11 | 假设目前有三台服务器,采用 nginx 的`ip_hash`负载均衡策略,假设现在有三台 Tomcat 服务器,其对应的节点 `index` 分别是:0、1、2,此时有四个客户端访问 nginx,那根据哈希算法:`hash(ip)%node_counts = index`,最终得到的各个客户端的请求会落到如下的机器上(假设四个客户端ip的哈希值分别为:5、6、7、8): 12 | 13 | - `ip`:指的是客户端的 ip 地址的前三个八位位组,打个比方:`138.23.324.13`,那就是取`138.23.324`,所以如果同一个局域网内访问 nginx 的时候,如果机器的前三个八位一致,比如这两个ip:`138.23.324.13`、`138.23.324.14`的请求最终只会落在同一台的节点上 14 | - `node_counts`:节点数量 15 | - `index`:节点的标识 16 | 17 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/hash.jpg) 18 | 19 | 但是使用哈希算法会存在一些问题,比如要删除一个节点3,这时候用户的请求落地情况会变成这样: 20 | 21 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/hash2.jpg) 22 | 23 | 原本用户4是访问的节点3,这时候就会转为访问节点1,这时候在之前节点3上的用户会话就会丢失,增加一个节点也是同理,同样会存在用户会话丢失的情况。如何正确让一台服务器下线?那在 nginx 官网的文档中写道:如果需要临时删除其中一个服务器,则应该使用 down 参数标记它,以便保存当前客户机 IP 地址的散列。 24 | 25 | > If one of the servers needs to be temporarily removed, it should be marked with the `down` parameter in order to preserve the current hashing of client IP addresses. 26 | 27 | 示例: 28 | 29 | ```nginx 30 | upstream backend { 31 | ip_hash; 32 | 33 | server backend1.example.com; 34 | server backend2.example.com; 35 | server backend3.example.com down; 36 | server backend4.example.com; 37 | } 38 | ``` 39 | 40 | 如果采用一致性哈希算法,出现以上问题的概率就会低很多。 41 | 42 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/iShot2020-11-05%E4%B8%8B%E5%8D%8812.22.28.png) 43 | 44 | 如上图,圆环上有 `0-2^32-1` 个节点,每个节点,按顺时针方向排列递增,用户的请求 ip,通过哈希算法,会落在圆环上的某个节点上。按顺时针方向,用户1、用户2会访问节点1,用户3、用户4会访问节点2,用户5、用户6会访问节点3,用户7访问到节点4。假如此时删除了节点3,那么原本的用户5和用户6的请求,则会落到节点4上,其他用户的访问均不会受到影响,只会有用户5和用户6的会话信息会丢失,不会造成全局的变动。 45 | 46 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/iShot2020-11-05%E4%B8%8B%E5%8D%8802.05.03.png) 47 | 48 | 另外还有一个优点,如果发现节点1访问量很大,负载高于其他节点,这就说明节点1存储的数据是热点数据。这时候,为了减少节点1的负载,我们可以在热点数据位置再加入一个node,用来分担热点数据的压力。 -------------------------------------------------------------------------------- /backup/60_2024.装修开支清单.md: -------------------------------------------------------------------------------- 1 | # [2024 装修开支清单](https://github.com/superleeyom/blog/issues/60) 2 | 3 | 今年下半年基本上都在忙于装修,截止到今天,总算是全部搞完了,每周的日常就是白天上班,晚上下班去工地巡查进度,周末跑各种建材市场比价询价。 4 | 5 | 因为要跟各个工种和商家对接,稍不盯着点,很容易某个环节出问题,以至于到最后,我都跟自己和解了,心里默念「问题不大,又不是不能住」来安慰自己。 6 | 7 | 我自己本身没有任何的装修经验,基本上是摸着石头过河,有啥不懂的就小红书上疯狂恶补和向领居请教。因为预算有限,没有请设计师,也没有找那种专门装修的公司 ,这样就可以省下设计费和10%的项目管理费。 8 | 9 | 我们最后直接以半包的形式承包给了一个包工头师傅,整个装修过程就是我和我老婆两个人,现场跟包工头和各个工种师傅对接,跟他们说明哪里要怎么搞,有些拿不定主意的,就让师傅按照他们的经验来搞,因为都是在工地摸爬滚打多年的老师傅,在没有图纸的情况下,基本上能理解我们的要求,也没出啥太大问题。 10 | 11 | 最后,118户型,套内96,整体落地差不多23w吧,以下是我自己汇总的整个装修支出: 12 | 13 | # 💰总开支(229361.88) 14 | 15 | ## 1、硬装阶段(总计162449.60) 16 | 17 | - [x] 半包第四笔尾款💰3700(未付) 18 | - [x] ✅全屋定制💰31000(还有尾款1550未付) 19 | - [x] ✅橱柜 +大理石入户门套 💰5980 20 | - [x] ✅美缝💰3000 21 | - [x] ✅阳台封窗💰17921 22 | - [x] ✅开工仪式💰450 23 | - [x] ✅2024年6月16日半包第一笔款💰22200 24 | - [x] ✅2024年7月14日半包第二笔款💰22200 25 | - [x] ✅2024年8月11半包第三笔款💰25900 26 | - [x] ✅装修期间水电费💰281 27 | - [x] ✅垃圾清运💰354 28 | - [x] ✅美的4P乐享三代风管机💰7400 29 | - [x] ✅美的前置过滤器💰454 30 | - [x] ✅九牧蹲便器💰220 31 | - [x] ✅瓷砖💰8248 32 | - [x] ✅公牛地插 2 个 💰264 33 | - [x] ✅德高瓷砖胶+背胶💰1460 34 | - [x] ✅全屋门💰8600 35 | - [x] ✅公牛轨道插座 1个💰292 36 | - [x] ✅1 个顶装双排抽拉裤架 💰168 37 | - [x] ✅瓦克 DA 15 倍防霉玻璃胶 1 支 💰83 38 | - [x] ✅厨房304不锈钢水槽 💰498 39 | - [x] ✅厨房烟道止逆阀💰31.6 40 | - [x] ✅厨卫铝扣板吊顶+石膏线💰1745 41 | 42 | ## 2、软装阶段(总计66912.28) 43 | 44 | ### 2.1、家电类(总计:35544.00) 45 | 46 | - [x] ✅小米路由器BE6500 + 2.5g交换机 💰449 47 | - [x] ✅移动宽带1000M 3年 💰852 48 | - [x] ✅扫地机器人:追觅 s30 pro ultra 超薄增强版💰3410 49 | - [x] ✅小爱音箱 pro 💰194 50 | - [x] ✅智能摄像头:小米智能摄像头3 Pro + 64g存储卡💰206 51 | - [x] ✅净水器:米家净水器1200G Pro💰1219 52 | - [x] ✅管线机: 奥克斯管线机 💰439 53 | - [x] ✅热水器:美的mate 16L +安装💰1656 54 | - [x] ✅空调挂机*3:美的酷省电pro*3 + 安装💰7130 55 | - [x] ✅智能马桶:恒洁 E3 轻智能马桶 💰1596 56 | - [x] ✅浴霸*2:奥普S01MD 💰1108 57 | - [x] ✅洗烘套装:美的清风明月 Pro 💰3837 58 | - [x] ✅消毒柜:美的 110Q21Pro 110L 二星级 💰1036 59 | - [x] ✅冰箱:美的 M60 大白豚 535 十字 💰3736 60 | - [x] ✅电视:雷鸟鹤6 pro 85寸+安装+挂架💰4902 61 | - [x] ✅电动晾衣架:邦先生智能晾衣机 M25A 💰974 62 | - [x] ✅吸油烟机+灶:美的AK5 Pro💰2800 63 | 64 | ### 2.2、家具类(总计:31368.28) 65 | 66 | - [x] ✅沙发:全友情怀真皮沙发2.92m💰1960(2件97折未登记) 67 | - [x] ✅开荒保洁:💰800 68 | - [x] ✅插座防水罩3个(公卫+公卫)💰10 69 | - [x] ✅空调伴侣2个(主卧+次卧)💰67.71 70 | - [x] ✅路由器收纳盒(主卧)💰33.6 71 | - [x] ✅米家LED灯泡1个(主卧)💰21.4 72 | - [x] ✅水浸传感器2个(厨房+阳台) 💰77 73 | - [x] ✅主卧梳妆台椅子 💰275 74 | - [x] ✅橡木浴室柜 * 2 💰2500 75 | - [x] ✅飘窗垫*2:💰308 76 | - [x] ✅铝合金踢脚线 💰1041 77 | - [x] ✅窗帘*4 💰 2585 78 | - [x] ✅欧普全光谱武大联合版4室2厅 💰1560 79 | - [x] ✅雷士照明筒灯10个 💰272 80 | - [x] ✅小沐厨卫集成吊顶超薄led灯 * 3 💰82 81 | - [x] ✅床头柜*4💰776 82 | - [x] ✅次卧床垫*2:芝华仕d026💰2162 83 | - [x] ✅主卧床垫:喜临门白骑士满满Pro💰 1746(迷瞪返现340还未到账) 84 | - [x] ✅源氏木语实木床*3💰4678 85 | - [x] ✅林氏家具1.6m 实木餐桌+4餐椅💰3413 86 | - [x] ✅淘宝书房多层实木3米矮柜💰1740 87 | - [x] ✅淘宝3米多层实木电视柜 1520 88 | - [x] ✅瓦克防霉胶1支💰41.88 89 | - [x] ✅开关插座:正泰开关插座 💰740 90 | - [x] ✅花洒:心海伽蓝 s7*2💰 1313 91 | - [x] ✅茶几:全友茶几1套💰700(2件97折未登记) 92 | - [x] ✅角阀:伟星角阀10个 + 伟星洗衣机水龙头1个 💰272 93 | - [x] ✅水龙头*2 💰349 94 | - [x] ✅东方雨虹结构胶2瓶 💰49 95 | - [x] ✅三棵树发泡胶1瓶💰15.69 96 | - [x] ✅消毒柜隔热板💰50 97 | - [x] ✅踢脚线美容胶💰 39 98 | - [x] ✅电钻+披头+网线钳+螺丝刀 💰121 99 | - [x] ✅扫把+簸箕 + 拖把 💰 50 100 | 101 | 102 | --- 103 | 104 | @chuyao @CharlesCCC @queyiqin @AustinLuke00 谢谢❤️ -------------------------------------------------------------------------------- /backup/61_我的.Apple.设备.md: -------------------------------------------------------------------------------- 1 | # [我的 Apple 设备](https://github.com/superleeyom/blog/issues/61) 2 | 3 | 4 | ![3371735201989_ pic_hd](https://github.com/user-attachments/assets/61d0e5a9-f908-474c-b7be-68ab0f974fbf) 5 | 6 | 7 | 今天给手上的Apple设备拍了个全家福,都是陪伴了我多年的老搭档了。 8 | 9 | 1、M1 MacBook Pro👨‍💻:这个是 21 年公司入职的时候发的,16g的运存,256g 的存储,主要用于日常开发,虽然现在M系列已经到M4了,但是3年多的时间过去了,M1的性能,放到现在,依然够用,日常编码开发,效率杠杠的,目前唯一的不足就是硬盘太小了。 10 | 11 | 2、iPad Pro 2021💻:21 年购入,不记得哪个平台买的了,现在主要是给老婆每天追剧用的,我使用的频率较低,真的是应了那句话:买前生产力,买后爱奇艺,不过 iPad Pro 这性能,M1再配上高刷,感觉再用 10 年都没有问题。 12 | 13 | 3、AirPods Pro 1代🎧:这个耳机是我在19年的时候,在多多上买的,也用了好几年了,目前除了续航有点下降外,其他功能均正常使用。每天通勤路上打开降噪模式,地铁上用来听播客,听音乐,再合适不过了。 14 | 15 | 4、iPhone 13📱:21年11月份的时候,在长沙的Apple直营店买的,当时还是用的 iPhone X 进行了抵扣,如今3年多时间过去了,系统升级到最新的 iOS 18 后,受限于只有 4g 的运存,系统杀后台严重,整体上使用比较卡顿。因为我属于裸奔党,目前也是伤痕累累,电池续航也崩了,前段时间花了150 换了块第三方电池,勉强还能凑合用,希望能撑到明年换 iPhone 17系列。 16 | 17 | 5、iPad mini 6:这个22年在闲鱼上淘的,64g的存储,目前我的使用频率蛮高的,主要是地铁上通勤看书用,这两年拿它也看了不少书,另外一个场景就是睡前躺床上拿着它,刷b站和油管,因为iPad mini 重量轻,拿在手上不累,所以跟iPad Pro相比,我更喜欢用 mini 系列。 18 | 19 | 6、Apple Watch Ultra 1代:这个是我老婆去年给我的30岁生日礼物,我之前带的是 S4,换到 ultra 系列后,最直观的感受就是屏幕变大,续航变长,目前每天用来记录日常运动,地铁通勤过闸机,超级喜欢。 20 | 21 | 7、妙控板 Type C 版:这个今年刚买的,之前就一直在等 Type C 版本的妙控板,今年终于更新了,我第一时间就去直营店购入了,因为是放在公司使用,就没有放到合照里面了,在习惯使用触控板以后,就再也回不去了。 22 | 23 | 以上便是我日常使用的一些Apple 设备,这些设备虽然都不是最新款,但是对我来说,实用够用就行,Apple 产品价格虽然贵,但是有句话叫做:长期主义,买贵点,用久点。因为明年打算搬新家,所以明年,我计划购入1 台最新款的 Mac mini ,作为家用电脑,另外还会更换 iPhone 17 Pro 作为我的主力设备,到时候再给大家分享。 -------------------------------------------------------------------------------- /backup/62_我的老婆.md: -------------------------------------------------------------------------------- 1 | # [我的老婆](https://github.com/superleeyom/blog/issues/62) 2 | 3 | ![3411735889399_ pic_hd](https://github.com/user-attachments/assets/25d07863-417c-4f0c-bd2a-30cf4cde48d9) 4 | 5 | 2025 年 1 月 1 日,这一天对我来说,是人生中最难忘的一天。我终于娶到了我最心爱的女孩,在亲朋好友的见证下,我们完成了属于我们的婚礼。此刻,趁着有时间,记录下此刻的心境。 6 | 7 | 回想起初见她,像是一场命中注定的相遇。2022 年 3 月中旬,我帮同事搬家时,同事老婆提到要给我介绍一个妹子。我当时以为是玩笑话,就随口答应了。没想到,命运真的就这样安排了我们相遇。 8 | 9 | 2022 年 4 月 9 日,同事请我吃饭感谢搬家帮忙,她也刚好在那里。那天,她穿着一件玫瑰色上衣和牛仔长裙,头发还不长,笑容甜美,像极了电视剧中的邻家女孩。我记得很清楚,饭后我们一起打牌,建了一个微信群。看她也在群里,我鼓起勇气偷偷加了她的微信。那一刻,命运的齿轮悄然转动,真是应了那句话,「命中注定的人,早晚会相遇」,我们最终在 2022 年 7 月 12 日 这天正式确定了关系。 10 | 11 | 我们的感情在点滴中慢慢深厚起来。有一次我住院做穿刺手术,手术后身体无法动弹,那几天她跑上跑下地照顾我。医院只有一个床位,她没有地方睡,我让她挤在我身旁一起休息。半夜醒来,看到她疲惫地蜷缩在床的一角,心里就很难受。从那一刻起,我暗暗发誓,今后一定要好好爱她,照顾她,让她少些辛苦,多些幸福。 12 | 13 | 她看起来小小只,但是身体里面却藏着大能量。无论多大的困难,她总能化解得轻轻松松。2024 年,我们一起把一套毛坯房装修成了理想中的家。从设计到选材,从谈价格到监督工地,她始终陪着我,顶着酷暑一起跑市场。这一路充满汗水,也满载着我们共同努力的成就感。 14 | 15 | 她是我的力量源泉,却也有脆弱的时候。每当她落泪,我总是手足无措,言语笨拙,只能默默陪在她身旁。她还笑着对我说,哭的时候,到最后都是她自己在安慰自己。这话让我心疼又内疚,也让我更加坚定,要用行动去保护她、爱护她,让她少流眼泪,多些笑容。 16 | 17 | 这一路走来,我们携手并肩,经历了很多事情。能够遇到她,我深感幸福。未来的日子里,我期待与她一起走过更多的春夏秋冬,共同书写属于我们的幸福篇章。 -------------------------------------------------------------------------------- /backup/63_iPad.Pro秒变Mac.mini显示器教程.md: -------------------------------------------------------------------------------- 1 | # [iPad Pro秒变Mac mini显示器教程](https://github.com/superleeyom/blog/issues/63) 2 | 3 | ![Image](https://github.com/user-attachments/assets/5804a97a-b671-4937-8355-62175654c62c) 4 | 5 | 最近购入了一台 Mac mini,因为我现在还没搬家,暂未购入显示器,恰好手上有一台 iPad Pro,所以就突发奇想,是否可以临时将 iPad 的作为 Mac mini的显示器。经过一番调研,确实可行,所以特意整理了下相关的操作。 6 | 7 | 💡PS:所有的操作必须先如下的几个大前提下才行: 8 | 1、Mac mini已激活,首次开箱没有显示器,那肯定是不行的,所以你得找个显示器先把Mac mini激活 9 | 2、满足随航开启的要求,iPad和Mac mini在同一个WiFi下,并且都打开蓝牙,两个设备登录的是相同的iCloud账户 10 | 11 | ## 1、软硬件准备 12 | 因为将iPad作为Mac mini的显示器,本质上是利用 macOS的随航功能,但是每次开机启用随航的前提是你得有一个显示器,通常解决这个问题有两种方案: 13 | - 虚拟显示器:需要安装 BetterDisplay 这个软件,给Mac mini创建一个虚拟显示器,然后设置BetterDisplay开机自启动,这个属于软件方案,我没有实践,这里不多赘述,我采用第二种硬件方案 14 | - HDMI欺骗器:pdd上,5~6块钱一个HDMI欺骗器,将它插在Mac mini的HDMI口上,可以欺骗macOS,告诉它我有外接显示器,这样就可以顺利开启随航。 15 | 16 | ## 2、开机免登录 17 | 在设置-用户与群组-自动以此身份登录,选择你创建的账户,这样每次开机就可以免密登录进入macOS,不然你每次都得盲输开机密码。 18 | 19 | ## 3、随航快捷键 20 | 在macOS的设置-键盘-键盘快捷键-App快捷键,然后点击+号,菜单栏标题叫:移到“xxx的iPad”(注意这个是中文的引号),然后设置一个快捷键,比如我设置的是:control+shift+o,这个「xxx的iPad」,是你的iPad的名字,在iPadOS的设置-通用-关于本机-名称 里面可以查看和修改。 21 | 22 | ## 4、访达快捷键 23 | 因为3的随航快捷键生效的前提,是必须打开访达的基础上,所以假设你123都做完了,按下开机键,还要再执行一个快捷键:option+command+空格,这个是个全局的快捷键会唤醒访达的全局搜索,然后再执行3的快捷键,不出意外,Mac mini 的镜像就会随航到 iPad上了。 24 | 25 | 假设你1234都明白了,所以整套流程下来,你每次开机,只需要执行两个快捷键,先执行 option+command+空格,然后执行 control+shift+o,就能将iPad作为Mac mini的显示器了。 26 | -------------------------------------------------------------------------------- /backup/6_Hackintosh黑苹果折腾之旅.md: -------------------------------------------------------------------------------- 1 | # [Hackintosh黑苹果折腾之旅](https://github.com/superleeyom/blog/issues/6) 2 | 3 | ![](https://raw.githubusercontent.com/superleeyom/blog/main/img/iShot20201115.png) 4 | 5 | 6 | ## 硬件配置 7 | 其实我这台黑苹果,今年年初三月份的时候就装好了,周末趁着有空,把系统升级到了 `macOS Big Sur` ,在此总结下自己的整个的安装的一些心得。 8 | 9 | 我这台黑苹果主机的整体配置清单如下: 10 | 11 | - `cpu`:intel i5 9400 散片 淘宝 1300元 12 | - `主板`:华擎B365M-ITX/AC 淘宝 659元 13 | - `显卡`:盈通 rx580 4g 2304sp 满血版 508元 14 | - `硬盘`:海康威视 C2000 pro 256g SSD 京东 335 元 + 「自用剩余的闪迪」 SATA3 256g SSD 15 | - `内存条`:宇瞻 DDR4 2666 16g*2 天猫 978元 16 | - `散热器`:ID-COOLING IS-40X 淘宝 89元 17 | - `显卡延长线`:傻瓜超人 ADT01 淘宝 99元(方便后期加显卡) 18 | - `电源`:海盗船 sf450 金牌 + 定制线 淘宝 728元 19 | - `机箱`:傻瓜超人 K55 + 定制铝侧板两块 淘宝 405 元 20 | - `无线网卡`:BCM94360CS2 无线网卡+转接卡 淘宝 160元 21 | - `总计`:5261 元 22 | 23 | 由于我是第一次装机,一来就装 `ITX` 主机,各种懵逼,好在说明书也挺全的,从下午一直装到晚上凌晨三四点,才把机器点亮。 24 | 25 | ## 组装初衷 26 | 27 | 那为啥要组装一台黑苹果呢?而不直接买白的呢?这个嘛,迫于入了苹果生态的坑,加上老笔记本 MacBook Pro (Retina, 13-inch, Early 2015) 性能已经逐渐跟不上了,所以就萌生了组台 itx 黑苹果主机,那说到底,其实就是「穷」。 28 | 29 | 黑苹果有优点也有缺点,优点是: 30 | - **省钱呀**,性价比高,同样的配置,可能只要白苹果的1/3的价格 31 | - 可扩展性强,可以自己随意diy硬件 32 | 33 | 缺点: 34 | - 如果硬件选的不好,比较折腾,没有合适的EFI的话,需要自己去摸索 35 | - 版本升级麻烦,每次大版本的升级的话,都需要重装 36 | - 需要一定软硬件基础 37 | 38 | 装黑苹果,要想省事的话,去论坛,比如国内的:[远景论坛](http://bbs.pcbeta.com/)、[黑果小兵](https://blog.daliansky.net/),国外的:[tonymacx86](https://www.tonymacx86.com/),按照别人已经有装成功过的硬件,并且对应的EFI也都有,那就对着采购一套相同的配件,尽可能选择免驱的硬件,基本上不会出大的问题,比如我的这个网卡BCM94360CS2 就是原笔记本的拆机原件。当然,你前提得懂一些基本的硬件和软件知识,否则还是花点钱,直接远程交给某宝来装。 39 | 40 | 由于我自己装的这套配置,已经有成功的 [(B365ITX-Hackintosh-OC)](https://github.com/Good0007/B365ITX-Hackintosh-OC) 例子,只要按照原作者给的提示,把对应的该删的删了,整体的安装过程,基本上很顺畅。整体的安装流程,参考的是黑果大神「[黑果小兵](https://blog.daliansky.net/)」的教程「[天逸510s Mini兼macOS BigSur安装教程](https://blog.daliansky.net/Lenovo-Tianyi-510s-Mini-and-macOS-BigSur-Installation-Tutorial.html)」,镜像也是用的黑果小兵大神封装的「[macOS BigSur 11.0.1 20B29 正式版](https://blog.daliansky.net/macOS-BigSur-11.0.1-20B29-Release-version-with-Clover-5126-original-image-Double-EFI-Version-UEFI-and-MBR.html)」。 41 | 42 | ## 遇到的问题 43 | 44 | 目前主流的两到黑苹果引导方式:OpenCore(简称OC)和 Clover(简称四叶草),目前 Clover 逐渐被淘汰了,很多的驱动 Kext 都放弃适配 Clover,大家目前都开始使用 OC 做为黑苹果的首选引导方式。 45 | 46 | 在安装过程中没遇到大的问题,就是通过「时间机器」恢复备份的时候,遇到一个比较坑爹的事情,我顺利的进入系统后,我打开系统自带的「迁移助理」,想恢复原有的备份,但是每次恢复到一半的时候,老是自动关机,重试几次,都是一样,很让人崩溃。最后,我不得不再次抹盘重装,在刚刚进入系统初始化的时候,系统提示是否要导入已有的备份,我从这里开始进行恢复,不等彻底登录进入系统后,通过「迁移助理」进行恢复,嚯,好家伙,顺顺利利的恢复成功了,你说这奇怪不奇怪。 47 | 48 | 再个就是修改引导工具 OpenCore 的相关的配置的时候,不生效的问题。比如原作者的 EFI,启动的时候开启了啰嗦模式(`-v`),系统启动的时候,总会打印一大串的debug的代码,所以为了隐藏这个,得把 EFI 里 `config.plist` 的 `boot-args` 属性里的 `-v` 去掉即可,但是发现去掉后,重启依旧不生效,后面通过找资料,最后发现,需要用工具 `Hackinttools`,清除 `NVRAM `里这行配置的缓存(选中删除即可),否则不会生效。 49 | 50 | 另外启动的时候,不想显示引导菜单的话,将 `showpicker` 改成 `false`,其他的话,目前没有遇到啥问题,由于大部分原作者已经调试好了,基本上就不需要调整了,感谢! 51 | 52 | ## 完成情况 53 | 54 | 目前这台黑苹果的整体的完整度在99%吧,唯一的缺点就是,睡眠的话,需要手动去点击系统左上角的「睡眠」选项,有时候系统无法自动进入睡眠。我自己也使用了大半年了,整体上和苹果的台式机 iMac、Mac Pro 基本上无任何的差别,也能配合 iPad、iPhone、Apple Watch 进行隔空投送、接力、随航、解锁 Mac等等一系列的功能。在会自己折腾的情况下,还是挺香的。所以如果你有差不多相同的配置,可以试试我这个 EFI。 55 | 56 | ## 参考资料 57 | 58 | - [B365ITX-Hackintosh-OC 华擎B365ITX 黑苹果OC 配置](https://github.com/Good0007/B365ITX-Hackintosh-OC) 59 | - [天逸510s Mini兼macOS BigSur安装教程](https://blog.daliansky.net/Lenovo-Tianyi-510s-Mini-and-macOS-BigSur-Installation-Tutorial.html) 60 | - [【黑果小兵】macOS BigSur 11.0.1 20B29 正式版 with Clover 5126原版镜像[双EFI版][UEFI and MBR] 61 | ](https://blog.daliansky.net/macOS-BigSur-11.0.1-20B29-Release-version-with-Clover-5126-original-image-Double-EFI-Version-UEFI-and-MBR.html) 62 | - [华擎 Asrock B365M-ITX/ac macOS Catalina 完美黑苹果 OC/Clover双版本](https://www.chenweikang.top/?p=846) 63 | - [黑苹果安装教程](https://zhih.me/hackintosh-install-guide/) 64 | - [精解OpenCore](https://blog.daliansky.net/OpenCore-BootLoader.html) 65 | - [使用 OpenCore 引导黑苹果](https://blog.xjn819.com/post/opencore-guide.html) 66 | 67 | ## 资源附件 68 | 69 | - 我目前在用的这台机器的 [EFI](https://github.com/superleeyom/B365ITX-Hackintosh-OpenCore) 70 | - 原作者的 [EFI](https://github.com/Good0007/B365ITX-Hackintosh-OC) 71 | - [opencore-configurator](https://mackie100projects.altervista.org/download-opencore-configurator/) 72 | - [Hackintool](https://github.com/headkaze/Hackintool/releases) 73 | -------------------------------------------------------------------------------- /backup/7_[笔记]睡眠革命.md: -------------------------------------------------------------------------------- 1 | # [[笔记]睡眠革命](https://github.com/superleeyom/blog/issues/7) 2 | 3 | ## 睡眠革命:如何让你的睡眠更高效 4 | 5 | > 作者:尼克·利特尔黑尔斯;数量:13个笔记;时间:2020-11-22 11:18:11 6 | 7 | - **[02] 走慢与走快——睡眠类型:** 8 | - 把咖啡因当成高效的表现增强剂使用,而不是出于习惯去喝咖啡,并且一天的咖啡因摄入量不要超过400毫克。 9 | - **[03] 90分钟睡眠法——睡眠周期:** 10 | - R90”指的是以90分钟为一个周期,获得身体修复。“90”这个数字,并不是我从1—100中随意选择的。从临床上说,90分钟是一个人经历各个睡眠阶段所需的时间。这些睡眠阶段组成了一个睡眠周期。 11 | - 事实上,如果想提高睡眠修复的质量,那么设置固定时间的闹铃,正是我们能采取的最有效的方法。 12 | - **[04] 热身与舒缓——睡眠前后的例行程序:** 13 | - 在睡觉前提前关闭电脑、平板电脑、智能手机和电视机,能减少你暴露在这些设备发出的蓝光下的时间。 14 | - **[05] 暂停片刻,该休息了!——日间小睡:** 15 | - 即使你并没有真正进入睡眠状态也没有关系。重要的是,你能利用这段时间闭上眼睛、脱离这个世界片刻。能够睡着固然很棒,但徘徊在似睡非睡、似醒非醒的蒙眬、迷糊的状态中,同样也很迷人。这就像一个美好的白日梦,你并没有认真在想什么,你的大脑处于一片混沌中。 16 | - **[06] 改造你的床铺——寝具套装:** 17 | - 侧卧是我唯一推荐的睡姿,但也许你现在侧卧的方向并不正确。我所训练的那些运动员睡觉时,会采用胎儿的姿势、躺向相对不太重要的身体一侧。因为这是使用较少也较不敏感的一侧,换句话说,如果你习惯使用右手,你该向左侧睡,反之亦然。 18 | - **[09] 与敌同眠——各种睡眠问题:** 19 | - 多年以来,我们一直盲目地相信应当每晚睡足8小时,因此,“每晚只睡4个半小时就够了”会让大脑一时难以接受。但这样的安排反而更有益:比较一下,3个无缝衔接的睡眠周期——至少其中的有效睡眠占了不少的比例(还记得吗,如果没有获得充足睡眠,你的大脑会优先进入快速眼动睡眠阶段),和一段类似时长的睡眠——但断断续续地分布在8小时、且大多是浅睡眠,哪一种睡眠对你更有益呢? 20 | - 根据R90方案,我们把7天作为一个周期来观测睡眠状况,而不再着眼于一个晚上的睡眠状况。所以,如果在7天之后,那些睡眠问题仍然存在,可以再减少一个睡眠周期,让她在凌晨2点上床睡觉。这似乎难以置信,但你必须认识到,这并不是一个长期措施。这样做是为了有效地重置你的睡眠模式,并找到你的极限——你能保持多长时间的高效睡眠,然后逐步增加睡眠时间。 21 | - 在睡眠不佳时,不要总想着这一晚的睡眠是多么重要。我之所以提倡观察一周总共获得的睡眠周期并且推介24×7的修复计划,原因就在于此。把一晚的睡眠看得太重,是有欠公允的。 22 | - 失眠是过度清醒造成的。在过度清醒状态下,人脑会因过于兴奋而难以入睡。 23 | - 如果你睡不着,试着用别的方式休息一下。比如试着冥想,或者重温一下你运动生涯中最辉煌的一刻。你可以利用这些时间做些别的事情。” 24 | - 让他们重温自己的辉煌时刻,能帮助他们降低焦虑水平——焦虑有可能是他们失眠的原因。 25 | - 夜晚,人体会自然分泌褪黑素进入睡眠状态。如果你在夜间工作,就错过了睡眠冲动和睡眠需求同时达到高峰的睡眠时机,等早上回到家中时,太阳已经升起,你的睡眠压力非常之大,但睡眠冲动开始下降,你很难获得像夜间睡眠一样的高质量睡眠。 26 | - **[10] 主队——性、伴侣和现代家庭:** 27 | - 和谐的性爱能以一种愉快的方式有效地降低压力、焦虑和担忧,具有惊人的功效。它能让大脑聚焦于令人兴奋的自发行为,让人沉浸在那一刻中。它能让我们感到自己有人爱、有人需要并带来安全感。它是一种自然而然的锻炼方式——越频繁越好——而且性爱过后能让我们感到温馨、放松、幸福。此外,它似乎能让人快速进入梦乡,特别是对男性来说。 -------------------------------------------------------------------------------- /backup/9_5分钟快速理解Redis的持久化.md: -------------------------------------------------------------------------------- 1 | # [5分钟快速理解Redis的持久化](https://github.com/superleeyom/blog/issues/9) 2 | 3 | Redis 的持久化指的是把内存中存储的数据以文件形式存储到硬盘上,而服务器也可以根据这些文件在系统停机之后实施数据恢复,让服务器的数据库重新回到停机之前的状态。为了满足不同的持久化需求,Redis 提供了`RDB持久化`、`AOF持久化`和`RDB-AOF混合持久化`等多种持久化方式以供用户选择。如果用户有需要,也可以完全关闭持久化功能,让服务器处于`无持久化状态`。 4 | 5 | ## RDB持久化 6 | 7 | `RDB` 的全称是 `Redis DataBase`,`RDB持久化`是 Redis 默认使用的持久化功能,通过创建以`.rdb`后缀结尾的二进制文件,该文件包含了服务器在各个数据库中存储的键值对数据等信息。Redis 提供了三种创建 RDB 文件的方法,分别是:手动执行`SAVE命令`、手动执行`BGSAVE命令`、通过配置选项自动创建等三种方式。 8 | 9 | 那 RDB 文件的结构是咋样的呢?它由如下几部分组成: 10 | 11 | | 结构 | 解释 | 12 | | :------------: | :----------------------------------------------------------: | 13 | | RDB 文件标识符 | 文件最开头的部分为 RDB 文件标识符,这个标识符的内容为"REDIS"这5个字符。Redis 服务器在尝试载入 RDB 文件的时候,可以通过这个标识符快速地判断该文件是否为真正的 RDB 文件。 | 14 | | 版本号 | 版本号是一个字符串格式的数字,长度为4个字符。新版 Redis 服务器总是能够**向下兼容**旧版 Redis 服务器生成的 RDB 文件。比如,生成第9版 RDB 文件的 Redis 5.0 既能够正常读入由 Redis 4.0 生成的第8版 RDB 文件。 | 15 | | 设备附加信息 | 记录了生成 RDB 文件的 Redis 服务器及其所在平台的信息,比如服务器的版本号、宿主机器的架构、创建 RDB 文件时的时间戳、服务器占用的内存数量等。 | 16 | | 数据库数据 | 记录了 Redis 服务器存储的0个或任意多个数据库的数据,各个数据库的数据将按照数据库号码从小到大进行排列,每个数据库里面存放的是键值对数据。 | 17 | | Lua 脚本缓存 | 如果 Redis 服务器启用了复制功能,那么服务器将在 RDB 文件的 Lua 脚本缓存部分保存所有已被缓存的 Lua 脚本。这样一来,从服务器在载入 RDB 文件完成数据同步之后,就可以继续执行主服务器发来的 EVALSHA 命令了。 | 18 | | EOF | 用于标识 RDB 正文内容的末尾,它的实际值为二进制值 0xFF。 | 19 | | CRC64校验和 | RDB文件的末尾是一个以无符号64位整数表示的 CRC64 校验和,用于校验 RDB 文件是否有出错或损坏的情况。 | 20 | 21 | Redis 服务器载入 RDB 文件的整体流程:**打开 RDB 文件 --> 检查文件头 --> 检查版本号 --> 读取设备信息 --> 重建数据库 --> 重建脚本缓存 --> 对比校验和 --> 数据载入完毕。** 22 | 23 | ### SAVE命令 24 | 25 | 可以通过`SAVE`命令,以**同步方式**创建出一个记录了服务器当前所有数据库数据的 RDB 文件。`SVAE`命令是一个无参数命令,创建成功后,返回 OK 提示: 26 | 27 | ``` 28 | redis> SAVE 29 | OK 30 | ``` 31 | 32 | 由于是同步方式进行创建的 RDB 文件,那在`SAVE`命令执行期间,Redis 服务器将**阻塞**,直到RDB文件创建完毕为止。如果 Redis 服务器在执行`SAVE`命令时已经拥有了相应的 RDB 文件,那么服务器将使用新创建的 RDB 文件代替已有的 RDB 文件。 33 | 34 | ### BGSAVE命令 35 | 36 | 那`BGSAVE`其实就是解决`SAVE`命令的阻塞问题的,它与`SAVE`命令的不同之处在于,`BGSAVE`命令是异步执行的,`BGSAVE`不会直接使用 Redis 服务器进程创建 RDB文件,而是使用子进程创建 RDB 文件。 37 | 38 | ``` 39 | redis> BGSAVE 40 | Background saving started 41 | ``` 42 | 43 | 用户执行`BGSAVE`命令,Redis 的整个执行流程如下: 44 | 45 | 1. 创建一个子进程 46 | 2. 子进程执行`SAVE`命令,创建新的RDB文件 47 | 3. RDB 文件创建完毕后,子进程退出,并通知Redis服务的主进程,新的 RDB 文件已创建完毕 48 | 4. Redis 服务器进程使用新的 RDB 文件替换已有的 RDB 文件 49 | 50 | 虽然说`BGSAVE`命令是异步执行的,Redis 服务器在`BGSAVE`命令执行期间仍然可以继续处理其他客户端发送的命令请求,不会阻塞服务器,**但由于执行`BGSAVE`命令需要创建子进程,所以父进程占用的内存数量越大,创建子进程这一操作耗费的时间也会越长,因此 Redis 服务器在执行`BGSAVE`命令时,仍然可能会由于创建子进程而被短暂地阻塞。** 51 | 52 | ### 通过配置选项自动创建RDB文件 53 | 54 | 除了手动执行`BGSAVE`和`SAVE`命令创建RDB文件外,Redis 还支持通过配置选项自动创建 RDB 文件: 55 | 56 | ``` 57 | save 58 | ``` 59 | 60 | 对于 `seconds` 和 `changes` 参数,可以这么理解:**如果服务器在 `seconds` 秒之内,对其包含的各个数据库总共执行了至少 `changes` 次修改,那么服务器将自动执行一次`BGSAVE`命令。** 61 | 62 | 当用户向 Redis 服务器提供多个 save 选项,只要满足其中任意一个选项,就会自动执行一次`BGSAVE`命令。 63 | 64 | ``` 65 | save 6000 1 66 | save 6000 10 67 | save 6000 100 68 | ``` 69 | 70 | 为了避免因满足条件,而频繁触发执行`BGSAVE`命令,Redis 服务器在每次成功创建 RDB 文件之后,负责自动触发`BGSAVE`命令的时间计数器以及修改次数计数器都会被清零并重新开始计数。 71 | 72 | ### RDB 持久化的缺陷 73 | 74 | 无论用户使用的是`SAVE`命令还是`BGSAVE`命令,停机时服务器丢失的数据量将取决于创建 RDB 文件的时间间隔:**间隔越长,停机时丢失的数据也就越多。** 75 | 76 | RDB 持久化是一种全量持久化操作,它在创建 RDB 文件时需要存储整个服务器包含的所有数据,并因此消耗大量计算资源和内存资源,所以用户是不太可能通过增大 RDB 文件的生成频率来保证数据安全的。 77 | 78 | 从 RDB 持久化的特征来看,它更像是一种**数据备份手段**而非一种普通的数据持久化手段。为了解决 RDB 持久化在停机时可能会丢失大量数据这一问题,并提供一种真正符合用户预期的持久化功能,Redis 推出 AOF 持久化模式。 79 | 80 | ## AOF持久化 81 | 82 | 与全量式的 RDB 持久化功能不同,AOF 提供的是**增量式**的持久化功能,这种持久化的核心原理在于:服务器每次执行完写命令之后,都会以**协议文本的方式**将被执行的命令追加到 AOF 文件的末尾。这样一来,服务器在停机之后,只要重新执行 AOF 文件中保存的 Redis 命令,就可以将数据库恢复至停机之前的状态,有点像 MySQL 的 `binlog`。 83 | 84 | | 时间 | 事件 | AOF 文件记录的命令 | 85 | | :--: | :-------------------: | ------------------------------------ | 86 | | T0 | 执行命令:`SET K1 V1` | SELECT 0
SET K1 V1 | 87 | | T1 | 执行命令:`SET K2 V2` | SELECT 0
SET K1 V1
SET K2 V3 | 88 | 89 | 随着服务器不断地执行命令,被执行的命令也会不断地被保存到 AOF 文件中。其实在实际的 AOF 文件中,命令都是以 Redis 网络协议的方式保存的,比如: 90 | 91 | ``` 92 | *2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n 93 | *3\r\n$3\r\nSET\r\n$2\r\nk1\r\n$2\r\nv1\r\n 94 | *3\r\n$3\r\nSET\r\n$2\r\nk2\r\n$2\r\nv2\r\n 95 | ``` 96 | 97 | Redis 服务器执行: 98 | 99 | - `appendonly yes`:开启 AOF 持久化功能,Redis 服务器在默认情况下将创建一个名为`appendonly.aof`的文件作为AOF 文件。 100 | 101 | - `appendonly no`:关闭 AOF 持久化功能。 102 | 103 | ### 设置 AOF 文件冲洗频率 104 | 105 | 为了提高程序的写入性能,现代化的操作系统通常会把针对硬盘的多次写操作优化为一次写操作。当程序调用 write 系统对文件进行写入时,系统并不会直接把数据写入硬盘,而是会先将数据写入位于内存的缓冲区中,等到指定的时限到达或者满足某些写入条件时,系统才会执行 flush 系统调用,将缓冲区中的数据冲洗至硬盘。 106 | 107 | Redis 向用户提供了`appendfsync`选项,以此来控制系统冲洗 AOF 文件的频率: 108 | 109 | ``` 110 | appendfsync 111 | ``` 112 | 113 | 通常有三个可选值,分别是: 114 | 115 | - `always`:每执行一个写命令,就对 AOF 文件执行一次冲洗操作。如果服务器停机,此时最多只会丢失一个命令的数据,但使用这种冲洗方式将使 Redis 服务器的性能降低至传统关系数据库的水平。 116 | - `everysec`:每隔 1s,就对 AOF 文件执行一次冲洗操作。服务器在停机时最多只会丢失 1s 之内产生的命令数据,这是一种兼顾性能和安全性的折中方案。 117 | - `no`:不主动对 AOF 文件执行冲洗操作,由操作系统决定何时对 AOF 进行冲洗。服务器在停机时将丢失系统最后一次冲洗 AOF 文件之后产生的所有命令数据,至于数据量的具体大小则取决于系统冲洗 AOF 文件的频率。 118 | 119 | 所以 Redis 使用`everysec`作为`appendfsync`选项的默认值。除非有明确的需求,否则用户不应该随意修改`appendfsync`选项的值。 120 | 121 | ### AOF 重写 122 | 123 | 随着服务器不断运行,被执行的命令将变得越来越多,而负责记录这些命令的 AOF 文件也会变得越来越大。与此同时,如果服务器曾经对相同的键执行过多次修改操作,那么 AOF 文件中还会出现多个冗余命令。 124 | 125 | ``` 126 | SELECT 0 127 | SET msg "hello world! " 128 | SET msg "good morning! " 129 | SET msg "happy birthday! " 130 | SADD fruits "apple" 131 | SADD fruits "banana" 132 | SADD fruits "cherry" 133 | SADD fruits "dragon fruit" 134 | SREM fruits "dragon fruit" 135 | SADD fruits "durian" 136 | RPUSH job-queue 10086 137 | ``` 138 | 139 | 以上命令,重写后最终可以简化为: 140 | 141 | ``` 142 | SELECT 0 143 | SET msg "happy birthday! " 144 | SADD fruits "apple" "banana" "cherry" "durian" 145 | RPUSH job-queue 10086 146 | ``` 147 | 148 | 为了减少冗余命令,让 AOF 文件保持“苗条”,并提供数据恢复操作的执行速度,Redis 提供了 AOF 重写功能`BGREWRITEAOF`命令,该命令能够生成一个全新的 AOF 文件,并且文件中只包含恢复当前数据库所需的尽可能少的命令。 149 | 150 | 与 RDB 持久化的 `BGSAVE`命令一样,`BGREWRITEAOF`命令也是一个异步命令,Redis 服务器在接收到该命令之后会创建出一个子进程,由它扫描整个数据库并生成新的 AOF 文件。当新的 AOF 文件生成完毕,子进程就会退出并通知 Redis 服务器(父进程),然后 Redis 服务器就会使用新的 AOF 文件代替已有的 AOF 文件,借此完成整个重写操作。 151 | 152 | 除了手动执行`BGREWRITEAOF`命令,也可以通过配置自动触发`BGREWRITEAOF`命令: 153 | 154 | - `auto-aof-rewrite-min-size `:选项用于设置触发自动 AOF 文件重写所需的最小 AOF 文件体积,当 AOF 文件的体积大于给定值时,服务器将自动执行`BGREWRITEAOF`命令,该值的默认值是 64mb。 155 | 156 | - `auto-aof-rewrite-percentage `:它控制的是触发自动 AOF 文件重写所需的文件体积增大比例。举个例子,如果此值设置为 100,表示如果当前 AOF 文件的体积比最后一次 AOF 文件重写之后的体积增大了一倍(100%),那么将自动执行一次`BGREWRITEAOF`命令。 157 | 158 | ### AOF 持久化的优缺点 159 | 160 | - 优点: 161 | - 与 RDB 持久化可能会丢失大量数据相比,AOF 持久化的安全性要高得多:通过使用`everysec`选项,用户可以将数据丢失的时间窗口限制在 1s 之内。 162 | 163 | - 缺点: 164 | - 为 AOF 文件存储的是协议文本,所以它的体积会比包含相同数据、二进制格式的 RDB 文件要大得多,并且生成 AOF 文件所需的时间也会比生成 RDB 文件所需的时间更长。 165 | - 因为 RDB 持久化可以直接通过 RDB 文件恢复数据库数据,而 AOF 持久化则需要通过执行 AOF 文件中保存的命令来恢复数据库(前者是直接的数据恢复操作,而后者则是间接的数据恢复操作),RDB 持久化的数据恢复速度将比 AOF 持久化的数据恢复速度快得多,并且数据库体积越大,这两者之间的差距就会越明显。 166 | - AOF 重写使用的`BGREWRITEAOF`命令与 RDB 持久化使用的`BGSAVE`命令一样都需要创建子进程,所以在数据库体积较大的情况下,进行 AOF 文件重写将占用大量资源,并导致服务器被短暂地阻塞。 167 | 168 | ## RDB-AOF 混合持久化 169 | 170 | 由于 RDB 持久化和 AOF 持久化都有各自的优缺点,因此在很长一段时间里,如何选择合适的持久化方式成了很多 Redis 用户面临的一个难题。为了解决这个问题,Redis 从 4.0 版本开始引入 RDB-AOF 混合持久化模式,这种模式是基于 AOF 持久化模式构建而来的,如果用户打开了服务器的 AOF 持久化功能,并且将 171 | 172 | ``` 173 | aof-use-rdb-preamble 174 | ``` 175 | 176 | 设置为 yes,那么 Redis 服务器在执行 AOF 重写操作时,就会像执行`BGSAVE`命令那样,根据数据库当前的状态生成出相应的 RDB 数据,并将这些数据写入新建的 AOF 文件中,至于那些在 AOF 重写(`BGREWRITEAOF`)开始之后执行的 Redis 命令,则会继续以协议文本的方式追加到新 AOF 文件的末尾,即已有的 RDB 数据的后面。 177 | 178 | 所以,开启了 RDB-AOF 混合持久化后,服务器生成的 AOF 文件将由两个部分组成,其中位于 AOF 文件开头的是 RDB 格式的数据,而跟在 RDB 数据后面的则是 AOF 格式的数据。 179 | 180 | | 结构 | 181 | | :------: | 182 | | RDB 数据 | 183 | | AOF 数据 | 184 | 185 | 当一个支持 RDB-AOF 混合持久化模式的 Redis 服务器启动并载入 AOF 文件时,它会检查 AOF 文件的开头是否包含了 RDB 格式的内容: 186 | 187 | - 如果包含,那么服务器就会先载入开头的 RDB 数据,然后再载入之后的 AOF 数据。 188 | - 如果 AOF 文件只包含 AOF 数据,那么服务器将直接载入 AOF 数据。 189 | 190 | 所以为了避免全新的 RDB-AOF 混合持久化功能给传统的 AO F持久化功能使用者带来困惑,Redis 目前默认是没有打开 RDB-AOF 混合持久化功能的:`aof-use-rdb-preamble no`,如果要开启,需要用户手动设置 value 为 yes。 191 | 192 | ## 无持久化 193 | 194 | 即使用户没有显式地开启 RDB 持久化功能和 AOF 持久化功能,Redis 服务器也会默认使用以下配置进行 RDB 持久化: 195 | 196 | ``` 197 | save 6010000 198 | save 300100 199 | save 3600 1 200 | ``` 201 | 202 | 如果用户想要彻底关闭这一默认的 RDB 持久化行为,让 Redis 服务器处于完全的无持久化状态,那么可以在服务器启动时向它提供以下配置选项: 203 | 204 | ``` 205 | save "" 206 | ``` 207 | 208 | 这样一来,服务器将不会再进行默认的 RDB 持久化,从而使得服务器处于完全的无持久化状态中。处于这一状态的服务器在关机之后将丢失关机之前存储的所有数据,这种服务器可以用作单纯的内存缓存服务器。 209 | 210 | ## 优雅的关闭 Redis 服务器 211 | 212 | 如何优雅的关闭 Redis 服务器呢?那就是使用 `SHUTDOWN`命令,执行该命令,将执行如下动作: 213 | 214 | 1. 停止处理客户端发送的命令请求。 215 | 2. 如果服务器启用了 RDB 持久化功能,并且数据库距离最后一次成功创建 RDB 文件之后已经发生了改变,那么服务器将执行SAVE 命令,创建一个新的 RDB 文件。 216 | 3. 如果服务器启用了 AOF 持久化功能或者 RDB-AOF 混合持久化功能,那么它将冲洗 AOF 文件,确保所有已执行的命令都被记录到了 AOF 文件中。 217 | 4. 如果服务器既没有启用 RDB 持久化功能,也没有启用 AOF 持久化功能,那么服务器将略过这一步。 218 | 5. 服务器进程退出。 219 | 220 | 所以只要服务器启用了持久化功能,那么使用`SHUTDOWN`命令来关闭服务器就不会造成任何数据丢失。`SHUTDOWN`命令提供的 `save` 选项或者` nosave` 选项,显式地指示服务器在关闭之前是否需要执行持久化操作: 221 | 222 | ``` 223 | SHUTDOWN [save|nosave] 224 | ``` 225 | 226 | 如果用户给定的是 `save` 选项,那么无论服务器是否启用了持久化功能,服务器都会在关闭之前执行一次持久化操作。如果用户给定的是 `nosave `选项,那么服务器将不执行持久化操作,直接关闭服务器。在这种情况下,如果服务器在关闭之前曾经修改过数据库,那么它将丢失那些尚未保存的数据。 227 | 228 | ## 总结 229 | 230 | 总的来说,在数据持久化这个问题上,**Redis 4.0 及之后版本的使用者都应该优先使用 RDB-AOF 混合持久化;对于 Redis 4.0 之前版本的使用者,因为 RDB 持久化更接近传统意义上的数据备份功能,而 AOF 持久化则更接近于传统意义上的数据持久化功能,所以如果用户不知道自己具体应该使用哪种持久化功能,那么可以优先选用 AOF 持久化作为数据持久化手段,并将 RDB 持久化用作辅助的数据备份手段。** -------------------------------------------------------------------------------- /images/1700479996197.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/images/1700479996197.jpeg -------------------------------------------------------------------------------- /images/1702553954147.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/images/1702553954147.png -------------------------------------------------------------------------------- /images/1702554291804.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/images/1702554291804.png -------------------------------------------------------------------------------- /images/1702554459873.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/images/1702554459873.png -------------------------------------------------------------------------------- /images/20240225204100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/images/20240225204100.jpg -------------------------------------------------------------------------------- /images/20240225205428.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/images/20240225205428.jpg -------------------------------------------------------------------------------- /img/17262840412011726284040702.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/17262840412011726284040702.png -------------------------------------------------------------------------------- /img/1727318774261.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/1727318774261.png -------------------------------------------------------------------------------- /img/20201122173132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201122173132.png -------------------------------------------------------------------------------- /img/20201122173313.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201122173313.png -------------------------------------------------------------------------------- /img/20201122173523.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201122173523.png -------------------------------------------------------------------------------- /img/20201122173737.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201122173737.png -------------------------------------------------------------------------------- /img/20201207155506.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201207155506.png -------------------------------------------------------------------------------- /img/20201216170122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201216170122.png -------------------------------------------------------------------------------- /img/20201216172630.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201216172630.png -------------------------------------------------------------------------------- /img/20201216172847.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201216172847.png -------------------------------------------------------------------------------- /img/20201216193720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201216193720.png -------------------------------------------------------------------------------- /img/20201222220143.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20201222220143.png -------------------------------------------------------------------------------- /img/20210109162855.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210109162855.png -------------------------------------------------------------------------------- /img/20210109171707.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210109171707.png -------------------------------------------------------------------------------- /img/20210110153502.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210110153502.png -------------------------------------------------------------------------------- /img/20210127153147.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210127153147.png -------------------------------------------------------------------------------- /img/20210127155351.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210127155351.png -------------------------------------------------------------------------------- /img/20210127155423.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210127155423.png -------------------------------------------------------------------------------- /img/20210131124723.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210131124723.png -------------------------------------------------------------------------------- /img/20210203160705.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210203160705.png -------------------------------------------------------------------------------- /img/20210203161326.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210203161326.png -------------------------------------------------------------------------------- /img/20210223170842.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210223170842.png -------------------------------------------------------------------------------- /img/20210706222729.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210706222729.png -------------------------------------------------------------------------------- /img/20210906215515.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210906215515.png -------------------------------------------------------------------------------- /img/20210906215624.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210906215624.png -------------------------------------------------------------------------------- /img/20210906220109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210906220109.png -------------------------------------------------------------------------------- /img/20210906220253.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210906220253.png -------------------------------------------------------------------------------- /img/20210906220916.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210906220916.png -------------------------------------------------------------------------------- /img/20210906222239.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210906222239.png -------------------------------------------------------------------------------- /img/20210906222437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210906222437.png -------------------------------------------------------------------------------- /img/20210906222539.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20210906222539.png -------------------------------------------------------------------------------- /img/20220114091234.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20220114091234.png -------------------------------------------------------------------------------- /img/20220928133622.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20220928133622.png -------------------------------------------------------------------------------- /img/20220928133650.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20220928133650.png -------------------------------------------------------------------------------- /img/20220928135119.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20220928135119.png -------------------------------------------------------------------------------- /img/202302270955561.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202302270955561.png -------------------------------------------------------------------------------- /img/202303061028280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202303061028280.png -------------------------------------------------------------------------------- /img/202303130921928.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202303130921928.png -------------------------------------------------------------------------------- /img/202303200907410.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202303200907410.png -------------------------------------------------------------------------------- /img/202303270920240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202303270920240.png -------------------------------------------------------------------------------- /img/202304030911311.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202304030911311.png -------------------------------------------------------------------------------- /img/202304031746128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202304031746128.png -------------------------------------------------------------------------------- /img/202304100921196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202304100921196.png -------------------------------------------------------------------------------- /img/202304171004893.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202304171004893.png -------------------------------------------------------------------------------- /img/202304242004525.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202304242004525.png -------------------------------------------------------------------------------- /img/202305150929276.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202305150929276.png -------------------------------------------------------------------------------- /img/202305221120720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202305221120720.png -------------------------------------------------------------------------------- /img/202305290932443.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202305290932443.png -------------------------------------------------------------------------------- /img/202306050916167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202306050916167.png -------------------------------------------------------------------------------- /img/202306120955363.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202306120955363.png -------------------------------------------------------------------------------- /img/202307170931794.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202307170931794.png -------------------------------------------------------------------------------- /img/202307240939919.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202307240939919.png -------------------------------------------------------------------------------- /img/202308070934355.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202308070934355.png -------------------------------------------------------------------------------- /img/202308141105270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202308141105270.png -------------------------------------------------------------------------------- /img/202308280903055.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202308280903055.png -------------------------------------------------------------------------------- /img/202309040937748.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202309040937748.png -------------------------------------------------------------------------------- /img/202309110914741.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202309110914741.png -------------------------------------------------------------------------------- /img/202309180926390.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202309180926390.png -------------------------------------------------------------------------------- /img/202402241953884.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402241953884.png -------------------------------------------------------------------------------- /img/202402251421855.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402251421855.png -------------------------------------------------------------------------------- /img/202402251422582.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402251422582.png -------------------------------------------------------------------------------- /img/202402251425702.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402251425702.png -------------------------------------------------------------------------------- /img/202402251427187.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402251427187.png -------------------------------------------------------------------------------- /img/202402251431170.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402251431170.png -------------------------------------------------------------------------------- /img/202402251433087.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402251433087.png -------------------------------------------------------------------------------- /img/202402251436421.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402251436421.png -------------------------------------------------------------------------------- /img/202402251444325.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402251444325.png -------------------------------------------------------------------------------- /img/202402252012668.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402252012668.png -------------------------------------------------------------------------------- /img/202402252013950.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/202402252013950.png -------------------------------------------------------------------------------- /img/20240225205754.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20240225205754.jpg -------------------------------------------------------------------------------- /img/20240225211001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20240225211001.jpg -------------------------------------------------------------------------------- /img/20240225212619.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20240225212619.jpg -------------------------------------------------------------------------------- /img/20240225213650.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20240225213650.jpg -------------------------------------------------------------------------------- /img/20240225213825.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/20240225213825.jpg -------------------------------------------------------------------------------- /img/IMG_216.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/IMG_216.JPEG -------------------------------------------------------------------------------- /img/IMG_6487.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/IMG_6487.JPEG -------------------------------------------------------------------------------- /img/IMG_904.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/IMG_904.JPEG -------------------------------------------------------------------------------- /img/IMG_908.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/IMG_908.JPEG -------------------------------------------------------------------------------- /img/hash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/hash.jpg -------------------------------------------------------------------------------- /img/hash2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/hash2.jpg -------------------------------------------------------------------------------- /img/iShot2020-09-19上午09.24.29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/iShot2020-09-19上午09.24.29.png -------------------------------------------------------------------------------- /img/iShot2020-11-05下午02.05.03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/iShot2020-11-05下午02.05.03.png -------------------------------------------------------------------------------- /img/iShot2020-11-05下午12.22.28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/iShot2020-11-05下午12.22.28.png -------------------------------------------------------------------------------- /img/iShot20201115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/iShot20201115.png -------------------------------------------------------------------------------- /img/iShot2022-01-13 17.42.06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/iShot2022-01-13 17.42.06.png -------------------------------------------------------------------------------- /img/running.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/running.jpeg -------------------------------------------------------------------------------- /img/telegram-cloud-photo-size-5-6089317861500758841-y.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/telegram-cloud-photo-size-5-6089317861500758841-y.jpg -------------------------------------------------------------------------------- /img/telegram-cloud-photo-size-5-6089317861500758842-y.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/telegram-cloud-photo-size-5-6089317861500758842-y.jpg -------------------------------------------------------------------------------- /img/telegram-cloud-photo-size-5-6089317861500758843-y.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superleeyom/blog/58bf6bbfa0f5707c08476be69ec13b110b51c024/img/telegram-cloud-photo-size-5-6089317861500758843-y.jpg -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import argparse 3 | import os 4 | import re 5 | 6 | import markdown 7 | from feedgen.feed import FeedGenerator 8 | from github import Github 9 | from lxml.etree import CDATA 10 | from marko.ext.gfm import gfm as marko 11 | 12 | MD_HEAD = """**

[Leeyom's Blog](https://blog.leeyom.top)

** 13 | ==== 14 | 15 | **

用于记录一些幼稚的想法和脑残的瞬间

** 16 | [![](https://raw.githubusercontent.com/superleeyom/blog/main/img/IMG_216.JPEG)](https://blog.leeyom.top) 17 | 18 | ## 联系方式 19 | - Twitter:[@super_leeyom](https://twitter.com/super_leeyom) 20 | - Telegram:[@super_leeyom](https://t.me/super_leeyom) 21 | - Email:[leeyomwang@163.com](mailto:leeyomwang@163.com) 22 | - Blog:[https://blog.leeyom.top](https://blog.leeyom.top) 23 | - RSS:[RSS Feed](https://raw.githubusercontent.com/{repo_name}/master/feed.xml) 24 | """ 25 | 26 | BACKUP_DIR = "backup" 27 | ANCHOR_NUMBER = 5 28 | TOP_ISSUES_LABELS = ["Top"] 29 | TODO_ISSUES_LABELS = ["TODO"] 30 | IGNORE_LABELS = TOP_ISSUES_LABELS + TODO_ISSUES_LABELS 31 | 32 | 33 | def get_me(user): 34 | return user.get_user().login 35 | 36 | 37 | def is_me(issue, me): 38 | return issue.user.login == me 39 | 40 | 41 | # help to covert xml vaild string 42 | def _valid_xml_char_ordinal(c): 43 | codepoint = ord(c) 44 | # conditions ordered by presumed frequency 45 | return ( 46 | 0x20 <= codepoint <= 0xD7FF 47 | or codepoint in (0x9, 0xA, 0xD) 48 | or 0xE000 <= codepoint <= 0xFFFD 49 | or 0x10000 <= codepoint <= 0x10FFFF 50 | ) 51 | 52 | 53 | def format_time(time): 54 | return str(time)[:10] 55 | 56 | 57 | def login(token): 58 | return Github(token) 59 | 60 | 61 | def get_repo(user: Github, repo: str): 62 | return user.get_repo(repo) 63 | 64 | 65 | def parse_TODO(issue): 66 | body = issue.body.splitlines() 67 | todo_undone = [l for l in body if l.startswith("- [ ] ")] 68 | todo_done = [l for l in body if l.startswith("- [x] ")] 69 | # just add info all done 70 | if not todo_undone: 71 | return f"[{issue.title}]({issue.html_url}) all done", [] 72 | return ( 73 | f"[{issue.title}]({issue.html_url})--{len(todo_undone)} jobs to do--{len(todo_done)} jobs done", 74 | todo_done + todo_undone, 75 | ) 76 | 77 | 78 | def get_top_issues(repo): 79 | return repo.get_issues(labels=TOP_ISSUES_LABELS) 80 | 81 | 82 | def get_todo_issues(repo): 83 | return repo.get_issues(labels=TODO_ISSUES_LABELS) 84 | 85 | 86 | def get_repo_labels(repo): 87 | return [l for l in repo.get_labels()] 88 | 89 | 90 | def get_issues_from_label(repo, label): 91 | return repo.get_issues(labels=(label,)) 92 | 93 | 94 | def add_issue_info(issue, md): 95 | time = format_time(issue.created_at) 96 | md.write(f"- [{issue.title}]({issue.html_url})--{time}\n") 97 | 98 | 99 | def add_md_todo(repo, md, me): 100 | todo_issues = list(get_todo_issues(repo)) 101 | if not TODO_ISSUES_LABELS or not todo_issues: 102 | return 103 | with open(md, "a+", encoding="utf-8") as md: 104 | md.write("## TODO\n") 105 | for issue in todo_issues: 106 | if is_me(issue, me): 107 | todo_title, todo_list = parse_TODO(issue) 108 | md.write("TODO list from " + todo_title + "\n") 109 | for t in todo_list: 110 | md.write(t + "\n") 111 | # new line 112 | md.write("\n") 113 | 114 | 115 | def add_md_top(repo, md, me): 116 | top_issues = list(get_top_issues(repo)) 117 | if not TOP_ISSUES_LABELS or not top_issues: 118 | return 119 | with open(md, "a+", encoding="utf-8") as md: 120 | md.write("## 置顶文章\n") 121 | for issue in top_issues: 122 | if is_me(issue, me): 123 | add_issue_info(issue, md) 124 | 125 | 126 | def add_md_recent(repo, md, me, limit=5): 127 | count = 0 128 | with open(md, "a+", encoding="utf-8") as md: 129 | # one the issue that only one issue and delete (pyGitHub raise an exception) 130 | try: 131 | md.write("## 最近更新\n") 132 | for issue in repo.get_issues(): 133 | if is_me(issue, me): 134 | add_issue_info(issue, md) 135 | count += 1 136 | if count >= limit: 137 | break 138 | except Exception as e: 139 | print(str(e)) 140 | 141 | 142 | def add_md_header(md, repo_name): 143 | with open(md, "w", encoding="utf-8") as md: 144 | md.write(MD_HEAD.format(repo_name=repo_name)) 145 | md.write("\n") 146 | 147 | 148 | def add_md_label(repo, md, me): 149 | labels = get_repo_labels(repo) 150 | 151 | # sort lables by description info if it exists, otherwise sort by name, 152 | # for example, we can let the description start with a number (1#Java, 2#Docker, 3#K8s, etc.) 153 | labels = sorted( 154 | labels, 155 | key=lambda x: ( 156 | x.description is None, 157 | x.description == "", 158 | x.description, 159 | x.name, 160 | ), 161 | ) 162 | 163 | with open(md, "a+", encoding="utf-8") as md: 164 | for label in labels: 165 | # we don't need add top label again 166 | if label.name in IGNORE_LABELS: 167 | continue 168 | 169 | issues = get_issues_from_label(repo, label) 170 | if issues.totalCount: 171 | md.write("## " + label.name + "\n") 172 | issues = sorted(issues, key=lambda x: x.created_at, reverse=True) 173 | i = 0 174 | for issue in issues: 175 | if not issue: 176 | continue 177 | if is_me(issue, me): 178 | if i == ANCHOR_NUMBER: 179 | md.write("
显示更多\n") 180 | md.write("\n") 181 | add_issue_info(issue, md) 182 | i += 1 183 | if i > ANCHOR_NUMBER: 184 | md.write("
\n") 185 | md.write("\n") 186 | 187 | 188 | def get_to_generate_issues(repo, dir_name, issue_number=None): 189 | md_files = os.listdir(dir_name) 190 | generated_issues_numbers = [ 191 | int(i.split("_")[0]) for i in md_files if i.split("_")[0].isdigit() 192 | ] 193 | to_generate_issues = [ 194 | i 195 | for i in list(repo.get_issues()) 196 | if int(i.number) not in generated_issues_numbers 197 | ] 198 | if issue_number: 199 | to_generate_issues.append(repo.get_issue(int(issue_number))) 200 | return to_generate_issues 201 | 202 | 203 | def generate_rss_feed(repo, filename, me): 204 | generator = FeedGenerator() 205 | generator.id(repo.html_url) 206 | generator.title(f"RSS feed of {repo.owner.login}'s {repo.name}") 207 | generator.author( 208 | {"name": os.getenv("GITHUB_NAME"), "email": os.getenv("GITHUB_EMAIL")} 209 | ) 210 | generator.link(href=repo.html_url) 211 | generator.link( 212 | href=f"https://raw.githubusercontent.com/{repo.full_name}/master/{filename}", 213 | rel="self", 214 | ) 215 | for issue in repo.get_issues(): 216 | if not issue.body or not is_me(issue, me) or issue.pull_request: 217 | continue 218 | item = generator.add_entry(order="append") 219 | item.id(issue.html_url) 220 | item.link(href=issue.html_url) 221 | item.title(issue.title) 222 | item.published(issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")) 223 | for label in issue.labels: 224 | item.category({"term": label.name}) 225 | body = "".join(c for c in issue.body if _valid_xml_char_ordinal(c)) 226 | item.content(CDATA(marko.convert(body)), type="html") 227 | generator.atom_file(filename) 228 | 229 | 230 | def main(token, repo_name, issue_number=None, dir_name=BACKUP_DIR): 231 | user = login(token) 232 | me = get_me(user) 233 | repo = get_repo(user, repo_name) 234 | # add to readme one by one, change order here 235 | add_md_header("README.md", repo_name) 236 | for func in [add_md_top, add_md_recent, add_md_label, add_md_todo]: 237 | func(repo, "README.md", me) 238 | 239 | generate_rss_feed(repo, "feed.xml", me) 240 | to_generate_issues = get_to_generate_issues(repo, dir_name, issue_number) 241 | 242 | # save md files to backup folder 243 | for issue in to_generate_issues: 244 | save_issue(issue, me, dir_name) 245 | 246 | 247 | def save_issue(issue, me, dir_name=BACKUP_DIR): 248 | md_name = os.path.join( 249 | dir_name, f"{issue.number}_{issue.title.replace('/', '-').replace(' ', '.')}.md" 250 | ) 251 | with open(md_name, "w") as f: 252 | f.write(f"# [{issue.title}]({issue.html_url})\n\n") 253 | f.write(issue.body or "") 254 | if issue.comments: 255 | for c in issue.get_comments(): 256 | if is_me(c, me): 257 | f.write("\n\n---\n\n") 258 | f.write(c.body or "") 259 | 260 | 261 | if __name__ == "__main__": 262 | if not os.path.exists(BACKUP_DIR): 263 | os.mkdir(BACKUP_DIR) 264 | parser = argparse.ArgumentParser() 265 | parser.add_argument("github_token", help="github_token") 266 | parser.add_argument("repo_name", help="repo_name") 267 | parser.add_argument( 268 | "--issue_number", help="issue_number", default=None, required=False 269 | ) 270 | options = parser.parse_args() 271 | main(options.github_token, options.repo_name, options.issue_number) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyGithub==1.59.1 2 | feedgen 3 | marko 4 | markdown --------------------------------------------------------------------------------