<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Posts on 云溪的 blog</title>
    <link>https://www.yunxicoding.top/post/</link>
    <description>Recent content in Posts on 云溪的 blog</description>
    <image>
      <title>云溪的 blog</title>
      <url>https://www.yunxicoding.top/papermod-cover.png</url>
      <link>https://www.yunxicoding.top/papermod-cover.png</link>
    </image>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sat, 16 Aug 2025 08:15:09 +0800</lastBuildDate><atom:link href="https://www.yunxicoding.top/post/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>我找到了心仪的 Linux 发行版</title>
      <link>https://www.yunxicoding.top/post/linux/cachyos/</link>
      <pubDate>Sat, 16 Aug 2025 08:15:09 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/cachyos/</guid>
      <description>这些年在我个人的开发电脑上用过很多的发行版，基于 Debian 的 （Ubuntu , Linux Mint ）, 基于 Red Hat Enterprise Linux （ CentOS，Fedora），基于 Arch 的（EndeavourOS、Manjaro）,还有我们国产优秀发行版 deepin。
作为个人开发使用的发行版我比较看重的点是能否更快的使用最新的软件版本和内核版本，能否充分利用硬件性能以及能否使用最新的 Linux 技术。
基于以上原因我把发行版锁定在 Arch 或基于 Arch 的发行版。Arch 的滚动更新，能够让我一直使用最新软件版本，也能第一时间体验 最新的 Linux 系统内核的新特性。
我是通过 Manjaro 进入了 Arch 的世界，它对新手非常友好，简单的安装方式、优秀的兼容性帮我平滑的度过了新手期，它也是我使用时间最长的发行版。
后来为了体验更加原汁原味的 Arch ，我安装了 EndeavourOS，EndeavourOS 比 Manjaro 更加的轻量化，软件包的发布也要比 Manjaro 要领先一些。EndeavourOS 整体安装和使用和 Manjaro 差不多，但兼容性上较 Manjaro 要差一些，这些兼容性问题通常并致命不影响正常的使用。
一次偶然的机会我接触到了 CachyOS ，我感觉它就是我想要找的发行版了，他所做的一切很符合我对操作系统的需求。 简单的安装方式 CachyOS 和 Manjaro 、EndeavourOS 一样有着图形化的安装方式，我们可以很方便的把系统安装到我们的电脑中。即使新手也可以很容易的使用 CachyOS。
CachyOS 集成了丰富的 Desktop Environments 除了常见的 KDE、GNOME 、FCE 还有很多平铺的 DE 如 i3、Hyprlad，CachyOS 集成的 DE 一共有 17 种之多，这可以让你用一种很纯净的方式把 DE 集成到系统中。</description>
    </item>
    
    <item>
      <title>我为什么切换到 Podman</title>
      <link>https://www.yunxicoding.top/post/container/podman/</link>
      <pubDate>Sat, 09 Aug 2025 00:31:18 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/container/podman/</guid>
      <description>最近我把本地的容器管理引擎切换到了 Podman,本篇文章来讲讲我为什么从 Docker 切换到 Podman。
兼容 Docker Podman 对 Docker 的兼容非常好，Podman CLI 和 Docker CLI 命令几乎一致，这样就大大降低了切换成本，你甚至可以把 Podman CLI 就当作 Docker CLI 来用。
无守护进程 Docker 有一个守护进程 dockerd 负责管理容器、镜像和网络，Docker CLI 所有的指令都是通过 dockerd 来实际执行的。
Podman 却没有这样一个守护进程来处理，Podman CLI 会启动一个 conmon 进程来管理容器的生命周期，启动 conmon 后 Podman CLI 退出了，只有 conmon 带着它管理的容器作为系统进程保留了下来。
这种无守护进程的模式带来了很多好处：
我的系统不用一直启动一个 dockerd 守护进程，减少了系统资源的占用。 不会因为 dockerd 的异常退出导致所有容器崩溃 支持 rootless Podman 支持以普通用户运行而 docker 默认情况下是以 root 用户来运行的。当发生容器逃逸时 Docker 能拿到 root 权限而 Podman 会将影响范围限制到用户层面，降低了风险的影响范围。
更方便的网络设计 Podman rootless 默认使用的是 slirp4netns 它仅能给单容器提供网络服务，不能提供容期间的通讯，如果想容器间进行通讯就只能通过物理机映射端口或者把容器加入到同一个 pod 进行通讯了。</description>
    </item>
    
    <item>
      <title>探索一种新的项目组织形式</title>
      <link>https://www.yunxicoding.top/post/docker/new-laravel-docker-compose/</link>
      <pubDate>Thu, 26 Jun 2025 07:47:05 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/new-laravel-docker-compose/</guid>
      <description>目前我们公司已经深度的使用 Docker 来部署项目，在推进 Docker 落地的过程中，如何将 Docker 的配置文件更好的融入项目一直是困扰着我们的问题。
刚开始我们把 Docker 配置文件全部一股脑地放在项目里, 使编程架构和部署架构给混在了一起，如果开发和部署是一拨人倒还好，如果开发和部署是分开的两个工种，这种方式会造成一定程度的混乱。
以我们 Laravel 项目目录结构为例
.
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── docker
├── docker-compose.yml
├── Dockerfile
├── .editorconfig
├── .env.example
├── .git
├── .gitattributes
├── .gitignore
├── .gitlab-ci.yml
├── package.json
├── .php-cs-fixer.cache
├── phpunit.xml
├── public
├── README.md
├── resources
├── rolling-update.sh
├── routes
├── storage
├── tests
├── vendor
└── vite.</description>
    </item>
    
    <item>
      <title>Laravel docker compose 部署方案介绍</title>
      <link>https://www.yunxicoding.top/post/php/laravel-dockercompose/</link>
      <pubDate>Wed, 25 Jun 2025 11:50:36 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/php/laravel-dockercompose/</guid>
      <description>最近在公司搭建最新版本的 Laravel 开发环境，对 docker 部署进行了进一步的优化，我们之前的项目是把定时任务 Cron 和 API 在同一个容器里启动的，这显然不太符合容器的最佳实践，借着这次框架搭建的契机，我决定优化一下这个问题。
环境如下:
Laravel : 12
PHP : 8. 4
我们把项目跑在两个容器里，一个是 API 的容器，另一个是 Supervisor 的容器，把 Cron 和 Jobs 放在放在 Supervisor 里执行。
这样就能避免维护过多的容器。在后期做 CI/CD 也会更加方便。
Supervisor 使用和 PHP 一样的 Dockerfile 因为 Supervisor 会用到 PHP 环境来起 Laravel 的 Jobs。
整体方案就是这样来做的，比较简单, 文末我会放一个 git 仓库地址，感兴趣的可以可以进一步了解，接下来讲讲一些细节。
Cron 的配置我们放在了 Dockerfile 里来配置，当然你也可以放在 entrypoint.sh 里来做。
RUN echo &amp;#34;* * * * * /usr/local/bin/php /var/www/artisan schedule:run &amp;gt;&amp;gt; /var/log/cron.log 2&amp;gt;&amp;amp;1&amp;#34; | crontab - 我们的 docker compose 配置如下：</description>
    </item>
    
    <item>
      <title>安宁</title>
      <link>https://www.yunxicoding.top/post/think/peace/</link>
      <pubDate>Fri, 13 Jun 2025 09:35:20 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/peace/</guid>
      <description>最近听孟岩播客《够与多》有很深的共鸣，我听了很多遍，在听的过程中去思考和感受，目的就是为了减少知识的缝隙。
要想回应什么是够什么是多，首先得定义我是谁，我需要一个怎样的人生，只有定义了这个才能知道什么是够，什么是多。
如何找自我 找寻自我非常难，很多人终其一生也没有找到自我。我也在找寻自我中陷入了迷茫，直到有一天 the pathless path 里的一句话，为我在黑暗中点起了一丝光亮。
你应该去做那些给你带来能量的事情，而不是消耗你能量的事情。
我们看到过很多地方告诉你要逃离舒适区，这种阐述似乎是没有什么问题，如果舒适区是一个圈，逃离的方向却有四面八方，你要怎么找到自己的方向才是更为关键的。
人生只有一次，我们的人生不该由他人定义，我们要花时间审视自己的人生，找到自己人生的方向。
时间跨度 以更大的时间跨度去思考自己的人生，会更有助于我们梳理人生的方向，段永平说：“做对的事情，把事做对。” 我觉得对我们找到自己人生的方向有很大的启发意义。
有些人可能会觉得玩游戏是自己人生的意义，我不否定这种意义，电竞已经成为奥运比赛的项目，可以看出游戏已经不像 20 年前那样被视为洪水猛兽。有很多游戏的职业选手，梦想的起点也是源于对游戏的热爱。
我们可以想象一直玩游戏十年以后的光景是什么，如果那个光景是你想要的那你就可以去把玩游戏当作自己的人生意义。
找到比别人好十倍的事情 我们也可以找到自己不用很努力就可以做的比别人好十倍的事情，这种事情一般不容易找到，你可以从今天开始主动觉察，去发现那些你比别人更擅长的事情。
也许你发现自己比别人好的并没有十倍，可能仅仅有两倍，那也没有关系，你已经比别人擅长了，去保护好这颗幼芽，持续灌溉，直到它生成为比别人好十倍的事情。
热爱的事情 巴菲特说每天跳着踢踏舞上班，可见热爱是有多么大的能量。热爱也是你能够让你做事比别人好十倍的基础之一。
热爱能让你苦中作乐，有些事情可能会让他人感到痛苦，但对你来说是攀登高峰的喜悦，你可以不断的在热爱的事情上突破，即帮助我们逃离舒适圈，又帮助我们更容易做出比别人好十倍的事情。
关于比较 人生的很多痛苦来自于比较，不必与他人对比，你有你自己的精彩。对于我们大多数人来说找到自己人生的意义，活出自己想要的人生才是最重要的。
什么是够？什么是多？每个人的标准都不一样，同样是有 80 亿，有人卧轨自杀了，有人却匿名捐到基金会帮助了更多的人,使自己的人生更有意义。
社会有很多的准则，工业社会不仅制造了更多的商品，也制造了人们想要更多的心，最开始我们买张桌子是为了满足放东西的需求，而现在我们买张桌子已经远远超过了放东西的基本需求&amp;hellip;
商家通过各种影响，让我们陷入了消费陷阱，我们希望购买的商品去替我们说话，来展现自己是一个什么样的人。
当我们停下来审视自己时，或许会发现我们真正需要的并没有那么多，当我们明白自己真正需要的是什么的时候，我们自然也就逃离了消费主义陷阱。
努力活明白 世界太复杂，人生又太短。我们都在努力的活明白，我们想把复杂的世界以及我们复杂的行为阐述的清楚明白，这并不简单。
我一个朋友的父亲说过这样有一段很有哲理的话，大意如下
我们每个人一生都在努力活得自洽
是的，世界是矛盾的，我们自己也是矛盾的，能活的自洽，不拧巴真的是一件不容易的事情。这一生太宝贵，值得我们好好思考和规划。
建立自己的内核 当你有了稳定的内核，你就不会被其他事物牵着鼻子走，你有自己衡量生活的准则，你可以努力的活成自己想要的样子。
没有人比我更适合定义我是谁，我想过怎样的生活。知道了自己想要的生活，才不会被外来的干扰所裹挟，每个人都不相同，各有各自的精彩。
把握当下 有这么一则小故事
小和尚问师父： “师父，什么是修行？” 师父答： “饥来吃饭，困来即眠。” 小和尚疑惑： “这人人都会呀！” 师父说： “非也，世人吃饭时百般计较，睡觉时千头万绪；我则吃饭时吃饭，睡觉时睡觉。”
专注与当下应该做的事情，偶尔抬头看看目标即可。现在的社会更为嘈杂，我们的注意力已经被各种超级 APP 占用，每天面对山呼海啸的信息流，能做到专注于当下已经越来越难。
适当的冥想更加有助于我们抚慰内心的安宁，正像有句话说的那样：“你担心的事情，90 % 都不会发生。”既然这样，我们又何苦耗费心神，担心那些本就不会发生的事情呢。
最后附上一首小诗，祝大家都能找到自己：
New York is 3 hours ahead of California,
在时间上，纽约走在加州前面三个小时,
But it does not make California slow.</description>
    </item>
    
    <item>
      <title>我为什么要使用 vim</title>
      <link>https://www.yunxicoding.top/post/think/use-vim-reason/</link>
      <pubDate>Thu, 12 Jun 2025 16:42:09 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/use-vim-reason/</guid>
      <description>一个十年的 Vimmer 说在 AI 时代，Vim 的优势正在被 tab 替换掉，加上 Vim 创始人离世，对 Vim 的前景充满忧虑，考虑放弃 Vim 使用现代代码编辑器+鼠标。
我觉得选择没有对错，只有适合不适合，当下的他认为编辑器+鼠标更适合他，那这个选择对他来说就是最好的选择。
我接触 Vim 时间不长，借此机会，我想谈谈我为什么选择 Vim，我认为 Vim 的优势不在于它快，而是一整套不被打断的工作流。鼠标操作它简单高效，它降低了个人电脑的使用门槛，我觉得个人计算机的普及除了图形化界面，另一个重要的因素就是鼠标的加入。它一定程度上带来了技术平权，让计算机不至于仅仅是极客群体才能使用的工具。
程序员是一个比较特殊的群体，当他开始把头脑里设计好的方案实现时，会有很长的时间对着代码编辑器进行比较重度的输入。这期间可能会设计打开文件或者 git commit 正在实现方案中某个最小实现的 feature，此时就需要使用鼠标进行操作，一般我们在 coding 的时候，速度是相当快的，而鼠标操作相对于键盘输入是要慢一点的，我们不得不从高速输入的状态中减速，使用鼠标去完成对应的操作。
由于鼠标一般离键盘都会有一段距离，这取决于你键盘的配列，配列越大，手移动的距离就会越大。这种物理距离的存在，即便你能很高效的切换，也多少会显得有些手忙脚乱。
这种中断，对我来说不太好，为了避免这样的中断, 我开始关注 vim, 好在 lazyvim 很大程度上降低了入门门槛，在一个 YouTuber 视频的指引下，我开始进入了 lazyvim 的世界。
目前我使用下来整体感觉还是比较好的，没有什么特别明显的不适，加上 lazyvim 可以通过 Lua 编写脚本，这使 lazyvim 有了更高的可定制性，我用 Lua 写了一个代码片段的脚本，整体效果良好，同样的事情我曾经想在 VS Code 里也想做过，由于 VS Code 对代码片段限制有点大，导致我没有实现。
我也不确定 lazyvim 是不是上述状态切换的正确答案，但是它是我目前找到的比较好的答案，如果未来有其他更好的方案，我很乐意去尝试。
我看过一个 YouTuber 使用 Macbook 键盘+ touchpad 来操作，看上去感觉要比鼠标更好一些，因为它移动距离足够小，且 touchpad 的操作效率，某些场景下确实比纯键盘操作更高。
其实说到底无论什么编辑器，最终它都是一个工具，只要使用顺手就可以，无须过多纠结，当你需求产生了，你自己自然就会知道自己需要什么样的代码编辑器。</description>
    </item>
    
    <item>
      <title>我找到了自己心仪的键盘</title>
      <link>https://www.yunxicoding.top/post/base/keyboard/</link>
      <pubDate>Wed, 21 May 2025 16:46:43 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/base/keyboard/</guid>
      <description>到目前为止，我也买了不少机械键盘，有早年非常火的菲尔可的圣手二代，也有近几年挺火的 RAINY75 ，但我始终没有找到自己心仪的键盘。
但是在这么多年的摸索过程中，我对键盘需求却越来越明确，我对小键盘依赖不大，键盘上方的数字键就已经很好的满足了我的需求，其次是我希望键盘满足我需求的情况下要尽可能的小（太大的键盘会给我一种笨重感），现阶段 75% 配列的键盘对我来说是非常合适的。
我希望它要是矮轴，能够让我在长时间打字的时候减少因为键程过长而带来手部的不适。矮轴对于高速的打字来讲也要比高轴更加的流畅，如果你对我说的这种感觉不太清楚可以找个笔记本的键盘和高轴键盘用你最快的速度打出同样的字（笔记本键盘别太差），你就能感受出两者的不同。
NuPhy Air75 V2 出现在我的视野中, 我看到它的第一眼我觉得，它是我想要的键盘了。
它尺寸小，重量轻，能够很好的满足便携性需求，外观设计也非常的不错，让你在第一眼看上去就有一种非常惊艳的感觉。稻盛和夫的书里面有这样一段关于产品的描述
要打造出划破手的产品 好的产品应该像一把精心打磨的刀，刀刃如此锋利，以至于轻轻触碰就能感受到其品质——这代表对细节和完美的极致追求。
当我看到 NuPhy Air75 V2 书中的描述仿佛在此刻具象化了，它的整体设计，让我看到了背后团队的努力和认真。也让我对背后的团队产生了兴趣，后来我在他们官网找到了他们的一个 slogan
NuPhy® Studio 是⼀群⽆聊的家伙创⽴的⼩公司，等到我们⽆法制造出有趣产品的那⼀天，这个⼯作室将不再存在。
我从 NuPhy Air75 V2 确实感受到了他们的热情，他们成功的通过产品表达了他们的理念。我认为倾尽热情做出一款让人认可的产品是一件非常幸福的事。
说回键盘本身，除了尺寸，重量和外观满足我的需求外，按键的使用感受也是非常出色的，我选的是青轴，它段落感清晰，回弹力度干脆，高速打字时不会有绊手的感觉，虽说是青轴，它的声音没有那么的大，快速打字的时候有点像鼠标点击的声音。它让你码字时感到非常舒适，买来后我使用了一月有余，这是一款我找不出缺点的产品。
关于矮轴的选轴建议，线性轴和段落轴看个人喜好选择即可，轴体的触发力度建议选大一些的，这样反馈会比较清晰，也能减少误触。
NuPhy Air75 V2 已经牢牢的把我圈粉了，不过在这里也给大家提醒一下，本文纯属主观的使用感受和体验，正所谓萝卜白菜各有所爱， NuPhy Air75 V2 能够很好的满足我的需求，但未必适合你。
如果你还没有找到一个适合自己的键盘，我建议你还是得首先明确自己的需求，知道了自己的需求，才能少走弯路，更快的找到适合自己的产品。
如果你对 NuPhy 也感兴趣，可以去关注一下他们家的产品。最好能够想办法试用一下，节省钱袋子😄。最后希望大家都能找到自己喜欢的键盘。</description>
    </item>
    
    <item>
      <title>手把手教你用 GitLab CI/CD 部署项目</title>
      <link>https://www.yunxicoding.top/post/ci-cd/gitlab101/</link>
      <pubDate>Sat, 15 Feb 2025 21:22:56 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ci-cd/gitlab101/</guid>
      <description>如果你用 GitLab 来做项目管理，我非常建议你了解一下 GitLab 的 CI/CD 的功能，它可以自动帮你完成项目的打包、部署、上线。
免去了上线过程中的复杂操作，对于标准化流程机器向来比人做的好。也能减少很多不必要的 Human Error 。
本文以 Python 项目部署为例，讲一下 CI/CD 中的一些部署思路，给你一个引子，引出你对 CI/CD 更多的探索。
概括来讲，CI/CD 包含两个关键步骤：配置和执行，只要你的项目根目录里有 .gitlab-ci.yml 配置文件，GitLab 就会读取，然后按照配置去执行计划。
配置： 是通过 GitLab CI 配置文件 (.gitlab-ci.yml )来告诉，执行者（Runner）要做哪些事情
执行： 执行的动作是由 Runner 来做的, Runner 是一个可执行程序，可以在任意一台服务器上。项目可以配置一个或多个 Runner。
关于配置 以下是 .gitlab-ci.yml 的一个模板，可以复制到你项目中改改就可以完成你项目的自动部署功能。
stages: # 定义步骤 - deploy deploy_test: # job 名称 image: instrumentisto/rsync-ssh:latest # 指定 docker 镜像 stage: deploy # 所属的步骤 script: # 执行的脚本 - mkdir -p ~/.ssh - echo -e &amp;#34;Host *\n\tStrictHostKeyChecking no\n\n&amp;#34; &amp;gt; ~/.</description>
    </item>
    
    <item>
      <title>我开始用 Neovim 来开发了</title>
      <link>https://www.yunxicoding.top/post/ide/nvim/</link>
      <pubDate>Sat, 26 Oct 2024 16:03:35 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ide/nvim/</guid>
      <description>一直以来，我都感觉传统编辑器鼠标和键盘的频繁切换的体验让我无法接受，它让编程这件事情拆分成了两个系统：输入和移动。我一直有将他们统一的冲动，但又受限于 vim 学习成本过高的固有认知一直没有行动。它像一根若有若无的刺一直刺痛着我，直到有一天我接触到了 lazyvim，我想也许是时候改变了。
lazyvim 可以极大程度降低了配置难度，可以在不进行任何配置的情况下拥有一个轻量级的代码编辑器。
我切换 Neovim 除了 lazyvim 降低了配置成本还有另一个原因：我认为自己还会有相当长的时间开发相关的活动，一个高度可定制的编辑器，可以让我随心所欲的打造一个称手的工具。
关于配置我使用的是 https://github.com/craftzdog/dotfiles-public 并在此基础上扩充了 PHP 开发相关的配置。在文章底部我会贴出自己的 dotfile 仓库，目前我的配置可以用于 VUE、PHP、GO 的开发，如果需要可以自取。
在开发之前我们还是需要对 Neovim 基础操作有一定的了解，这里介绍一下我的学习方式。
我是通过 https://lazyvim-ambitious-devs.phillips.codes/ 一书建立的最初的基本只是，后续又看了一些视频加深我对 Neovim 的理解。
在视频学习的过程中，可以很好的学习资深的 vimer 是如何进行 vim 操作的，拿来主义可以省去很多自己探索的精力。最开始只需要模仿就可以了，等你对 vim 使用熟练后你会有自己的想法，那时自然而然的会形成自己独有的操作方式。
视频的学习我参考了 craftzdog 比较多，在 B 站上也学习了其他人的一些操作技巧。
Neovim 的学习成本也没有想象中那个高，正常下来，一周你就可以比较得心应手的进行开 发了。
得益于 LSP 的技术，让 Neovim 对开发语言的支持和 Vs code 并没什么本质的差别。 Vs code 对语言的支持也是通过 LSP 来实现的。
Neovim 带来了什么？它确实能使我整个开发的工作流完全脱离了鼠标，所有开发的工作包括但不限于代码编写、函数跳转、以及 git 的提交等全部都可以脱离鼠标完成，双手不需要离开键盘区域就可以完成编程所需要的操作。
此外像文件操作：打开、删除、重命名等操作，Neovim 也可以很方便的操作。它确实让我更专注了，所有的操作都没有任何的打断，我不需要频繁的把手离开键盘做一些额外的鼠标操作。
上文说到，Neovim 可定制化程度更更高，理论上你能按照你的习惯去配置 Neovim 来使它与你配合的更默契。
Neovim 会不会对开发效率带来一定的提升？从我目前的使用程度来看，它并不能带来明显的开发效率的提升，Neovim 在一些操作上的确更有优势，但是也有一些操作没有 GUI 更加的高效。总的看来我觉得它的开发效率和 Vs code 差不多。</description>
    </item>
    
    <item>
      <title>谈谈规范</title>
      <link>https://www.yunxicoding.top/post/think/coding-standards/</link>
      <pubDate>Fri, 20 Sep 2024 22:10:58 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/coding-standards/</guid>
      <description>在多年的开发经历中，我发现不重视编程规范是普遍存在的一个问题。很多开发人员对规范的态度都是很抵触的，认为规范的条条框框是枷锁，会降低开发效率。
不仅仅是普通员工，很多公司的管理人员对于规范的重视程度也是不够的，甚至就没有制定规范的想法。
为什么会出现这种现象呢，我想每个人都会有自己的角度，大多数原因可能是因为项目周期紧张，没有时间制定规范，或是单纯的认为规范会影响开发效率。
上面的原因是客观存在的，所以大家对于不制定规范也没有太大的心智负担。今天我想就这个现象谈谈我对规范的认识。
规范一般有两种，一种是项目规范一种是编程规范。
关于项目规范 我在刚开始入行的时候，公司虽然也没有自己的规范，但是带我的前辈，给了我一份谷歌的开发规范，让我学习，代码的书写格式是怎么样的，什么时候应该换行，应该怎么避免写嵌套 if&amp;hellip; 这让我对代码规范有了一个最基本的认识。
代码需要书写的简洁，易读。代码规范有点像写文章时的字体工整程度，越遵守代码规范，字体就越工整。你可以想象就算同样的一篇文章，字体工整和字体潦草给阅读人的感受是完全不同的。
后来我在一家公司做管理，刚到公司时也没有自己的规范，每个人的代码风格，变量命名，项目组织的习惯各有不同，导致项目的风格多样，这给我们带来了一些问题。
维护成本高 当我们维护其他同事写的代码的话，需要从头开始阅读一遍代码，才能了解问题具体出在哪。如果我们有规范就不需要通读所有代码，仅需依据规范出问题代码的大体位置。
代码复用差 当我们有一些通用业务需要封装时，如果没有规范，就需要考虑所有的代码风格，甚至要做一些额外的兼容才能把组件封装完成，有了规范我们完全就可以避免这类问题。
项目对接慢 由于没有规范，每次公司来了新同事，我们并没有很完善的文档给到他。导致新同事上手项目比较困难，中间还要不断地去询问老同事，才能对代码有一个相对比较完善的理解。
这无疑增加了很多的摩擦成本，且团队越大，这种摩擦成本就会越高。就像我们都说中文，又各自有自己的方言，大家需要了解每个人的方言体系，才能更好的深入交流。
我们需要“普通话”这样一个统一的说话规范，来降低我们团队内部的摩擦成本。
建立规范 为了解决上述问题，我们结合自身团队的特点，参考了他人的规范制定出了我们自己规范的初版。
初版规范经过内部评审和修改后，我们形成了自己的规范文档。
后续我们在实践的过程中遇到了一些规范上的不足，也进一步对规范做了补充，使规范在原有的基础上不断的迭代和优化。
这样一套规范建立后，使我们每一步都是在之前的基础之上，而不是每一次都是从 0 开始。这让我们可以不断的阶梯向上。
如何让规范落地 规范的制定后就需要考虑规范落地问题，如何让团队更好的遵守规范。
首先需要让团队认识到规范对我们团队合作是有益的。做好心理建设，消除心理的抵制情绪。
把规范塑造成开发文档，让团队养成经常查阅的习惯，对规范有疑惑就去规范文档找答案。
利用工具降低规范的执行难度，像一些代码风格的问题可以通过 IDE 代码格式化解决，不用团队开发者额外注意。
使用静态代码分析工具检测代码是否符合规范，如果使用 CI/CD 可以把这一步集成到 CI/CD 中，让不符合规范的代码，无法提交到线上环境。
关于编程规范 编程规范包括：设计模式，设计原则，编程范式，最佳实践等等。这类规范一般是行业发展多年，一些经验丰富的前辈总结的一些开发工作的规范。
因为这类规范通常是前辈们在长期实践中总结出来的。遵守这类规范能让我们更容易开发出稳定的程序，少走弯路。
这类规范一般都具有普遍性，对语言没有任何的限制，因此一旦你熟练的掌握，你用任何语言开发的程序都会有更好的稳定性，更少 Bug 、更容易维护和扩展。
我发现有些同学会遇到一些奇奇怪怪的问题，或者一段时间后，程序出现了运行瓶颈。有不少是因为没有遵守某些编程规范引发的问题。
有时他们不遵守编程规范，并非不愿意，而是因为他们不了解某些规范。因此对于编程规范，要像探寻宝藏一般，时常去探索一下。不断地完善和补充自己对编程规范的理解。
总结 规范短期看可能会觉得有点浪费时间，影响开发效率，从长期看，却未必如此，那些减少的 Bug, 复用的代码，以及与同事之间协作效率的提升，足以弥补开发时的损耗。
牛顿说：我之所以比现别人看的远，是因为我站在巨人的肩上。
运用前人的智慧和经验，能让我们做事情更加的顺利。要站在前人的肩膀上，哪怕你觉得这个人不够高，也比自己站在平地上看的更远。</description>
    </item>
    
    <item>
      <title>零停机升级服务</title>
      <link>https://www.yunxicoding.top/post/ci-cd/zero-downtime/</link>
      <pubDate>Wed, 18 Sep 2024 11:59:00 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ci-cd/zero-downtime/</guid>
      <description>在项目迭代的过程中，我们经常会进行更新线上代码的操作，而更新的操作我们会希望影响的用户越少越好，最好是能做到零停机更新。
在目前云原生的大背景下，K8s 的滚动更新策略会更为人知一些，但是一些中小型项目的部署建立一套 K8s 服务从成本和收益的角度来看是有些得不偿失的。
本文介绍用 Docker 和 Traefik 如何实现零停机升级服务。关于 Traefik 在 更靠近云原生的反向代理神器-Traefik 一文中有更详细的介绍。
整个服务部署流程如下 本方案通过 gitlab CI/CD 来进行部署，开发人员仅需在对应的分支提交代码，服务器会自动完成 构建 -&amp;gt; 部署 -&amp;gt; 通知 等操作。 在 部署 阶段通过蓝绿发布的方式来进行零停机升级的。
蓝绿发布是通 shell 脚本进行操作的，由于 Traefik 的 docker provider 不支持动态的增删服务，本方案采取了 file provider,shell 会启动新服务的时候修改Traefik 的 file provider 来把新服务加入到负载均衡的集群，并在新服务启动完成后，把老服务从负载均衡迁出并停用老服务。
得益于 Traefik 对动态配置的支持，所有对配置文件的修改是无需重启 Traefik，它会自动发现配置文件的变动，把最新的配置加载到程序内部。
整个 部署 流程中始终都会有容器在线提供服务，我们也就实现了零停机升级系统的目标。
本文最后会附上一份完整的 demo 链接，感兴趣的可以下载之后运行一下了解整个操作机制。
本方案还存在一些局限，如蓝绿发布其实是 blue 和 green 交替提供服务，如果你本身是一个服务集群，这个方案并不能很好的适应。
如果你面临上述问题可以考虑 docker swarm 来解决问题，或者部署 K8s，其间的选择主要是看你服务的规模。
本文到此基本结束，如果你更好的想法，欢迎留言讨论。
示例代码：https://github.com/yunxi177/zero-downtime
References zero-downtime deploys with Docker Compose and Traefik:https://github.</description>
    </item>
    
    <item>
      <title>更靠近云原生的反向代理神器-Traefik</title>
      <link>https://www.yunxicoding.top/post/docker/traefik/</link>
      <pubDate>Mon, 02 Sep 2024 08:06:53 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/traefik/</guid>
      <description>Traefik 是一个用于反向代理的软件，如果你没有听说过，可以把它看作 Nignx 也是可以的。
Traefik 相比与 Nginx 更适合容器化部署：
只需要配置下容器的标签，Traefik 就可以自动发现新服务的加入，并根据标签绑定对应的域名 支持 TLS 证书自动申请（Let&amp;rsquo;s Encrypt） 支持 TCP/UDP 协议的转发 支持动态配置，可以在服务运行的状态下进行 Service、Router、Middleware 的增/删/改 Traefik 在项目最初部署阶段，会让整个部署更聚焦。以前用 Nginx 部署，你除了需要把容器启动起来，还需要配置 Nignx 将对应服务的域名反向代理到容器导出的端口上。
Traefik 将这个绑定域名的步骤放在了 docker compose 编写的阶段只需给容器加一个标签即可。项目上线时启动容器，Traefik 根据标签的配置把域名绑定到对应的容器上。
Traefik 可以通过 docker network 容器进行通讯，这意味着容器服务的端口无须再导出到物理机，Traefik 通过 docker network 就可以找到对应的容器处理请求。
Traefik 整个流程如下（中间件非必须，可有可无）：
入口点(Entrypoints) -&amp;gt; 路由(Routers) -&amp;gt; [中间件(Middleware)] -&amp;gt; 服务(Service) -&amp;gt; Server Traefik 的配置分为两个：程序配置和路由配置。程序配置通过 traefik.yml 文件进行配置。traefik.yml 提供了路由配置的解析引擎（Providers）。
traefik.yml 配置示例如下：
log: level: INFO accessLog: {} api: dashboard: true insecure: true entryPoints: # 入口点配置 web: address: &amp;#34;:80&amp;#34; providers: docker: # 通过 docker label 的方式进行路由配置 endpoint: &amp;#34;unix:///var/run/docker.</description>
    </item>
    
    <item>
      <title>我把本地开发环境全部容器化了</title>
      <link>https://www.yunxicoding.top/post/docker/dev-docker/</link>
      <pubDate>Sat, 17 Aug 2024 10:08:20 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/dev-docker/</guid>
      <description>由于本地项目比较多，且对于环境要求各不相同，导致电脑上要装多个环境才能满足项目的运行。以我常用的 PHP 开发为例，需要 PHP 7.1 、PHP 7.3、PHP 8.1 、PHP 8.3，在这些项目中 compose 如何寻找到正确的 PHP 版本来安装依赖也是一个比较难以解决的问题。
为了不再受此困扰，我决定用容器来解决我面临的问题。开发环境容器化还有一个考虑就是容器本身对 CI/CD 比较友好，我们在测试和线上环境已经开始转向容器化部署，如果本地也进行容器化，编写好的 Dockfile 可以复用到后面的部署流程。
我的方案是为每个项目编写一个 docker-compose.yml 当我开始进入某个项目的开发工作时。只要打开对应的项目通过 docker-compose.yml 启动容器就可以启动对应项目的开发环境进行开发了。
容器化给我带来了哪些好处：
纯净的物理机：我不需要在物理机上安装各种各样的环境 低负载开发： 只有运行开发的相关环境，其他不相关的服务都不会在后台运行 部署和开发一致： 由于都是 Docker 部署，不会因为环境问题导致本地开发没问题线上出错的情况 专注于开发本身 ：免去了多种环境下互相兼容下的管理工作，专注于开发本身。 在容器话的过程中，我也遇到了一些问题，在这里记录一下，供诸君参考。
一些问题 首先遇到的问题就是依赖缓存的问题，比如一个 GO 的项目，因为 GO 的环境是在容器里，这样每次启动项目都要重新下载一次依赖，浪费时间也没有必要，于是我就将 mod 依赖下载目录通过挂载卷映射到我本机上。
如此依赖就解决了每次启动容器，都要重新安装依赖的问题，这样做还有一个额外的好处就是我其他的 GO 项目也也可以把 mod 依赖下载路径映射到相同的位置，这样就能实现下载依赖的复用，也能避免过多的磁盘占用。
如果你是前端项目使用 pnpm 管理工具，也可以参考上述思路解决 npm 包的依赖复用问题。
遇到的另一个问题就是 Windows 环境下，文件读写性能不足的问题。Windows 用户在开发环境容器化是需要注意一下，如果你的项目运行需要大量的文件读取，会导致项目启动异常慢，这个是 Windows 下面使用容器的硬伤，目前无解。Linux 和 MacOS 并不会出现此类问题。
工具推荐 如果你用 Visual Studio Code 进行开发，可以安装一个 Docker 的扩展，通过设置 Docker: Compose Down 和 Docker: Compose Up 两个快捷键实现快速的启动和停止项目。</description>
    </item>
    
    <item>
      <title>Pagespy 落地实践</title>
      <link>https://www.yunxicoding.top/post/web/pagespy-practice/</link>
      <pubDate>Sat, 16 Mar 2024 15:41:01 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/pagespy-practice/</guid>
      <description>PageSpy 是货拉拉开发的一款可以远程调试前端页面、小程序的工具。在调试端，你可以像操作 chrome 控制台一样很方便的查看 console 输出、网络请求、本地存储、SEESION/COOKIE。基本上 PageSpy 的调试端相当于一个 chrome 控制台，只不过他可以让你本地调试任何远程的客户端。
关于 PageSpy 在前端远程调试利器 PageSpy 更详细的介绍。
理论上 PageSpy 能力还是很强大的，为了保证方案更好的落地，我们希望在解决我们主要痛点的同时，接入成本要尽可能的简单。
我们准备用 PageSpy 做线上偶发性异常调试,在前端开发中经常会遇到自己测试没问题，但是客户那边有问题。这种问题调试起来往往都会比较繁琐。
通过 PageSpy 可以轻松的看到客户那边的报错信息，可以更方便有效的进行问题的定位。
为了方便管理和排查，我们做了如下参数约定：
# title 作为调试端房间列表显示的标识 # project 为所接入项目的标识 new PageSpy({title: &amp;#39;user01&amp;#39;, project: &amp;#39;test&amp;#39;}) 此外 PageSpy 的引入也是动态加载的，我们在页面的某个位置加入触发机制，当客户出现问题时，我们会告知客户触发加载 PageSpy 复现 Bug， 这时我们的前端工程师就可以在房间列表中查看客户那边的具体报错，辅助前端工程师解决问题。
我们可以看到 PageSpy 还有日志回放的功能。如果引入这个能力，客户只需要加载出 PageSpy ，并触发 Bug 点击上传日志即可。前端工程师可以根据客户上传的日志，通过日志回放看到具体的报错。这样就把问题的反馈变成了异步的形式，无需前端工程师和客户都在线。
PageSpy 日志回放，目前只支持 WEB 端，我们业务主要以小程序为主，且可以比较方便的找到客户帮忙排查问题，因此并没有使用这个功能。</description>
    </item>
    
    <item>
      <title>前端远程调试利器 PageSpy</title>
      <link>https://www.yunxicoding.top/post/web/debug-tool/</link>
      <pubDate>Thu, 14 Mar 2024 13:06:51 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/debug-tool/</guid>
      <description>PageSpy 是由货拉拉一款用来远程调试的工具。可以让你像操作本地 chrome 控制台一样，调试远程页面代码。 PageSpy SDK 支持如下：
Repo Type Status page-spy-types Common types Done page-spy-browser Web sdk Done page-spy-wechat Wechat sdk Done page-spy-alipay Alipay sdk Done page-spy-uniapp uniApp sdk Done page-spy-taro Taro sdk WIP page-spy-rn React Native sdk WIP PageSpy 存在两个端:
调试端: 用于开发者查看调试信息 客户端：客户端用于搜集异常数据传输到调试端。
PageSpy 应用场景如下：
远程工作场景下，测试人员反馈程序问题，只需要开启 PageSpy 重现问题步骤，开发者就可以看到具体的报错原因，避免了很多的沟通。 线上一些兼容性问题，可以帮用户开启 PageSpy ，可以快速定位到问题。 PageSpy 模块间的依赖关系和交互示意图： 调试端示意图 控制台： 网络请求： 存储:
调试端安装 Docker docker run -d --restart=always -p 6752:6752 --name=&amp;#34;pageSpy&amp;#34; ghcr.io/huolalatech/page-spy-web:latest Node yarn global add @huolala-tech/page-spy-api@latest # if you use npm npm install -g @huolala-tech/page-spy-api@latest 调试端配置 Nginx 配置 将 PageSpy 作为一个完整项目部署：</description>
    </item>
    
    <item>
      <title>Go CI/CD 实践</title>
      <link>https://www.yunxicoding.top/post/ci-cd/go/</link>
      <pubDate>Thu, 01 Feb 2024 14:28:01 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ci-cd/go/</guid>
      <description>最近用 Go 开发项目，本地发开和调试起来都非常方便，当到了接口对接阶段就出现了问题。
CI/CD 中文为持续集成/持续部署是敏捷开发的重要一环，有了 CI/CD 你可以快速的构建出 Feature/Fix 环境，加快版本的上线节奏。
本次的设计目标是：让开发者只需要提交代码即可，由 Gitlab 去执行代码构建和代码部署的能力，此外我还需要在部署阶段保证服务的正常可用。
我将 Gitlab 流水线 (Pipelines) 设置了两个阶段：
构建：编译出 Go 的可执行文件 部署：完成线上的部署工作 在部署阶段, Runner 会保证原有接口的正常的情况下：
用最新的可执行文件启动一个临时服务，并修改 nignix 反向代理，将所有的新请求代理到临时服务 停掉并升级老服务 把 Nginx 反向代理代理到新的正是服务，关闭掉临时服务。 具体 .gitlab-ci.yml 内容如下：
stages: - build - deploy variables: GOMODCACHE: /project-path/mod build: image: golang:1.21 stage: build cache: # 缓存 paths: - mod script: - touch ./config/config.yml # 编译你的 Go 程序 - export GO111MODULE=on - export GOPROXY=https://goproxy.cn - go mod download - go build -ldflags &amp;#34;-linkmode external -extldflags -static -s -w&amp;#34; -o main .</description>
    </item>
    
    <item>
      <title>go 语言实现轻量级队列事件</title>
      <link>https://www.yunxicoding.top/post/golang/queue-event/</link>
      <pubDate>Wed, 31 Jan 2024 11:51:25 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/queue-event/</guid>
      <description>最近再开展整合系统的工作，将原有系统中的共用逻辑抽离出来形成一个中台系统，根据业务开展形式的不同，划分出各个子系统。
在开展的过程中，遇到一种情况，在一些场景下子系统需要根据中台系统的一些操作去初始化自身的业务数据。
我们准备用事件（ Event ） 的形式来解决这个问题，子系统监听中台系统发布的一些事件，当中台系统进行相关操作时，触发事件通知监听中的子系统。
我们的中台系统 （ main system ） 是用 go 语言开发的，事件的触发是通过队列的形式发送给 event hadle 然后由它去发送事件通知到各个子系统。
在一些其他语言中，队列的消费一般都是启动一个进程来监听处理。而 go 有协程，可以用更轻量的协程来处理队列消费，这样以来有一个直接的好处就是服务启动了，队列的消费者就跟着启动了，不需要单独维护一个进程的启停。
当然要实现上述能力，有几个问题需要解决：
协程消费者如果崩溃了，不能影响主进程的运行
协程异常要能重新拉起一个新的消费者，保证协程消费者能一直运行。
崩溃隔离 消费者有自己的逻辑，在逻辑处理中有可能会出现 panic，我们需要消费者的 panic 限制在协程里，以避免协程崩溃影响主进程。
Go 有 recover 机制，可以让你捕获 panic 并且限制 panic 不在向上蔓延。代码如下：
go (func() { defer func() { if err := recover(); err != nil { fmt.Println(&amp;#34;捕获到 panic:&amp;#34;, err) return } }() err := EventHandle() if err != nil { Logger.Error(&amp;#34;event 处理监听失败&amp;#34;, err) } })() 消费者协程保活 消费者业务逻辑在协程里，可能在一些场景（panic 或其他不可知情况）下意外退出，我们需要保证能够重新拉起消费者协程，从而保证整个事件逻辑的闭环。</description>
    </item>
    
    <item>
      <title>laravel docker-php 运行环境搭建好了</title>
      <link>https://www.yunxicoding.top/post/docker/apt-update-error/</link>
      <pubDate>Mon, 22 Jan 2024 10:20:20 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/apt-update-error/</guid>
      <description>Laravel 是 PHP 里非常重要的一个框架，对于一个 PHPer 来讲，就算你没有用过 Laravel 也一定听说过它。
同事用 Laravel 开发了一个项目，需要部署一个测试环境，由于服务器的 PHP 环境不满足他项目的要求，所以需要多加一个 PHP 环境。
为了减少对原有系统的影响和更快的安装，我们决定用 Docker 来部署新项目的 PHP 环境。
本以为整个流程会无比的丝滑，但是在推进的过程中遇到了一些问题，也就有了这篇文章记录一下当时的经历，同时也为后续遇到类似问题的同仁提供解决思路。
整个部署方案如下： Dockerfile 很快就编写好了，本地测试能够成功的构建出 Docker Image。到此一切都很顺利，后续只需要把 Dockerfile 上传到服务器在服务器上把镜像构建出来，用 Nginx 解析一下就大功告成了。
当把代码上传到服务器，开始构建 Docker Image 时，问题出现了。在执行 apt update 的时候出现了下面的错误。
The following signatures couldn&amp;#39;t be verified because the public key is not available: NO_PUBKEY 0E98404D386FA1D9 NO_PUBKEY 6ED0E7B82643E131 本地构建好好的，怎么上线就出现问题了呢？简单的用搜索引擎搜索了一下，找了几篇带有&amp;quot;亲测&amp;quot;字样的文章进行验证，却都失败了。
后来我想既然这样问问 GPT 吧，看看它有没有什么高见。GPT 告诉我时因为服务器证书问题导致的，这里我就产生了一个疑问，我构建 Dcoker Image 执行 apt update 应该是在 Docker 环境里执行的呀，为什么会跟服务器证书有关系呢？
于是我继续追问，它给出了自己的解释，让我开始质疑自己了。
好的，它让我按照它的方案解决，我就试试吧，不出所料的失败了。到此我决定放弃对 GPT 的依赖，再次尝试用引擎来解决问题。</description>
    </item>
    
    <item>
      <title>如何构建 php-fpm 镜像</title>
      <link>https://www.yunxicoding.top/post/docker/php-fpm/</link>
      <pubDate>Fri, 17 Nov 2023 13:21:27 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/php-fpm/</guid>
      <description>在实际的项目部署中，我们有时候会遇到在一个服务器上部署多个 php 环境的情况。遇到这种情况，我们可以在同一物理机上装多个 php-fpm 监听不同端口，Nginx 根据不同的 Vhost 去找不同的 php-fpm 解析。
上述方法虽然可以，但是在项目管理上并不是很友好，比如我需要在某个项目执行 PHP 脚本，那还需要写 /path/php script.php 这样的话就需要每个维护项目的人都十分清楚，哪个项目应该用哪个版本的 PHP 去执行相应的脚本。
其次，如果某天需要把某个项目迁移到其他服务器了，我们还需要在新机器上安装 PHP 环境。
如果用 docker 进行部署，就不会有上面的问题，我们只需要写一次 Dockfile 可以在任何服务器上构建出相同的 php-fpm 环境，从而让迁移更简单，如果需要执行脚本，只需要进入对应的容器，直接执行 php script.php 即可。
php-fpm 官方镜像介绍 在 [php Tags | Docker Hub](https://hub.docker.com/_/php/tags 找到你所需的基础镜像的 tag
扩展安装 Docker PHP 安装有三种方式
Core Extensions：
FROM php:8.2-fpm RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \ libfreetype-dev \ libjpeg62-turbo-dev \ libpng-dev \ &amp;amp;&amp;amp; docker-php-ext-configure gd --with-freetype --with-jpeg \ &amp;amp;&amp;amp; docker-php-ext-install -j$(nproc) gd PECL extensions :</description>
    </item>
    
    <item>
      <title>Dockerfile 介绍</title>
      <link>https://www.yunxicoding.top/post/docker/dockerfile/</link>
      <pubDate>Fri, 17 Nov 2023 13:19:31 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/dockerfile/</guid>
      <description>Dockerfile 是用于构建镜像的脚本文件。在某些情况下，需要在基础镜像上加装一些软件，就需要使用 Dockerfile 来构建出自己所需的镜像。
指令介绍 指定基础镜像 ( FROM ) # 不指定版本则默认拉去 latest FROM nginx # 指定版本 FROM nginx:1.25.3 执行命令 ( RUN ) FROM nginx Run apt-get update \ &amp;amp;&amp;amp; apt-get install -y --no-install-recommends git vim libpng-dev libjpeg-dev libfreetype6-dev 声明容器运行时监听的端口号（ EXPOSE ） FROM nginx Run apt-get update \ &amp;amp;&amp;amp; apt-get install -y --no-install-recommends git vim libpng-dev libjpeg-dev libfreetype6-dev EXPOSE 80 设置工作目录 ( WORKDIR ) FROM nginx Run apt-get update \ &amp;amp;&amp;amp; apt-get install -y --no-install-recommends git vim libpng-dev libjpeg-dev libfreetype6-dev EXPOSE 80 WORKDIR /var/www 复制文件到容器目录 ( COPY ) FROM nginx Run apt-get update \ &amp;amp;&amp;amp; apt-get install -y --no-install-recommends git vim libpng-dev libjpeg-dev libfreetype6-dev WORKDIR /www COPY .</description>
    </item>
    
    <item>
      <title>docker 入门介绍</title>
      <link>https://www.yunxicoding.top/post/docker/101/</link>
      <pubDate>Fri, 17 Nov 2023 13:17:47 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/101/</guid>
      <description>Docker 可以理解为一个轻量级虚拟机，你可以在一台物理机上，装 N 个系统，每个系统部署不同的服务。
Docker 有几个非常重要的概念：镜像、仓库、容器。
镜像：可以理解为操作系统的系统盘，用于给容器安装系统用的，你也可以基于别人的镜像创建自己的镜像。
仓库：用于存储镜像的仓库
容器：相当于虚拟出来的一台台主机，他有存储、网络、系统等关键要素。你可以在容器里部署任何你想要部署的服务。
镜像操作 你可以创建 ( docker build )、删除 ( docker rmi )、下载 ( docker pull ) 镜像
设置镜像源 Docker 官方镜像仓库，在国内下载比较慢，可以通过国内的镜像源来加速镜像下载。本文介绍 Windows 通过 Docker Desktop 换源，如果你是其他系统，可以自行搜索。
复制下面信息到 Docker Desktop 设置
&amp;#34;registry-mirrors&amp;#34;: [ &amp;#34;https://hub-mirror.c.163.com&amp;#34;, &amp;#34;https://mirror.baidubce.com&amp;#34; ] 创建镜像 ( docker build ) 前提是要有构建镜像所需的 Dockerfile，关于 Dockerfile 后面会有专门的文章进行讲解。
docker build -t image_name . 下载镜像 ( docker pull ) 这里就会通过仓库下载镜像到本机。
docker pull ubuntu 删除镜像 ( docker rmi )
docker rmi image_name 容器操作 容器操作分为：启动（ docker run ），停止 ( docker stop )，删除 ( docker del )，执行 ( exec )、查看日志（docker logs）。</description>
    </item>
    
    <item>
      <title>SOLID-开闭原则</title>
      <link>https://www.yunxicoding.top/post/design-patterns/ocp/</link>
      <pubDate>Sun, 20 Aug 2023 21:35:53 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/design-patterns/ocp/</guid>
      <description>开闭原则-对扩展开放，对修改关闭。
开闭原则要求在扩展新功能时应当避免对旧代码的改动。但现实的开发中，新增功能多数情况下都要涉及到旧代码的改动，这主要是因为在业务刚开始推进的时候并不能很清晰的看出业务的拓展方向，因此通常情况下都不会进行预留扩展的设计（避免过度设计）。
当扩展需求出现时，就是开始重构原有代码的时机，而开闭原则就是指导重构的主要思想，在重构过程中，应将此次扩展的业务方向做一个通用方案的处理，保证下次再出现同类型的扩展可以做到不改动或少改动旧代码。
依据开闭原则做架构设计时，我们应该清楚要保护什么，可以通过分层来决定保护级别，最高层的保护级别最高，最低层的保护级别最低。低层级的改动不会影响高层。
层级的划分可以根据业务来划分，核心业务逻辑应该是最高等级，其他层级根据与核心业务的关联程度依次向下划分等级。这样的划分逻辑我们能够保证核心业务规则不会因为其他相关模块的改动而受影响。
接下来通过《架构整洁之道》(clean architecture) 中的一张图来解释一下分层设计 图 8.2&lt; I &gt; 表示接口，&lt; DS &gt; (data struct) 表示数据类，数据类的特性是第一次访问的时候会创建类，后续则是对该实体的读操作（实体类特性也是如此）。空心箭头表示对应接口的实现，实心箭头表示依赖关系， A-&gt;B 则是 A 依赖 B，A 知道 B 的存在，B 对 A 一无所知。正是由于依赖的这种特性，我们可以把被保护的代码，写到被依赖的类中，这样被依赖的类不会以为依赖方代码的改动而受影响。
上图中：Interactor &amp;gt; (database) &amp;gt; Controller &amp;gt; Presenter
关于图 8.2 调用链路如下： 这种设计 Controller、Presenter、Database 任何模块代码的改动，都不会影响 Interactor ，从而达到了对 Interactor 代码的保护，如果某天对 Presenter 中的 PDF View 进行改动出现了 bug，仅仅印象 PDF View 这一个功能，把 bug 进行最大程度的隔离，实现功能的稳定。
总结 开闭原则保证了程序的扩展性，同时用分层的思想设计架构，通过依赖保护核心业务逻辑，避免核心逻辑的频繁改动，隔离低层代码的改动影响高层。
如果你对架构设局同样感兴趣，推荐给你一本书《架构整洁之道》(clean architecture) 。这本书对架构设计的讲解十分深刻，相信你也一定会有所收获。</description>
    </item>
    
    <item>
      <title>从面向接口编程看标准化问题</title>
      <link>https://www.yunxicoding.top/post/think/interface-oriented-program/</link>
      <pubDate>Mon, 07 Aug 2023 20:52:11 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/interface-oriented-program/</guid>
      <description>面向接口编程： 当一个实体依赖另一个实体时，不依赖实体而是依赖实体的抽象。
比如说生产一个电灯，电灯需要一个开关来控制电灯的点亮和关闭，如果电灯依赖某个具体的开关,，比如电灯出场内置开关依赖了 A 品牌，当有一天买电灯的客户家里是 B 品牌的开关，那么电灯就没办法使用了。但是如果我依赖的是开关的抽象接口，它提供打开和关闭两个行为给电灯，电灯根据打开和关闭的行为去控制电灯的点亮和关闭。
这种设计可以让生产的电灯适应市面上的所有开关，那怕未来实现了脑机接口的开关，只要这个它遵循开关的抽象提供打开和关闭两种行为，这款电灯就能受这种新开关的控制。
从上面例子上可以看出，所谓的接口是依赖双方提前的一种约定，依赖方按照这种约定去调用，被依赖方按照这种约定去实现，这种约定其实就是标准化，调用方按照约定去实现功能，使用方按照约定去使用功能。
标准化的好处 从上面的例子中可以看出，标准化带来最大的好处就是复用，你可以任意的生产各种各样的开关，只要是按照标准制定的，我的电灯都是可以使用的。
其次标准化会带来效率的提升，现实中交通规则就是一个很好的例子，交规就是一个标准, 只要是在路上的走的，都受到交规的约束，而正是有了这种约束我们才有了更高效的通勤，绿灯了可以走，红灯了，就停下来等待，我们对路上所有的车辆和行人的轨迹都有预期，从而提升通行效率。
可以设想一下没有交规，是一个什么样的场景，大概率会出现所有车辆堵在了路口，整个路口堵的水泄不通，最后整体通行效率变得无比低下。
标准化还会带来工具化，再拿生活中常见的交通为例，有了完善的交通规则，后续就可以生产适用这种规则的自动驾驶车辆，这将大大改善人们的出行效率。
标准化在工作中的用处 标准化在工作中也有着十分重要的作用，如今的工作大多都会有工种之间的划分，工种和工种之间就存在着依赖关系，上一个工种的工作要到什么程度，下一个工种应该怎么衔接，这就是一个依赖，而在这两个工种之间就需要一个明确的约定。
有了这种约定，可以减少很多不必要的沟通，不需要去关心上个工种的实现细节，只需要双方按照约定各自生产，在产品成型阶段把各个工种生产出的设备进行组装即可。
当公司来了新人的时候，他也不需要重新去每个环节去了解其他工种的产品特性，只需要去了解约定的标准化制度即可投入到生产中。
标准化的问题 标准化也并非都是好处，它在设计阶段需要投入相当的人力去设计，并且需要在后续的生产阶段通过实践中得到的反馈，去不断的完善标准。
如果标准设计的不好，频繁的变更标准，将会导致所有依赖的标准方都要进行变动，这种变动的成本是相当高的。
因此在标准设计的时候要进行必要的论证和演练，确保标准没有明显的问题，最大限度的减少标准化带来的成本。
总结 标准化相当于在一个有限范围内设计了一个小型的生态系统，标准化制定了基本的流程和规则，生态系统的其他元素按照这种流程和规则去演化出标准化所期望达成的效果。
标准化的制定是有难度的，有些时候仅从设计阶段，无法看到最终的结果，这时可以通过抓大放小的形式去解决。刨除细节，去设计整体框架，保证大方向的正确，在标准化实践的过程中去不断的丰富细节，完善标准，从而让标准化实现最终的落地。
可能某些情况下理想下的标准化永远也无法达成，但这不能成为放弃标准化的理由，不能因为我们理想是得到 100 分的好处，因为我们拿不到 100 分就放弃。拿不到 100 分，能拿 60 分也比一分都没有要好一些，你觉得呢？</description>
    </item>
    
    <item>
      <title>SOLID-单一职责原则</title>
      <link>https://www.yunxicoding.top/post/design-patterns/single-responsibility/</link>
      <pubDate>Wed, 19 Jul 2023 21:58:04 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/design-patterns/single-responsibility/</guid>
      <description>单一职责原则：一个模块的功能只能因为一个原因进行修改。
单一职责介绍 我们假设一个场景：
有一个动物类，它会呼吸空气，用一个类描述动物呼吸这个场景：
class Animal{ public function breathe(string $animal){ echo $animal . &amp;#34;呼吸空气&amp;#34;; } } $animal = new Animal(); $animal-&amp;gt;breathe(&amp;#34;牛&amp;#34;); $animal-&amp;gt;breathe(&amp;#34;羊&amp;#34;); $animal-&amp;gt;breathe(&amp;#34;猪&amp;#34;); 当有一天我们发现并不是所有的动物都呼吸空气，例如水生的动物他们呼吸的是水，我们就需要进行如下的代码改动。
class Animal{ public function breathe(string $animal,string $type){ if ($type == &amp;#34;terrestrial&amp;#34;) { echo $animal . &amp;#34;呼吸空气&amp;#34;; } else { echo $animal . &amp;#34;呼吸水&amp;#34;; } } } $animal = new Animal(); $animal-&amp;gt;breathe(&amp;#34;牛&amp;#34;,&amp;#34;terrestrial&amp;#34;); $animal-&amp;gt;breathe(&amp;#34;羊&amp;#34;,&amp;#34;terrestrial&amp;#34;); $animal-&amp;gt;breathe(&amp;#34;猪&amp;#34;,&amp;#34;terrestrial&amp;#34;); $animal-&amp;gt;breathe(&amp;#34;鱼&amp;#34;,&amp;#34;aquatic&amp;#34;); 上述代码的改动就违反了单一原则，breathe 方法会因为两个因素造成修改，分别是 terrestrial 和 aquatic 。
这种违反会带来什么问题，最直观的问题就是会更容易造成 bug, 比如我们 terrestrial 需要增加新的功能，在些新功能的时候引入了一些 bug ，这将导致 aquatic 的代码块也无法执行，具体例子如下：</description>
    </item>
    
    <item>
      <title>为什么我们需要一个知识库</title>
      <link>https://www.yunxicoding.top/post/think/knowledge-base/</link>
      <pubDate>Tue, 20 Jun 2023 12:37:40 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/knowledge-base/</guid>
      <description>知识库是什么？简单来说知识库是一系列文档的集合。文档是沉淀知识的载体，而存放知识的地方，就叫做知识库。
我想大多数程序员都和我一样是不爱写文档的，在这些年的开发过程中，我逐渐认识到写文档的重要性。因此才有了这篇文章，向你介绍一下为什么我们需要一个知识库。
知识库的作用 不知道你有没有这种困扰：
场景一：
某天你需要在某个同事开发的功能基础上拓展一些新的功能，但是你不清楚这位同事开发的功 能是怎么运行的。你想要了解程序的具体内容，于是你找同事咨询，恰巧这位同事有事请假了，于是你只能硬着头皮从代码层面去阅读理解，去梳理原有程序的实现逻辑，遇到某些复杂部分，你可能需要格外的小心，重读多遍，以便于能正确理解实现思想&amp;hellip;
如果在你看代码前，你突然得到一个消息，这位同事之前写过关于这部分功能的实现文档，你心头会不会突然涌现出一丝暖意，嘴角不自觉的上扬呢？
场景二：
你实现了一个核心业务，它被很多的其他的业务方关联，这些业务方在实现自己的业务之前都会跑来找你了解你这部分业务的实现情况。你每次都能耐心详细的把自己的业务讲明白，也很好的帮助了业务方调用你的业务。
随着业务的发展，会有新的业务实现人员发现自己的业务依赖你所实现的业务，于是他再一次找 到了你说：我需要你的帮助&amp;hellip;
以上种种让你觉得深陷泥沼，无法自拔。这时如果你有一份文档详细的介绍了你所实现业务的逻辑，它去帮你解答他人的困惑，你是否觉得它就像天使一样，拯救你于水火呢？
正如上述场景所描述的那样，知识库能够解决团队内的信息同步问题，当有需要的时候知识库能够帮助团队的成员更快的了解相关信息。
对于一些高频出现的问题，知识库可以作为你的助手一样，帮助你向有需要的人进行解答，解放出你的时间和精力，专注更重要的事情。
同时知识库还能作为你的外脑，帮你去记忆某些重要的事情。人的记忆是不稳定的，而计算机却十分擅长记忆，把需要长期记忆的通过知识库交予计算机，让大脑去做它擅长的事情。
如何选择知识库 知识库作为一个文档存储载体，它的选择是多种多样的，可以根据不同的需求做出不同的选择。
个人知识库 如果个人使用，你可以用笔记软件去组织自己的知识。它最好要有一下特性：
多端支持（IOS，Android, Windows, Linux） 多端之间要有相互同步的能力 支持 Mrkdown 支持双链 上述特性中，多端支持和多端同步是我认为必须要有的，它可以让你随时随地记录，并且随时随地调取你的文档。
Markdown 和双链 是选用功能，Markdown 是一个通用语法，如果你用 Markdown 写的文档可以很平滑的迁移到其他平台。双链可以帮你更好的组织文档之间的关系，可以让知识系统由一个个独立的点串联成知识网络。
团队知识库 团队知识库的选择，最重要的一个原则是：如何让团队以最小的成本接入。可以考虑从团队现有工具中找到相关的知识库进行使用。
所选知识库要有能对外分享的能力，有时候有些文档是需要让客户开放的。
推荐两个知识库软件：飞书，语雀。两款软件使用感受都不错，语雀的使用体验要更好一些，不过有些功能需要开通会员才能使用，比如把文档分享到互联网的功能。
飞书的使用体验比语雀稍差一些，最大的优势是它免费的功能能满足一般的使用需求。
如何使用知识库 知识库根据使用用途一般分为对内和对外两部分。对内可以根据知识库的敏感程度设置为：是整企业可见/部门可见/指定人可见。对外可以完全是对互联网开放，做开放平台开放的文档，或者产品使用手册等都可以设置为对互联网开放。
如何写一份功能文档 以功能文档为例，我认为文档应该在实现功能之前就要写了，这么做的目的是在开发功能之前梳理开发思路，形成一个框架。这个阶段主要是考虑全局，不需要深入细节。
如果梳理过程中有需要讨论的地方，也可以用文档来作为基础和参与讨论人员做思想对齐，使接下来的讨论更加的聚焦在问题本身。
框架梳理完成后，就可以进入功能开发了，功能开发阶段可以对关键业务部分做一些流程梳理，也不需要过分追求细节，能够明确的表达思想即可。有了这份文档在方向上的指引，也有助于在开发阶段少走弯路。
在功能开发完成后对文档进行补充，补充一些细节的说明，让整个文档更加的丰富，表达更为通畅和清晰。
什么情况下需要写文档 写文档也是需要付出成本的，因此在写文档之前需要考虑这个文档它的使用收益是否大于成本。因此写文档可以由一下两种策略：降低写作成本 和 提高写作收益。
降低写作成本：可以不用在意文档的完整程度，可以把这类文档作为一个提示文档，它只需要能够辅助我们想起来如何去找到答案即可，不追求写作的表达和完整性。
提高写作收益：对于一个问题多次被提及到，我们可以认为此问题形成文档的收益在提升，可以考虑输出文档去解决此类问题。
定期维护文档 文档也有生命周期，随着业务的发展，文档也需要进行相应的调整。这个维护工作应该是文档作者和所有使用的人，也就是说不仅仅写文档的人要维护文档，阅读文档的人也可以维护文档。
阅读人可以为文档补充案例。或者对于一些有疑问的地方得到解答后，可以细化到文档中，帮助后面的阅读者更好的理解文档。
总结 本文大概讲解了一些知识库的价值和使用方式，目的是在你的心中埋下一颗种子，它需要时间去发芽和成长。
知识库的使用并不是那么容易，肯定会遇到各种各样的问题，要有直面并解决的勇气。千里之台始于垒土。 如果你认为这是一件正确的事情，就坚持下去，潜心耕耘，静待花开。
扩展阅读 双链笔记是伪概念？聊聊双链的核心优势——知识管理 - 少数派 (sspai.com)</description>
    </item>
    
    <item>
      <title>简单的艺术</title>
      <link>https://www.yunxicoding.top/post/think/simple-art/</link>
      <pubDate>Thu, 08 Jun 2023 09:00:24 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/simple-art/</guid>
      <description>最近读《微信背后的产品观》很受启发，张小龙在文中阐述了他的产品哲学，以及微信产品的发展过程中的一些思考与体会。对于好的产品，共性之一就是他们都足够的简单。
微信发展了这么多年，他已经成为一个非常重的应用，里面的功能也是非常的复杂，但是这些复杂没有暴露给用户，对于大多数人，常用的功能占 1% 都不到，从此也能看出，微信的克制。
微信不会在你使用产品的时候打扰你。比如它不会在更新后，给你弹个操作引导，告诉你它更新了什么东西。当你在使用微信的时候，觉得这个地方应该可以进行某个操作，当你按照自己的想法去操作的时候，你会发现你想要的功能就在哪里。微信通过符合直觉的方式去展示他的功能，而不是去教育用户让用户去适应它。
像上述例子比比皆是，它贯彻微信的整个生命周期。如何能像微信一样，做一个简单且有用的产品呢？
你有没有做伟大产品的决心 你是想做一个伟大的产品，还是想当一天和尚撞一天钟。是能否做出伟大产品的关键。心态在其中是起到无可比拟的作用的。
心态是决定你能把事情做到什么程度，它会潜移默化的影响着你。也时刻影响着你的产出质量。
你可以因为没有那么热爱而离开，但最好不要敷衍。你的时间不会重来，把时间浪费在低质量的产出是相当不明智的。
所以去全力以赴把产品做到当下的最好，不辜负团队，也不辜负自己的生命。这是你对自己生命最好的交代。
别用战术上的勤奋掩盖战略上的懒惰 做产品的时候，我们可能会从用户或者运营那里得到很多的想法，更有甚者会直接告诉你我需要一个什么样的功能，你帮我做出来就好。
如果面对这种情况，我们努力去完成每一个人的需求，勤奋的让自己都心生怜悯，这种做法也是十分不可取的。
做产品要解决的东西是本质，而非现象。现象是千变万化的，今天可能是 A 明天可能是 B，但是他们可能都是基于某个基础需求所衍生出来的。如果只是去解决现象，那不仅会辛苦而且收获甚微。思考和找到其本质，才是一劳永逸的解决之道。
沉浸其中 深入到产品中, 了解你产品是什么？解决了什么问题？解决了谁的问题？ 你得清楚的了解它，才能在新需求来时知道如何安置。才能找到一个支点，用最小的代价撬起万钧之力。
所以深入到你的产品中，深入到你的客户中，去感受他们给出的回应。
总结 做产品也是一定程度上反人性的，有些时候我们凭借直觉给出方案，但这种方案仅仅是满足需求而已，它既不好用，也不简单。这样的事情做的多了，产品就会膨胀和混乱，最终导致复杂且晦涩，用户更没办法理解和使用。
所以我们需要适时的提醒自己，这样做对吗？它足够简单吗？它能使我的客户更方便吗？
去让客户自然的使用你的产品，而不是教他如何使用你的产品。</description>
    </item>
    
    <item>
      <title>Fiddler 实现手机抓包</title>
      <link>https://www.yunxicoding.top/post/network/fiddler/</link>
      <pubDate>Thu, 08 Jun 2023 08:59:06 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/network/fiddler/</guid>
      <description>Fiddler 抓包原理并复杂，Fiddler 软件会在 PC 上起一个代理的服务，手机连上 Fiddler 代理服务后，手机访问某个网址时，其实时发送给 Fiddler, Fiddler 去请求对应的地址，把响应结果在返回给手机显示，这样 Fillder 就可以在软件里做一个记录从而显示出手机访问的所有请求信息了（如下图所示）。
知道原理后，我们要抓包也就十分明确了，我们需要 Fillder 软件，一部手机即可。
Fiddler 下载地址 安装可以自行百度如何安装。
Fiddler 配置 配置 https 请求抓包 配置端口号 此步非必要，如果本机 8888 端口，未被占用，可以用默认的 8888 跳过此步
完成上述配置后，重启 Fiddler 软件。在软件右上角有个 Online 鼠标放在上面就可以看到本机 ip 地址，这个 IP 我们叫做【代理 IP】需要记住，手机配置的时候需要用到。
手机配置（IOS） 如果你的手机是安卓，可以 Google 安卓是如何设置 wifi 代理的，设置上即可，安卓安装证书更加的简单，只要点击证书安装上即可，不需要其他的额外操作。
配置代理 前置条件：手机连接的 WIFI 需要和电脑是同一局域网。
配置 WIFI 代理：设置-&amp;gt;无线局域网-&amp;gt;点击连接的 WIFI ，拉到最底部点击【配置代理】选择【手动】。
服务器：Fiddler 软件右上角 Online 显示的 IP 即【代理 IP】
端口号： 8888 （如果自己设置了，就改成自己设置的）
下载证书 这里需要下载 TLS 证书，不然是无法抓到 HTTPS 请求内容的。用手机浏览器访问 【代理 IP】</description>
    </item>
    
    <item>
      <title>开发需要理解需求</title>
      <link>https://www.yunxicoding.top/post/think/develop_think/</link>
      <pubDate>Wed, 19 Apr 2023 17:05:18 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/develop_think/</guid>
      <description>由运营同事提出的一个问题引发了我对开发需要理解需求的思考。为了业务的保密，我把问题简化为：某个用户下的数据能否迁移到另一个用户下。
仅从技术的角度，这个迁移肯定时可以迁移的。如果回答可以，并帮运营同事去做了，看上去也没有什么问题。
回到运营同事提的需求本身，我们看一下，我要迁移一个人所属的数据到另一个人下面，我要找到所有被迁移人的数据，这需要涉及到系统中各个功能产生的用户数据，本身涉及的表就会比较多，操作起来会也就会比较容易出错（可能会漏掉某些功能产生的数据），且通过运营同事的沟通，我认识到这种迁移操作可能会高频出现。
问题分析到这里，好像我们通过脚本就可以解决了，一次找到所有的表，写完脚本，后续的迁移只需要传入两个人的 ID，就可以把数据实现迁移了。
如果仅从当前看，上述想法没有任何问题，如果我们把时间再拉长一些，我们后续业务又开发了一个新的功能且此功能也会产生用户数据结果是怎么样的呢？
那我可能就需要在原来写的脚本里加上这个功能的迁移逻辑，那么这个迁移脚本的维护成本如下图所示：
作为一个开发来讲，这样的模型当然不是我们所希望的，我们都不想给系统添加了一个新功能还需要去维护和修改以前的功能，才能保证系统的平稳运行。
我意识到上述问题后，问了一下运营同事：&amp;ldquo;为什么会有这种迁移的需求？&amp;quot;，经过和运营同事的更进一步的交流后发现，运营同事要解决的问题其实可以通过其他方式解决，而且这种方式，在系统加入新功能后，不需要改动和维护任何的已开发功能，开发成本如下图所示：
到此即解决了运营同事的问题，也减少了开发的维护成本，可以说是一举两得。
在职业生涯中，我们不乏碰到一些人并不认真去理解需求，甚至会说出：“你直接告诉我怎么做”，这样的话，最中导致的解决可能就是：
没有按照需求实现功能 实现了需求的功能，但是影响到了其他已有的功能模块 实现了需求代码写的复杂、难懂。 往小了说会造成个人开发效率的下降，且 bug 相对较多，往大了说会对团的和公司带了负担。
因此在开发过程中，了解需求是很有必要的。有时候我们会与不同的人讨论问题，有时是运营，有时是产品，有时也可能是老板，但是作为开发，我们除了完成需求交付之外，我们也有责任让他们了解，他们所提方案的成本，以及该方案下会产生哪些潜在的风险。
clean architectrue 里面有一句话非常受启发分享给大家：
The goal of software architecture is to minimize the human resources required to build and maintain the required system
软件架构的目标是用最少的人力成本去构建和维护所需系统</description>
    </item>
    
    <item>
      <title>面对新技术的态度</title>
      <link>https://www.yunxicoding.top/post/think/attitude-of-new-technology/</link>
      <pubDate>Mon, 27 Feb 2023 10:20:52 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/attitude-of-new-technology/</guid>
      <description>最近看苹果无创检测血糖技术实现突破，专家说精度不足1的新闻，及前一段时间大火的 ChatGPT 都让我有一种感受，一项新技术的出现，总会出现两种人的声音，一种是及其看好，把其堪称为颠覆和突破，另一种则是觉得新技术各种瑕疵，无法替代现有方案。
两种看法都是相对来说片面的，技术是会迭代的，在迭代的过程中可能发展向第一种看法，打造出颠覆性的解决方案。也有可能迭代的过程中依然无法解决核心的问题，那么就可能永远无法颠覆现有解决方案。
飞机最初的发明思路，是按照鸟的飞行结构去设计的，但是鸟由于骨架轻等特点可以用自身的结构实现飞行，但飞机显然是不可以的，飞机的自重加上载重如果利用鸟的飞行结构去设计显然是无法飞行的。
我们回到刚刚发明飞机的那个时间节点去看，有人觉得飞机会颠覆出行，有人觉得飞机连飞都飞不起来，不可能替代现有出行方案，双方观点截然相反，但又有各自的道理。而从事后来看飞机显然成为现代出行必不可少的交通工具，但是如果飞机在设计的时候没有空气动力学的支持，飞机可能永远也无法飞起来，那么后者的观点也就成了对的。
既然两个观点都是片面的，应该如何看待一项新技术的产生？我认为我们应该用乐观的态度和发展的眼光看技术的突破，也就是更倾向于第一种观点了。
原因是任何技术的诞生之初都是不完美的，悲观的态度只能阻碍其发展，而乐观却不同，乐观能够更大程度的促进技术发展，即便是技术没有实现落地，至少也努力验证了其不可行性，也能为后续的技术的发展提供一个不可行案例，也是存在贡献的。悲观可能会导致直接放弃，对于技术发展而言是没有任何贡献的。
所以不管是对一项技术还是人生中在做的事情，我们都应该用积极的态度去面对，也许用尽各种努力最终还是失败，失败只是结果，但在努力的过程中是一定会有所收获。
从结果上看，努力尝试和直接放弃最终都是失败，但是收获上来看是完全不同的。
References 苹果Apple Watch将实现无创测血糖？专业人士：物理检测 精度不足 (10jqka.com.cn)&amp;#160;&amp;#x21a9;&amp;#xfe0e;</description>
    </item>
    
    <item>
      <title>如何避免代码的熵增</title>
      <link>https://www.yunxicoding.top/post/think/code_clean/</link>
      <pubDate>Mon, 20 Feb 2023 13:58:10 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/code_clean/</guid>
      <description>在开发中与别人交流或在程序员社区中，经常会有关于烂代码的讨论，并且为其起了一个打趣的名字”屎山“。
仅从名字上也不难看出，大家对于这种代码是十分的厌恶的。其实随着需求的变化和增加，项目不可避免的会变的混乱，在物理学里有个名词叫“熵增”。
熵增过程是一个自发的由有序向无序发展的过程
既然代码会膨胀，并且会变得混乱，如何做能够尽量的避免这种情况的发生呢？
核心办法就是主动做功，在规划、实施、复盘等各个阶段有意的去做治理，从而避免代码的混乱。
勾勒整体 在需求开发之前，要建立对需求的整体的认识，从整体拆分细节，从细节着手开发。这么做的主要目的就是对需求有一个全局的规划，确定需求边界，就不会再开发的时候迷失方向，导致整个需求的实现变得混乱，代码和思想都没有清晰的表达。
在整体确定，边界清晰的时候，还需要考虑一点：当前需求与已有功能的关系时怎么样的，如何能花费更小的成本将新增需求融入现有系统。
完成上面说的还有一步就是追问，此方案是否为最优方案，是否还存在更为简化更易于理解的放啊的方案，如果回答全部为是，那么关于整体这部分的考量也就到此结束了。
化繁为简 我对专业的定义是：可以将自己领域的复杂问题简单化的能力。把简单的事情做复杂是庸才，把复杂的事情做复杂是通才，把复杂的事情做简单是天才。
在程序开发中，以易读为第一要务。写代码的主要目的是给人阅读，其次才是机器执行。在开发中，应该以代码的可读性为最高优先级，其次才是性能或其他指标。
大道至简，做到简不容易，需要通过不断的练习与反思，才能窥其门径。
摒弃过度设计 好的程序设计应该是立足业务的基础上长出来的，在业务不是很明朗的时候，应该更专注于方案和代码的简单而不是过度设计。
业务不明朗的时候大多数的设计是无用的而且一些设计会带来代码复杂读的增加，虽然可以美其名余曰有更好的扩展性，没有用的扩展性等于没有扩展性。
好的设计应该是以对业务有深入理解后，通过重构的手段展现出来的，而不是在业务一开始就过分的考虑设计。
不断重构 重构的时机有三种：
对业务有更深入理解的时候
在原有功能扩展新功能的时候
已有功能存在设计缺陷的时候（性能，扩展性，易读性）
重构不应该等”有时间“的时候，”有时间“就等于没有时间。重构应该发生在发现问题的时候，发现问题就解决问题。
就像打扫卫生一样，每发现一个地方乱了，就顺手整理一下，虽然没有刻意的花时间去进行整理，但是整个房间依然会保持整洁。
应该缩小重构的粒度，增加重构的频率，使重构变得小而频繁。
总结 除了上述说的情况之外，保持学习也是很关键。思而不学则殆，通过学习他人项目治理的思想，来增加自身的理解，并在开发中不断的去验证、思考和改进，从而写出更简洁高效的代码。
纸上得来终觉浅，绝知此事要躬行。学习只能是觉知，关键在于实践，知行结合，才是真正把知识内化为己所用。希望本文能对你有所帮助，个人观点难免会有疏漏和偏颇，如果有不同观点，欢迎留言讨论。</description>
    </item>
    
    <item>
      <title>我用了近两天的时间,只为了写三行代码</title>
      <link>https://www.yunxicoding.top/post/think/debug-laravel-library/</link>
      <pubDate>Sat, 27 Aug 2022 08:59:31 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/think/debug-laravel-library/</guid>
      <description>事情发生在我们线上的一个遗留问题，问题大致情况是我们引入的一个库会导致服务在运行一段时间后出现死锁，并且会造成整个网站无法访问。
我们发现通过配置库的一些参数可以避免死锁的情况发生，但是会引发事务无法回滚的新问题。
由于后者相对故障等级更低一些，我们首先进行了故障降级，选择了优先保障服务运行。 后面则是排查为什么会新的配置会出现事务无法回滚，这个事情由我负责排查。
从部门同事那了解了问题的详细情况后我就开始着手进行排查。首先是对问题进行梳理把新引入的库与框架之间的关系以及整体的一个运行流程进行了梳理。
如果要对整个流程做详细的了解，需要阅读的代码量是十分庞大的，不仅需要阅读库，还需要对框架源码进行阅读。
就解决问题而言，我认为需要做粗略的流程梳理即可，通过对整个流程的一个大致了解，然后确定问题的具体发生位置，然后对具体位置进行详细了解。
在粗略梳理的过程中，我从新引入库开始着手，主要是由于库的代码量相对较少，阅读起来相对更快一些，且如果仅从库层面就能解决问题，也可以省略的框架代码的阅读。
对于库源码的了解我通过 wiki + 源码的方式，这样能够更省力的了解库的整个运行机制。
然而了解了库的运行机制后，我发现问题可能出现在库与框架的配合上。需要涉及到框架源码的阅读。
在开始阅读框架源码前，我决定在本地构建一个复现环境，通过对构建出的本地环境对源码进行 debug 调试。
经过反复的 debug 最终终于确定了问题出现的原因，是由于新库引入后，事务开启时新建一个数据库连接，而通过 ORM 进行数据库查询时会创建另一个数据库连接。这就导致事务和 SQL 的执行不是在同一个数据库连接中，也就造成了数据库事务无法生效的问题。
确定了问题的原因解决方案就好确定了，我们只需要保证事务和 SQL 执行在同一个连接中即可，而解决这个问题只写了三行代码，既保证了功能的完整，没有侵入库和框架进行底层源码的改造。
这次问题的排查让我想到了福特公司用一万美元画一条线的故事。
画一条线，1美元；知道在哪儿画线，9999美元。
这次的问题离不开部门同时的帮助，在排查的过程中和同事进行了 2-3 轮的沟通。不仅让我对问题有一个全面的认识，也几次在我遇到困惑时，给了我解决问题的灵感，包括在最后解决方案制定后的论证，都蕴含了整个团队的心血。
也希望这次的一个排查经过能够对看文章的你有一些启发，欢迎你分享你的感悟和思考。</description>
    </item>
    
    <item>
      <title>Casbin 入门</title>
      <link>https://www.yunxicoding.top/post/web/casbin/</link>
      <pubDate>Fri, 29 Jul 2022 21:40:31 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/casbin/</guid>
      <description>Casbin 是一个强大的、高效的开源访问控制框架，其权限管理机制支持多种访问控制模型。
通俗来讲就是一个权限管理的框架，把常见的权限管理集成在框架中，使用者只需要根据框架规范就可以很方便的实现相应的权限控制。
Casbin 支持的权限管理如下：
ACL (Access Control List, 访问控制列表) 具有 超级用户 的 ACL 没有用户的 ACL: 对于没有身份验证或用户登录的系统尤其有用。 没有资源的 ACL: 某些场景可能只针对资源的类型, 而不是单个资源, 诸如 write-article , read-log 等权限。 它不控制对特定文章或日志的访问。 RBAC (基于角色的访问控制) 支持资源角色的RBAC: 用户和资源可以同时具有角色 (或组)。 支持域/租户的RBAC: 用户可以为不同的域/租户设置不同的角色集。 ABAC (基于属性的访问控制): 支持利用 resource.Owner 这种语法糖获取元素的属性。 RESTful: 支持路径, 如 /res/* , /res/: id 和 HTTP 方法, 如 GET , POST , PUT , DELETE 拒绝优先: 支持允许和拒绝授权, 拒绝优先于允许。 优先级: 策略规则按照先后次序确定优先级，类似于防火墙规则。 Model 概念 Model 相当于框架的配置文件，如我想在系统里有可能实现 是ALC 或者 RBAC ，应该如何告诉 Casbin 框架，我实现的是那种类型的权限控制呢?</description>
    </item>
    
    <item>
      <title>golang 简洁架构介绍</title>
      <link>https://www.yunxicoding.top/post/golang/template/</link>
      <pubDate>Wed, 29 Jun 2022 16:48:36 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/template/</guid>
      <description>在 go 开发中，首先遇到的一个问题，应该怎么设计开发架构。一个好的架构可以在后续的项目开发中能够更好的使用项目的推进，同时能够让团队的配合更加的顺利。
一个简洁的架构应该具有如下几个特点：
不依赖框架：好的架构应当是独立的，不依赖框架可扩展的 不依赖 UI：UI 应该很容易修改，而不需要改业务部分代码 独立于数据库：不绑定数据库，可以在不改动业务代码的情况下，更换其他数据库 可测试的： 能够比较方便测试用例的编写 下面目录结构引自 《go 整洁模板》
├─cmd 应用入口│ └─app├─config├─docs // 存放文档├─internal│ ├─app│ ├─controller // 控制器│ │ ├─amqp_rpc│ │ └─http│ │ └─v1│ ├─entity // 实体层│ ├─middleware // 中间件│ └─usecase│ ├─repo // 数据库操作│ └─webapi // RESTful API├─migrations├─pkg //以被外部程序安全导入的包│ ├─crypto│ ├─httpresponse│ ├─httpserver│ ├─logger│ ├─mysql│ ├─postgres│ ├─rabbitmq│ └─redis 分层介绍 层级调用是通过外层-&amp;gt;内层，内层是不知道外层的存在的。在内层代码中，不应该引用外层声明的：变量、类、方法，结构体。</description>
    </item>
    
    <item>
      <title>golang 接口（interface）最佳实践</title>
      <link>https://www.yunxicoding.top/post/golang/interface-best-practice/</link>
      <pubDate>Sat, 11 Jun 2022 08:53:21 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/interface-best-practice/</guid>
      <description>接口定义 应该定义在接口所使用的包中，而非接口实现的包中。
以io包中的io.Reader/io.Wirter为例，接口定义在io包中，而他的实现bytes.Buffer、os.File等都是在不同包中。
接口实现应该返回具体类型 应该返回对应的结构体或指针，这样实现类如果扩展新的方法就可以直接添加，无需改动接口定义部分，更加灵活。
//为了方便演示，本实例代码未遵循「接口需要在使用包中声明」规范 package demo import &amp;#34;fmt&amp;#34; type Demo interface { show() error } type MyDemo struct { } //这里的返回值应该是 `MyDemo` 或 `*MyDemo` 而非 `Demo` func NewMyDemo() *MyDemo { return &amp;amp;MyDemo{} } func (md *MyDemo) show() error { fmt.Println(&amp;#34;show&amp;#34;) return nil } 接口应该什么时候定义 在当前模块有依赖外部服务的时候，这时候就会定义一个接口，来对外部的依赖资源进行抽象解耦，屏蔽接口的实现。相反，依赖的提供方在不知道使用方需求的时候，定义接口也就没什么意义。</description>
    </item>
    
    <item>
      <title>树莓派变身旁路由，扩展家中网络</title>
      <link>https://www.yunxicoding.top/post/net/openwrt-101/</link>
      <pubDate>Tue, 03 May 2022 16:23:04 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/net/openwrt-101/</guid>
      <description>由于家中网络覆盖存在死角，手中有一个树莓派，就准备刷入「openwrt」作为旁路由，扩展家中的无线网络。
整体思路如下，用树莓派的自带网口桥接主路由网络，然后通过树莓派无线网卡发射无线网络，从而达到扩展网络的目的。
如果你也正巧有类似的需求可以通过下面清单，准备所需材料：
树莓派
读卡器
TF 卡
台式机/笔记本（用于调试网络）
网线一根
下载镜像 镜像推荐：OpenWrt-Rpi ，这款镜像支持绝大多数的常见设备，并且镜像集成了很多很好用的功能，也免去了刷官方固件，后续自己折腾安装软件的过程。
进入固件下载 页面能够看到支持设备的列表，找到自己的设备，点击固件下载，进入你设备的固件下载页。
我这边是树莓派 3B 下载的为: immortalwrt-bcm27xx-bcm2710-rpi-3-ext4-factory.img.gz
到此镜像就已经下载好了，下面就是将下载好的镜像，写入树莓派的 TF 卡。
写入镜像 将 TF 卡插入读卡器，并将读卡器插入电脑上，镜像写入工具使用的为: Win32 Disk Imager 如果没有的话可以自行下载。打开 Win32DiskImager 软件，选择下载好的 「openwrt] 镜像和对应 TF 卡的盘符，点 Write 按钮开始写入镜像。
看到写入完成的提示后，将 TF 卡拔出插入树莓派，将准备的网线插入树莓派网口，并网线的另一端连接到电脑上，将树莓派开机。
openwrt 配置 接口配置 树莓派开机后，电脑端输入 192.168.1.1 可以看到路由器的登录页面。
镜像的默认密码为: password ,输入密码后进入系统后，点击「网络-接口」进行接口设置，系统默认已经创建好了 LAN 接口，点击 LAN 接口的修改，进行配置，具体配置如下：
传输协议：静态地址
IPv4 地址: 前三位与主路由地址相同，最后一位写一个内网中没有分配的地址即可。我这里为：192.168.1.100，需要注意的是此地址也是后续登录路由器管理界面的地址。
IPv4 子网掩码： 255.255.255.0
IPv4 网关： 主路由网关，我这里为 192.168.1.1
DNS 服务器1：114.114.114.114
DNS 服务器1：8.8.8.8
上述配置完成后，拉到页面底部点击 保存&amp;amp;应用 应用配置，配置应用成功后，重新登录路由器，此时再用192.</description>
    </item>
    
    <item>
      <title>NGINX与PHP-FPM 是如何通讯的</title>
      <link>https://www.yunxicoding.top/post/web/nginx-fpm/</link>
      <pubDate>Tue, 08 Mar 2022 10:29:47 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/nginx-fpm/</guid>
      <description>Nginx、CGI、FastCGI、php-fpm关系，介绍了 NGINX PHP-FPM FastCGI 之间的关系。
本问介绍一下 NGINX 与 PHP-FPM 是如何进行通讯的，NGINX 是如何将请求转发到 PHP-FPM，PHP-FPM 有事如何知道该区找哪个脚本执行相应的代码逻辑。
建立连接 NGINX 通过 fastcgi_pass 与 PHP-FPM 建立连接，fastcgi_pass 有两种方式进行配置
通过 TCP 连接 fastcgi_pass 127.0.0.1:9000; 优势：
将 PHP-FPM 与 NGINX 进行解耦，可以动态的扩展 PHP-FPM 的服务数量 劣势：
由于将服务暴露到外网，会带来一些安全相关的风险 通过 UNIX socket 连接 fastcgi_pass unix:/var/run/php-fpm.sock; 优势：
进程间通讯，效率更高 劣势
NGINX 和 PHP-FPM 必须在同一系统环境内 两种建立连接的方式都需要 PHP-FPM 配置进行配合，如果 NGINX 采用 TCP 连接，则 PHP-FPM 也需要进行 TCP 的监听，反之亦然。
传递参数 PHP 在处理业务的时候需要一些参数比如：用户的 quey 和 请求 URL&amp;hellip;，这些参数 NGINX 是如何传递给 PHP-FPM 的呢？</description>
    </item>
    
    <item>
      <title>安全传输层协议(TLS)介绍</title>
      <link>https://www.yunxicoding.top/post/web/tls/</link>
      <pubDate>Thu, 17 Feb 2022 13:38:40 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/tls/</guid>
      <description>TLS 介绍 TLS (和 SSL) 协议位于应用程序协议层和 TCP/IP 层之间，可以在其中保护应用程序数据并将其发送到传输层。 由于协议在应用程序层和传输层之间工作，因此 TLS 和 SSL 可以支持多个应用程序层协议。
TLS 和 SSL 假定使用面向连接的传输（通常为 TCP）， 可以预防如下风险：
消息篡改 消息拦截 消息伪造 TLS 握手 介绍 TLS 整个握手流程如下图所示
客户端请求(clinet hello) 这个阶段主要发送信息如下：
客户端支持的 TLS 版本号 客户端随机数Random 客户端支持的加密方法 客户端支持的压缩算法 服务端响应（server hello） 服务端支持的 TLS 版本号 服务端随机数Random 服务端支持的加密方法 服务端支持的压缩算法 服务端证书发送 服务端发送 server hello 后会发送证书，通常他们俩者在同一个网络包中，即同一个 TLS 记录层消息中。
证书验证（可选） 客户端拿到服务端证书，到证书办法机构的服务器（OCSP responder）验证证书是否可用
Client Key exchange 在此阶段客户端也会生成一个随机数，加上 clinet hello, server hello 生成的随机数一共三个随机数 作为 pre-master，发送给服务器，服务器端收到pre-master算出main master。而客户端当然也能自己通过pre-master算出main master。如此以来双方就算出了对称密钥。
客户端 finished 一个 Finished 消息一直会在一个 change cipher spec 消息后立即发送，以证明密钥交换和认证过程是成功的。一个 change cipher spec 消息必须在其它握手消息和结束消息之间被接收。</description>
    </item>
    
    <item>
      <title>从 IOS 进入 H5 页面过慢，理解 OSCP</title>
      <link>https://www.yunxicoding.top/post/web/ios-h5-slow/</link>
      <pubDate>Wed, 16 Feb 2022 10:33:50 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/ios-h5-slow/</guid>
      <description>问题分析 近期系统出现了部分 IOS 进入 H5 页面很慢的情况，而 Android 进入却正常。
在排除调程序和网络的问题后，最终把问题定位到 SSL 证书上。要想了解整个问题所在，需要从 TLS 协议开始说起
TLS 握手流程如下图所示
问题就出在客户验证证书这里，这里验证证书是通过 在线证书状态协议（OCSP），进行验证的。OCSP 验证原理也比较简单，就是拿证书信息到证书颁发机构的 OCSP 服务器做校验。
由于我们使用的是国外机构颁发的证书，因此验证证书的服务器也在国外，由于国内网络环境受限的因素，验证服务器无法访问，从而导致整个网站的访问卡在了证书校验这一步，无法正常的完成 TLS 握手。
找到了原因解决的方法也就迎刃而解了，我们把站点证书换成了国内机构颁发的，成功的解决了问题。
问题验证 在苹果官网找到了如下说明，佐证了上述猜想
对 TLS 证书信任状态的评估依照既定的行业标准（如 RFC 5280 所列）执行，且整合了新出现的标准，如 RFC 6962（证书透明度）。在 iOS 11 或更高版本以及 macOS 10.13 或更高版本中，Apple 设备会随撤销及受限证书的当前列表来进行定期更新。该列表由 Apple 信任的每个内建根证书颁发机构及其从属的 CA 签发者所发布的证书撤销清单 (CRL) 聚合而成，列表还可能根据 Apple 的要求酌情包括其他限制。每当使用网络 API 函数进行安全连接时都会使用此信息。如果需要单独列出的 CA 撤销证书太多，信任评估可能需要在线证书状态响应 (OCSP)，否则将无法通过信任评估。
其他一些症状 症状1 IOS 有的进入速度很快，有的进入很慢
出现上述问题的原因是：验证服务器一般都是通过 CDN 分发的，有可能部分 IP 未被限制
症状2 IOS 进入慢的用户，更换网络环境可以更快的进入。
运营商的限制策略不同，导致更换网络提供商后有可能解决问题。
一些疑问 问题出现的时候 Android 并未收到影响，一直进入速度都很快，如果说验证证书的服务器出现问题，为什么 Android 不受影响呢？</description>
    </item>
    
    <item>
      <title>Nginx、CGI、FastCGI、php-fpm关系 </title>
      <link>https://www.yunxicoding.top/post/web/nginx-php/</link>
      <pubDate>Wed, 12 Jan 2022 15:27:12 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/nginx-php/</guid>
      <description>NGINX 和 PHP 是一对很好的搭档，那么 nginx 是如何与 php 结合实现 对外的网络访问呢？
首先 NGINX 作为一个 web 服务器只能处理静态请求，关于动态请求，是通过 CGI 具备的动态请求能力。
NGINX会将动态请求交给 CGI 处理，CGI 将处理结果返回给 NGINX，NGINX 再将请求的结果返给给浏览器
CGI 的作用 当在浏览器中输入一个动态地址时，如 /index.php， 通过 DNS 解析，最终请求会到 web 服务器上，当 NGINX 发现请求时动态请求。会交给 PHP 解释器处理此请求，那再给 NGINX 需要给 PHP 解释器传输哪些数据呢？请求地址，请求方法，请求参数这些东西总归是要有的，但是也不能仅仅是这些，像请求头和 cookie &amp;hellip; 也应该包含其中。
那么怎么确定到底该传输哪些数据，以及以什么样的格式传输呢？ CGI 就是解决这个问题而生的。
CGI 全程通用网关接口 (英语：Common Gateway Interface，CGI) 是为提供网络服务而执行控制台应用的程序，提供于服务器上实现动态网页的通用协议。通常情况下，一次请求对应一个CGI 脚本的执行，生成一个 HTML。
CGI 接口规定了 nginx 与动态解释器之前传输数据的参数和格式，为 nginx 和脚本解释器之间建立起了一个桥梁。
CGI 的存在使 web 服务器与动态语言之间的关系进行了解耦，使得 web 服务器可以处理的动态请求，但是 CGI 的设计使通过进程的方式来处理动态请求的，当动态请求过来后，CGI 会起一个进程进行处理，请求结束后关闭进程.
进程的频繁创建和销毁是存在开销的，这也就导致了 CGI 很难应对高并发的场景, FastCGI 的出现解决了这个问题.</description>
    </item>
    
    <item>
      <title>linux 挂载新盘</title>
      <link>https://www.yunxicoding.top/post/linux/mount-disk/</link>
      <pubDate>Tue, 11 Jan 2022 14:28:01 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/mount-disk/</guid>
      <description>当一个新盘挂载的 linux 上，可以通过 fdisk -l 指令，查看挂载的磁盘信息，此时虽然已经挂载到主机上，但是主机还不能正常使用。
要想使用新磁盘，需要经过如下几步：
磁盘分区 磁盘格式化 挂载分区到某个目录 经过上面三部后，就可以使用上新的磁盘了，接下来讲解每一步具体应该如何操作
磁盘分区 $ fdisk -l #查看主机所有的磁盘列表 如上图可以看出 /dev/vda 是新的磁盘并且没有进行分区操作，接下来对 /dev/vda 磁盘进行分区操作
$ fdisk /dev/vda // 进入分区操作，这里要填写你的新磁盘信息，不一定和示例一致 $ m //查看帮助 上图为分区对应的各个操作
$ p #输入 p 查看分区列表 目前并没有划分分区，接下来进行第一个分区的划分
$ n #输入 n 划分分区 $ p # 输入 P 划分为主要分区 接下来需要填写编号（1-4之间）和 磁盘的开始扇区和结束山区
这里由于磁盘不大就没有进行过于细的划分，将所有磁盘容量全部划分到主分区1中
$ w # 输入 w 保存 到此磁盘的划分就已经全部完成，再次输入 fdisk -l 可以看到新划分的主分区 /dev/vda1
格式化磁盘 磁盘划分好后，继续要格式分区，这里使用 ext4 进行格式化
$ mkfs.ext4 /dev/vda1 # 用 ext4 扩展文件系统进行格式化 输入完上述命令后，就可以完成格式化了，如果有报错，请参考常见问题章节解决。</description>
    </item>
    
    <item>
      <title>服务器公钥登陆</title>
      <link>https://www.yunxicoding.top/post/operations/public-login/</link>
      <pubDate>Tue, 02 Nov 2021 15:56:22 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/operations/public-login/</guid>
      <description>所谓&amp;quot;公钥登录&amp;quot;，原理很简单，就是用户将自己的公钥储存在远程主机上。登录的时候，远程主机会向用户发送一段随机字符串，用户用自己的私钥加密后，再发回来。远程主机用事先储存的公钥进行解密，如果成功，就证明用户是可信的，直接允许登录shell，不再要求密码。
要实现公钥登陆的前提是需要用户端需要先生成公/私钥对，正常情况下都是有的，目录为：用户目录下的 .ssh 文件夹，私钥没有后缀，公钥后缀为 .pub。
如果没有公私密钥的话可以使用 ssh-keygen 进行生成.
复制代码
ssh-keygen -f test // -f 制定公司钥名称，回车进入下一步 Enter passphrase (empty for no passphrase): // 这里是密钥口令，如果担心私钥安全可以设置一下 Enter same passphrase again: // 确认密钥口令，回车后完成生成 服务器导入公钥 命令行导入 3ssh-copy-id -i ~/.ssh/test.pub user@host 手动导入 通过账号密码登陆服务器，将公钥复制到 ~/.ssh 目录下,执行下方命令:
cat RemotePPK.pub &amp;gt;&amp;gt; authorized_keys xshell 使用公钥登陆演示 输入用户名
选择公钥登陆
导入密钥
选择导入好的密钥点 Ok
点击OK 完成登陆</description>
    </item>
    
    <item>
      <title>golang设计模式之单例模式</title>
      <link>https://www.yunxicoding.top/post/golang/design-pattern/singletion/</link>
      <pubDate>Sat, 19 Jun 2021 17:35:46 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/design-pattern/singletion/</guid>
      <description>日常开发中经常会遇到单例模式的使用场景，单例模式可以保证我们初始化出来的结构体只有一个，在一些请求上下文，mysql 连接池..场景经常有着不可估量的作用。
在 golang 开发中，我们应当如何去设计单例模式呢？
常见的错误书写方式 相信如果你对 PHP 写的比较多的情况下经常会写出下面代码一样的单例模式
package main type Singleton struct { } var ins *Singleton func GetIns() *Singleton { if ins == nil { ins = &amp;amp;Singleton{} } return ins } 那上述代码，在正常开发存在什么样的问题呢？上述代码如果用于 PHP 项目是没有任何问题的，因为 PHP 本事是请求隔离的，因而也不会存在并发的问题，但是如果放在常驻内存类型的语言中就会出现并发性不安全问题。
接下来考虑一个场景，在高并发场景下，同时又两个业务都执行到 ins == nil 而此时的 ins 确实没有实例化，那程序将会如何执行呢？答案显而易见，两个业务都会实例化出自己的 ins 结构体，这显然不符合我们的程序预期。
如何解决 golang 标准库sync中找到了Once类型。它能保证某个操作仅且只执行一次。有了他我们就可以很方便的解决并发的问题了接下看看代码示例：
package main import &amp;#34;sync&amp;#34; type Singleton struct { } var ins *Singleton var once sync.Once func GetIns() *Singleton { once.</description>
    </item>
    
    <item>
      <title>golang base64 编码与 PHP 输出不一致</title>
      <link>https://www.yunxicoding.top/post/golang/base64-ascii/</link>
      <pubDate>Wed, 16 Jun 2021 17:49:39 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/base64-ascii/</guid>
      <description>最近开发中，将一个 php 算法，移植到 golang 中，发现 base64 算法生成的字符串不一致，经过排查发现是由于 ASCII 控制字符导致的原因，加下来看代码
&amp;lt;?php $asciiArr =[10, 187, 217, 12, 207, 183, 184, 231, 184, 149, 118, 151]; $str = &amp;#39;&amp;#39;; foreach ($asciiArr as $ascii) { $str .= chr($ascii); } echo base64_encode($str); 上述代码输出： CrvZDM+3uOe4lXaX
package main import ( &amp;#34;encoding/base64&amp;#34; &amp;#34;fmt&amp;#34; ) func main() { res := []int{10, 187, 217, 12, 207, 183, 184, 231, 184, 149, 118, 151} var str string var b []byte for _, v := range res { str += string(v) b = append(b, byte(v)) } byteCode := base64.</description>
    </item>
    
    <item>
      <title>jenkins pipeline 环境变量详解</title>
      <link>https://www.yunxicoding.top/post/ci-cd/environment-variables/</link>
      <pubDate>Mon, 07 Jun 2021 17:16:46 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ci-cd/environment-variables/</guid>
      <description>关于 Jenkins 的环境变量，可以分为系统内置环境变量和自定义环境变量。系统内置环境变量是 Jenkins 内部定义的环境变量。自定义环境变量是用户自己定义的环境变量
系统环境变量 jenkins 的内置环境变量的查看方式有两种，一种是通过 web url 地址查看，另一种是通过 shell 命令 printenv 查看
方式一:通过地址访问 直接在浏览器中访问 ${YOUR_JENKINS_HOST}/env-vars.html 页面就可以，比如 http://localhost:8080/env-vars.html ，每个变量的用途写的都很清楚
方式二：printenv 通过 shell 命令printenv 获取
pipeline {agent anystages {stage(&amp;#34;Env Variables&amp;#34;) {steps {sh &amp;#34;printenv&amp;#34;}}}} 通过执行上述 pipeline 的构建，就可以打印出系统内置的环境变量
自定义环境变量 在内置环境变量的基础上，有事我们也需要定义我们自己的的环境变量来方便开发和部署，自定义环境变量的设置分为三种，分别是：声明式，脚本式，内置函数式，接下来分别讲解一下三种方式各是如何设置的。
声明式 声明式定义结构如下：
environment {key = value} 声明式可以在 pipeline 的任意阶段声明，看如下示例
pipeline {agent {label any}environment { //全局环境变量NAME = &amp;#34;zhangsan&amp;#34;}stages {stage(&amp;#39;Build&amp;#39;) {environment { // 仅在 Build 阶段下有效的环境变量NAME = &amp;#34;Andy&amp;#34;}steps {sh &amp;#39;printenv&amp;#39;}}}} 脚本式 脚本式基本结构如下:</description>
    </item>
    
    <item>
      <title>nginx location 优先级</title>
      <link>https://www.yunxicoding.top/post/nginx/location-priority/</link>
      <pubDate>Wed, 19 May 2021 13:52:44 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/nginx/location-priority/</guid>
      <description>location 优先级介绍 location 匹配方式有以下几种
location = /path/a/exact.png {}: 精确匹配
location ^~ /path/a/ {}: 优先前缀匹配（符合最长匹配原则）
location ~ /Path?/ {} ： 区分大小写正则匹配（首次匹配原则）
location ~* /path?/ {} : 不区分大小写正则匹配（首次匹配原则）
location /path/a/test.png {} : 前缀匹配（符合最长匹配原则）
location 优先级匹配与配置的先后顺序无关，优先级排列顺序如下
精确匹配 &amp;gt; 优先前缀匹配 &amp;gt; 区分大小写正则匹配=不区分大小写正则匹配 &amp;gt; 前缀匹配
实例说明 location = /path/a/exact.png { [ configuration A ] } location ~ /Path?/ { [ configuration B ] } location ~* /path?/ { [ configuration C ] } 如果理解了优先级，将很容易得出如下结论:
www.web.com/path/a/exact.png =&amp;gt; 匹配 configuration A</description>
    </item>
    
    <item>
      <title>nginx 单站点多证书配置</title>
      <link>https://www.yunxicoding.top/post/nginx/multi-cert/</link>
      <pubDate>Tue, 18 May 2021 16:03:21 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/nginx/multi-cert/</guid>
      <description>问题 在同一 server 中同时设置了， a.com, b.com.c.com 三个域名，若三个域名都需要 https 访问，应当如何配置呢？
示例配置如下：
server { listen 443 ssl; server_name a.com b.com c.com; ssl_certificate a.crt; ssl_certificate_key a.key; ... } 问题分析：如果在 ssl_certificate 与 ssl_certificate_key 的配置项中可以增加变量，此问题就可以迎刃而解。
变量 ssl_server_name 经过 goole 发现 nginx 在 1.15.10 及以后的版本支持 ssl_server_name 变量，此变量输出的便是所访问 URL 的 host 部分(不包含端口及后面的 path 部分).
URL ssl_server_name https://a.com/test a.com https://b.com:83/test b.com https://c.com/test c.com 有了 ssl_server_name 就可以很好的解决我们在文章开头描述的问题参考配置如下:
server {listen 443 ssl;server_name a.com b.com c.com;ssl_certificate /crtPath/$ssl_server_name.crt;ssl_certificate_key /keyPath/$ssl_server_name.</description>
    </item>
    
    <item>
      <title>Composer 私有仓库搭建</title>
      <link>https://www.yunxicoding.top/post/php/composer/private-repo/</link>
      <pubDate>Thu, 01 Apr 2021 18:00:46 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/php/composer/private-repo/</guid>
      <description>Composer 是 PHP 的软件包管理系统，它提供用于管理 PHP 软件和依赖库关系的标准格式。作为日常开发，Composer 能够满足我们的日常需求，但有些情况下，我们有些公司内部的扩展，不希望被检索或者放在共有库中，就需要搭建私有库来解决问题。
在讨论搭建私有仓库前，我们先了解一下 Composer 是如何安装扩展的
Composer 安装扩展流程 通过上图我们不难发现，如果要构建私有仓库，我们需要构建一个类似 Packagist 的网站，来告诉 Composer 该从哪里下载扩展代码，这就需要用到 Satis 工具
Satis 介绍 Satis 是一个开源的静态 Composer 仓库生成器，有了它我们可以轻松的构建出一个类似 Packagist 的索引网站，来告诉 Composer 通过我们自己的私有仓库下载扩展。
安装 命令行安装 // 安装composer create-project composer/satis:dev-main// 构建索引仓库php bin/satis build &amp;lt;configuration-file&amp;gt; &amp;lt;output-directory&amp;gt; docker 安装 下载镜像：
docker pull composer/satis 运行镜像:
docker run --rm --init -it \ --user $(id -u):$(id -g) \ --volume $(pwd):/build \ --volume &amp;#34;${COMPOSER_HOME:-$HOME/.composer}:/composer&amp;#34; \ composer/satis build &amp;lt;configuration-file&amp;gt; &amp;lt;output-directory&amp;gt; 配置 创建一个名为：satis.</description>
    </item>
    
    <item>
      <title>前端CI/CD落地实践</title>
      <link>https://www.yunxicoding.top/post/ci-cd/front-cicd/</link>
      <pubDate>Mon, 29 Mar 2021 18:30:18 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ci-cd/front-cicd/</guid>
      <description>随着 nodejs 的兴起，前端开发也进入了 新的时代，webpack 的诞生，更是让其如虎添翼，构建出欣欣向荣的前端生态. 然而事物的发展总是在：发现问题-&amp;gt;解决问题-&amp;gt;引入新问题中往复。
webpack 给前端带来了一个新的高频操作就是打包，高频的打包会带来如下问题：
阻塞前端工作：前端必须等到打包完成，才能 进入后续工作,如果打包时间过长，这中间就会阻塞就会更加明显。
影响团队协作：当功能点开发完成后，未避免频繁打包，会将多个功能点合并在 一起打包，这样就会导致测试没有办法提前介入测试，如果测试出问题的功能点是在几天前开发的，对于代码已经有所遗忘，还需要阅读代码帮助找回记忆gg。
打断专注时间：如果前端正在专注的解决一个复杂问题,此时出现紧急问题需要打包，使得前端不得不从当前工作中抽身出来进行打包，打包完成后将需要一段时间的预热才可以像之前一样 专注的处理未完成的问题。
对于上述 问题完全可以通过工具构建出一套 CI/CD 方案交由计算机来解决，让前端工程师从打包解放出来。
工具 jenkens: 是一款由 JAVA 编写的开源的持续集成工具.
gogs: 轻量级代码仓库
jenkins 与 gogs 的通讯是通过 Gogs plugin 通过 webhook 来通讯的。
方案 方案设计大致如下：
方案执行步骤：
前端工程师往代码仓库 push 代码 gogs 会调用 jenkins 的 webhook 地址触发 jenkins 工作流 jenkins 会将前端代码从代码仓库中拉取最新的代码，并执行构建流程 将构建好的代码上传到 dist 仓库(如果存在 CDN 则清除 CDN 缓存) push dist 仓库到 gogs 远程仓库 gogs 触发 dist 仓库的 webhook,将服务器代码更新到最新的打包文件 完成部署 测试人员执行测试用例（如果由缺陷，将缺陷反馈给前端开发人员） 好处 完成上述方案，前端工程师只需要将前端代码 push 到远程仓库，服务器会自动构建响应的包，并部署到测试服务器上供测试测试,前端可以缩小提交粒度并更快的将功能交付测试，也可以将问题尽早暴露尽早解决。</description>
    </item>
    
    <item>
      <title>Base64编码的前世今生</title>
      <link>https://www.yunxicoding.top/post/base/base64/</link>
      <pubDate>Mon, 12 Oct 2020 10:30:54 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/base/base64/</guid>
      <description>Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64，所以每6个比特为一个单元，对应某个可打印字符。三个字节有24个比特，对应于4个Base64单元，即3个字节可表示4个可打印字符。它可用来作为电子邮件的传输编码。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9，这样共有62个字符，此外两个可打印符号在不同的系统中而不同。一些如uuencode的其他编码方法，和之后binhex的版本使用不同的64字符集来代表6个二进制数字，但是它们不叫Base64。
起因 计算机以二进制形式（0和1）进行通信，但是人们通常希望与更丰富的表单数据（例如文本或图像）进行通信。 为了在计算机之间传输此数据，首先必须将其编码为0和1，然后发送，然后再次解码。 以文本为例-有许多不同的方法可以执行此编码。 如果我们都可以同意一个编码，那就简单得多了，但不幸的是事实并非如此。
最初创建了许多不同的编码（例如Baudot码），每个字符使用不同数量的位，直到最终ASCII成为每个字符7位的标准。 但是，大多数计算机将二进制数据存储在每个字节由8位组成的字节中，因此ASCII不适合传输此类数据。 有些系统甚至会擦除最高位。 此外，跨系统的行尾编码的差异意味着有时还会修改ASCII字符10和13。
为了解决这些问题，引入了Base64编码。 这样，您就可以将框架字节编码为已知可以安全发送而不损坏的字节（ASCII字母数字字符和几个符号）。 缺点是使用Base64编码消息会增加其长度-每3个字节的数据会编码为4个ASCII字符。 为了可靠地发送文本，您可以首先使用所选的文本编码（例如UTF-8）将其编码为字节，然后再对Base64进行编码，将生成的二进制数据编码为可安全发送为ASCII的文本字符串。 接收者将不得不逆转此过程以恢复原始消息。 当然，这要求接收者知道使用了哪种编码，并且该信息通常需要单独发送。
从历史上看，它已用于对电子邮件中的二进制数据进行编码，其中电子邮件服务器可能会修改行尾。 一个更现代的示例是使用Base64编码将图像数据直接嵌入HTML源代码中。 在这里，有必要对数据进行编码，以避免像“ &amp;lt;”和“&amp;gt;”这样的字符被解释为标签。
原理 base64 加密原理，是将待转换字符串转换为二进制并以三个字为一组（数据不足用 0 补足），每 6 位为一个索引组转换为十进制索引，通过索引在 base64 索引表中找到对应的字符作为编码后的字符（若原数据长度不是 3 的倍数时则对 3 取余余数为 1，则在编码结果后加2个=；若余数为 2，则在编码结果后加 1 个 =。）。
base64 转换实例 以 helloworld 为例转换过程如下：
经过上述步骤转后字符串为：aGVsbG93d29ybGQ=，到此一个完整的转码例子便完成了。
应用 base64 在需要网络通讯的场景下，有着非常广泛的应用场景，比如邮件的附件和图象传输，html img 标签的 src 属性也可以是 base64 编码的图片，还有我们常用的各类证书(支付证书，ssl 证书)的公钥和私钥..
Summary base64 是一个很优秀的编码方式，他很好的解决了复杂文件在传输中的问题，也因此被广泛的应用在各种场景之中。关于 base64 的使用，你还需要知道如下几点：
base64 转码后的长度会有所变化，会比源数据的长度多出大约 1/3 base64 不具有加密特性，因此他不适用于加密的场景 由于 base64 字符中会有 +,/ 等字符，因此如果需要 url 传参的时候注意需要使用 URL 编码一下，否则有可能会导致接受到的数据无法正常解密的情况 References 维基百科</description>
    </item>
    
    <item>
      <title>PHP-FFMpeg 安装</title>
      <link>https://www.yunxicoding.top/post/php/ffmpeg-install/</link>
      <pubDate>Fri, 28 Aug 2020 13:49:19 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/php/ffmpeg-install/</guid>
      <description>安装 FFMPEG wget http://download.bt.cn/install/ext/ffmpeg.sh &amp;amp;&amp;amp; sh ffmpeg.sh 安装完后可输入以下命令是否安装成功
ffmpeg -version 删除禁用函数 proc_open， exec, system
安装 PHP-FFMpeg composer require php-ffmpeg/php-ffmpeg 常见问题 Executable not found, proposed : avprobe, ffprobe php-fpm 在执行 FFMPEG 的时候，没有找到 ffprobe 和 ffmpeg 解决办法如下：
命令行解决 通过 shell 确定文件位置 whereis ffprobe/usr/local/bin/ffprobewhich ffmpeg/usr/local/bin/ffmpeg 在初始化 FFMpeg 的时候将文件位置写入 $ffmpeg = FFMpeg::create([ &amp;#39;ffmpeg.binaries&amp;#39; =&amp;gt; &amp;#39;/usr/local/bin/ffmpeg&amp;#39;, &amp;#39;ffprobe.binaries&amp;#39; =&amp;gt; &amp;#39;/usr/local/bin/ffprobe&amp;#39;, ]); PHP 代码直接解决 // 写入 WWW 用户的环境变量 putenv(&amp;#39;PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:$PATH&amp;#39;); // 通过 exec 函数找到文件对应位置 $ffmpeg = FFMpeg::create([ &amp;#39;ffmpeg.</description>
    </item>
    
    <item>
      <title>CSV 中文解析异常</title>
      <link>https://www.yunxicoding.top/post/php/csv-parse-error/</link>
      <pubDate>Sat, 06 Jun 2020 14:42:50 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/php/csv-parse-error/</guid>
      <description>最近在做导入 csv 数据的时候出现了，未按预期导出的情况，具体情况见下图： 如图所示，商家地址部分和商家电话部分，没有正常解析。后来在PHP官网找到如下解释：
该函数对区域设置是敏感的。比如说 LANG 设为 en_US.UTF-8 的话，单字节编码的文件就会出现读取错误。 我系统语言设置的 en, 当解析 utf8 字节编码的文件，就出现了上述问题。
既然找到了是因为区域的问题，就号解决了，在导入前先设置一下区域：
setlocale(LC_ALL, &amp;#39;us&amp;#39;); 如果有疑问欢迎留言交流。
Reference fgetcsv</description>
    </item>
    
    <item>
      <title>Linux Manjaro 安装指南</title>
      <link>https://www.yunxicoding.top/post/linux/manjaro-install/</link>
      <pubDate>Tue, 05 May 2020 17:03:42 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/manjaro-install/</guid>
      <description>Linux 历史 1994 年 3 月，Linux1.0 版正式发布，Marc Ewing 成立 Red Hat 软件公司，成为最著名的 Linux 经销商之一。早期 Linux 的引导管理程序（boot loader）使用 LILO（Linux Loader），早期的 LILO存在着一些难以容忍的缺陷，例如无法识别 1024 柱面以后的硬盘空间，后来的 GRUB（GRand Unified Bootloader）克服这些缺点，具有‘动态搜索内核文件’的功能，可以让用户在引导的时候，自行编辑引导设置系统文件，透过 ext2 或 ext3 文件系统中加载 Linux Kernel（GRUB通过不同的文件系统驱动可以识别几乎所有 Linux 支持的文件系统，因此可以使用很多文件系统来格式化内核文件所在的扇区，并不局限于 ext 文件系统）。
今天由 Linus Torvalds 带领下，众多开发共同参与开发和维护 Linux 内核。理查德·斯托曼领导的自由软件基金会，继续提供大量支持 Linux 内核的GNU组件。一些个人和企业开发的第三方的非GNU组件也提供对Linux 内核的支持，这些第三方组件包括大量的作品，有内核模块和用户应用程序和库等内容。Linux社区或企业都推出一些重要的 Linux 发行版，包括 Linux 内核、GNU 组件、非 GNU 组件，以及其他形式的软件包管理系统软件。
什么人适合 linux 系统 对 Linux 保持高敏感度的人（运维人员，后端开发&amp;hellip;） 有 Geek 精神喜欢折腾的人。 对 Windows 生厌， 想要尝试新系统的人，不差钱可以上 MAC。 Linux 发行版之 Manjaro Manjaro Linux 基于 Arch Linux，但拥有自己独立的软件仓库。Manjaro 的目标是让强大的 Arch 更方便用户使用，Manjaro 使用著名的 Pacman 且可以直接利用 AUR 上的資源。Manjaro 本身使用三个软件仓库：不稳定库，即含有那些不成熟的 Arch 包，这些包与 Arch 源有 1-2 天 的延后；测试库，每周同步一次，包含那些 Arch 不稳定源的包；以及稳定库，包含那些由开发团队确认稳定的软件。</description>
    </item>
    
    <item>
      <title>snapcraft 下载软件慢，解决办法</title>
      <link>https://www.yunxicoding.top/post/linux/snap-slow/</link>
      <pubDate>Fri, 01 May 2020 08:39:20 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/snap-slow/</guid>
      <description>由于国内网络原因，snap 安装软件会非常的慢，而且会有中断的情况，可以通过给 snap 设置代理的设置代理的方式解决此问题，具体操作如下：
$ sudo systemctl edit snapd 编辑 snapd 文件， 加入如下代码：
[Service]Environment=&amp;#34;http_proxy=http://127.0.0.1:port&amp;#34;Environment=&amp;#34;https_proxy=http://127.0.0.1:port&amp;#34; ctrl + o 写入文件， 回车确认
ctrl + x 退出编辑
重启服务 $ sudo systemctl daemon-reload
$ sudo systemctl restart snapd
再次执行安装操作，会发现速度已经有了大幅度提升。</description>
    </item>
    
    <item>
      <title>git 删除 commit 信息</title>
      <link>https://www.yunxicoding.top/post/git/del-commit/</link>
      <pubDate>Mon, 28 Oct 2019 20:38:00 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/git/del-commit/</guid>
      <description>#Clone your git repogit clone &amp;lt;repo url&amp;gt;;#Entre your local repocd lestatzhang.github.io;#Checkoutgit checkout --orphan latest_branch;#Add all the filesgit add -A;#Commit the changesgit commit -am &amp;#34;Reinitialize&amp;#34;;#Delete the branchgit branch -D master;#Rename the current branch to mastergit branch -m master;#Finally, force update your repositorygit push -f origin master; </description>
    </item>
    
    <item>
      <title>Gin_bind_json_return_eof</title>
      <link>https://www.yunxicoding.top/post/golang/gin-bind-json-return-eof/</link>
      <pubDate>Wed, 23 Oct 2019 12:03:46 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/gin-bind-json-return-eof/</guid>
      <description>在一次开发中,通过中间件访问请求中的post参数,于是使用 ioutil.ReadAll 来读取body中的内容并做相应的验证,代码如下:
var pj map[string]interface{}body := c.Request.Bodydata, _ := ioutil.ReadAll(body) 一开始一切都挺美好,但等到测试的时候发现接口中调用 BindJSON(项目使用的框架是 gin) 返回错误,错误信息如下:
bind multipart: NextPart: EOF 经过一番排查发现是因为ioutil.ReadAll() 方法会将,会在你读取之后,就没有了内容,既然知道了原因,解决方法也随之出现,只需要在获取之后,从新将 body 的内容写入,代码如下:
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data)) 至此问题得到了解决
参考链接 Golang: Read from an io.ReadWriter without losing its content</description>
    </item>
    
    <item>
      <title>centos 搭建 NFS</title>
      <link>https://www.yunxicoding.top/post/linux/nfs/</link>
      <pubDate>Sun, 04 Aug 2019 21:17:55 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/nfs/</guid>
      <description>查看系统是否已安装NFS $ rpm -qa | grep nfsnfs-utils-lib-1.1.5-13.el6.i686nfs-utils-1.2.3-78.el6_10.1.i686 如果没有安装,执行下面命令
$ yum -y install nfs-utils rpcbind NFS配置文件 NFS的常用目录
/etc/exports NFS服务的主要配置文件 /usr/sbin/exportfs NFS服务的管理命令 /usr/sbin/showmount 客户端的查看命令 /var/lib/nfs/etab 记录NFS分享出来的目录的完整权限设定值 /var/lib/nfs/xtab 记录曾经登录过的客户端信息 配置 NFS 服务 NFS服务的配置文件为 /etc/exports，这个文件是NFS的主要配置文件，不过系统并没有默认值，所以这个文件不一定会存在，可能要使用vim手动建立，然后在文件里面写入配置内容。 /etc/exports文件内容格式：
客户端1 选项（访问权限,用户映射,其他）
客户端2 选项（访问权限,用户映射,其他）
例如
$ vim /etc/exports写入下列语句/home/data your_ssl_server_ip/24(rw,sync,no_root_squash) 常用配置项说明:
访问权限选项 设置输出目录只读：ro 设置输出目录读写：rw
用户映射选项
all_squash：将远程访问的所有普通用户及所属组都映射为匿名用户或用户组（nfsnobody）；
no_all_squash：与all_squash取反（默认设置）；
root_squash：将root用户及所属组都映射为匿名用户或用户组（默认设置）；
no_root_squash：与rootsquash取反；
anonuid=xxx：将远程访问的所有用户都映射为匿名用户，并指定该用户为本地用户（UID=xxx）；
anongid=xxx：将远程访问的所有用户组都映射为匿名用户组账户，并指定该匿名用户组账户为本地用户组账户（GID=xxx）；
其它选项:
secure：限制客户端只能从小于1024的tcp/ip端口连接nfs服务器（默认设置）；
insecure：允许客户端从大于1024的tcp/ip端口连接服务器；
sync：将数据同步写入内存缓冲区与磁盘中，效率低，但可以保证数据的一致性；
async：将数据先保存在内存缓冲区中，必要时才写入磁盘；
wdelay：检查是否有相关的写操作，如果有则将这些写操作一起执行，这样可以提高效率（默认设置）；
no_wdelay：若有写操作则立即执行，应与sync配合使用；
subtree：若输出目录是一个子目录，则nfs服务器将检查其父目录的权限(默认设置)；
no_subtree：即使输出目录是一个子目录，nfs服务器也不检查其父目录的权限，这样可以提高效率；
设置防火墙 $ sudo vim /etc/sysconfig/iptables加入下面两句-A INPUT -p tcp -s your_client_ip/32 --dport 1:65535 -j ACCEPT-A INPUT -p udp -s your_client_ip/32 --dport 1:65535 -j ACCEPT向客户端开放所有端口(因为 NSF 端口是随机监听的所以需要对客户端开放所有端口) 重启 NFS 服务 注意重启顺序需要严格按照下面执行</description>
    </item>
    
    <item>
      <title>解决nfs挂载错误wrong fs type, bad option, bad superblock</title>
      <link>https://www.yunxicoding.top/post/linux/bad-superblock/</link>
      <pubDate>Sat, 03 Aug 2019 19:54:26 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/bad-superblock/</guid>
      <description>[root@localhost]# mount -t nfs 192.168.0.106:/home/nfs1mount: wrong fs type, bad option, bad superblock on 192.168.0.106:/home/nfs1,missing codepage or helper program, or other error(for several filesystems (e.g. nfs, cifs) you mightneed a /sbin/mount.&amp;lt;type&amp;gt; helper program)In some cases useful info is found in syslog - trydmesg | tail or so 解决办法：
apt-get install nfs-common 或者
yum install nfs-utils </description>
    </item>
    
    <item>
      <title>申请 let&#39;s encrypt 通配符 ssl 证书</title>
      <link>https://www.yunxicoding.top/post/web/wildcard-https/</link>
      <pubDate>Sat, 03 Aug 2019 19:37:51 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/wildcard-https/</guid>
      <description>在&amp;lt;使用 let&amp;rsquo;s encrypt 为网站开启 https&amp;gt;一问中,讲述了,如何用 let&amp;rsquo;s encrypt 为网站开启 https 协议,这篇内容讲一下如何申请 let&amp;rsquo;s encrypt 通配符证书
什么是通配符证书 通配符 SSL 可以保护网站的 URL 及其所有子域（数量不限）。例如，一个单独的通配符证书就可以保护 www.artacode.com、blog.artacode.com 和 store.artacode.com。
通配符证书可以保护通用域名和您在提交申请时指定的级别下的所有子域。只需在通用域名左侧的子域区域添加星号 (*) 即可。
举例 如果为 *.artacode.com 申请证书，则可以保护：
artacode.com www.artacode.com photos.artacode.com blog.artacode.com 如果为 *.www.artacode.com 申请证书，则可以保护：
www.artacode.com mail.www.artacode.com photos.www.artacode.com blog.www.artacode.com 通配符证书可以像常规 SSL 证书一样为网站提供保护，并且申请是使用同一种验证方法进行处理的。不过，一些 Web 服务器可能要求使用通配符证书的每个子域都有唯一的 IP 地址。
如何申请 let&amp;rsquo;s encrypt 通配符证书 安装 Certbot $ wget https://dl.eff.org/certbot-auto$ sudo mv certbot-auto /usr/local/bin/certbot-auto$ sudo chown root /usr/local/bin/certbot-auto$ sudo chmod 0755 /usr/local/bin/certbot-auto 申请证书 certbot-auto --server https://acme-v02.</description>
    </item>
    
    <item>
      <title>使用 let&#39;s encrypt 为网站开启 https</title>
      <link>https://www.yunxicoding.top/post/web/enable-https/</link>
      <pubDate>Tue, 30 Jul 2019 20:54:18 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/web/enable-https/</guid>
      <description>启用可用通道 $ yum -y install yum-utils$ yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional 安装 Certbot $ sudo yum install certbot python2-certbot-nginx 获取 nginx 服务器证书 $ sudo certbot certonly --nginx 遇到问题 certbot 未找到 nginx 二进制文件 The nginx plugin is not working; there may be problems with your existing configuration. The error was: NoInstallationError(&amp;ldquo;Could not find a usable &amp;rsquo;nginx&amp;rsquo; binary. Ensure nginx exists, the binary is executable, and your PATH is set correctly.&amp;rdquo;,)
由于我机器之前机器上已经安装 nginx 安装目录为 /usr/local/nginx/sbin/nginx 而 certbot 默认会到 /usr/bin/nginx 目录下找,因此需要建立一个软连接</description>
    </item>
    
    <item>
      <title>windows 命令行设置环境变量</title>
      <link>https://www.yunxicoding.top/post/windows/env_variable/</link>
      <pubDate>Wed, 24 Jul 2019 15:36:24 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/windows/env_variable/</guid>
      <description>powershell
#Powershell设置环境变量#查看所有环境变量 ls env:#搜索环境变量 ls env:NODE*#查看单个环境变量 $env:NODE_ENV#添加/更新环境变量 $env:NODE_ENV=development#删除环境变量 del evn:NODE_ENV cmd
#cmd设置环境变量#查看所有环境变量 set#查看单个环境变量 set NODE_ENV#添加/更新环境变量 set NODE_ENV=development#删除环境变量 set NODE_ENV= </description>
    </item>
    
    <item>
      <title>Uninx 网络编程环境搭建</title>
      <link>https://www.yunxicoding.top/post/network/init/</link>
      <pubDate>Wed, 24 Jul 2019 06:37:53 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/network/init/</guid>
      <description>1，安装编译器，为了齐全还是安装一下build-essential
sudo apt-get install build-essential 2，下载书中的头文件及示例源码
Source Code
3，解压unpv13e.tar.gz后进入目录查看README，然后可以按照里面提示操作，不过会遇到问题
第一步：在终端中进入upnv13e目录，然后执行代码：1. chmod u+x configure2. ./configure 第二步：进入lib目录下执行make：1. cd lib 2. make第三步：建立基础类库：1. cd ../libfree # continue building the basic library 2. make 第四步：编译函数库：1. cd ../libgai # the getaddrinfo() and getnameinfo() functions 2. make 在执行上述代码的第三步的时候会如果遇到如下错误
gcc -I../lib -g -O2 -D_REENTRANT -Wall -c -o in_cksum.o in_cksum.cgcc -I../lib -g -O2 -D_REENTRANT -Wall -c -o inet_ntop.o inet_ntop.cinet_ntop.c: In function ‘inet_ntop’:inet_ntop.</description>
    </item>
    
    <item>
      <title>exec.Command 返回 “exit status 1” 获取详细错误信息</title>
      <link>https://www.yunxicoding.top/post/golang/command-debug/</link>
      <pubDate>Sat, 20 Jul 2019 17:04:20 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/command-debug/</guid>
      <description>cmd := exec.Command(&amp;#34;find&amp;#34;, &amp;#34;/&amp;#34;, &amp;#34;-maxdepth&amp;#34;, &amp;#34;1&amp;#34;, &amp;#34;-exec&amp;#34;, &amp;#34;wc&amp;#34;, &amp;#34;-c&amp;#34;, &amp;#34;{}&amp;#34;, &amp;#34;\\&amp;#34;)output, err := cmd.CombinedOutput()if err != nil {fmt.Println(fmt.Sprint(err) + &amp;#34;: &amp;#34; + string(output))return}fmt.Println(string(output)) </description>
    </item>
    
    <item>
      <title>ubuntu 创建桌面快捷方式</title>
      <link>https://www.yunxicoding.top/post/linux/desktop-shortcut/</link>
      <pubDate>Sat, 20 Jul 2019 16:57:01 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/desktop-shortcut/</guid>
      <description>安装 sudo apt-get install --no-install-recommends gnome-panel 使用 gnome-desktop-item-edit &amp;ndash;create-new ~/Desktop</description>
    </item>
    
    <item>
      <title>vs code 调试远程代码</title>
      <link>https://www.yunxicoding.top/post/ide/vs-code/remote-ssh/</link>
      <pubDate>Wed, 19 Jun 2019 20:31:23 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ide/vs-code/remote-ssh/</guid>
      <description>简介 Visual Studio Code Remote - SSH 可以打开远程计算机或容器中的文件(计算机或容器中需要运行 SSH Server),而且可已充分利用 VS Code 的一切特性.一旦连接上服务器,可以与任意位置的文件进行交 由于 Remote-SSH 扩展运行命令行和其他扩展是在远程主机上,因此本地不需要代码,就可以正常运行.
入门 确保本机已经安装 VS CODE 确保本机已安装 SSH client, 并且确保远程主机已安装 SSH server 为本机 VS CODE 安装 Remote Development 扩展 连接远程主机 连接配置 Ctrl + Shift + p 输入 Configure SSH Hosts... 回车,设置远程主机信息,配置信息如下
# Read more about SSH config files: https://linux.die.net/man/5/ssh_configHost oa-onlineHostName 远程主机IpUser root Host 远程主机名称 HostName 远程主机IP User 连接用户名 用户名密码连接 在配置文件 settings.json 加入 &amp;quot;remote.SSH.showLoginTerminal&amp;quot;: true</description>
    </item>
    
    <item>
      <title>vue jsonp 跨域调用 腾讯地图 api报错</title>
      <link>https://www.yunxicoding.top/post/cross-origin/jsonp/</link>
      <pubDate>Sat, 11 May 2019 07:12:56 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/cross-origin/jsonp/</guid>
      <description>今天帮助前端小伙伴解决了一下 jsonp 调用腾讯地图 WebService API 报错 Unexpected token : 的问题，这里记录一下解决过程
项目中使用 vue-jsonp 组件来解决跨域问题，使用流程如下
安装 jsonp npm install vue-jsonp main.js 引入 jsonp 组件 import Vue from &amp;#39;vue&amp;#39; import VueJsonp from &amp;#39;vue-jsonp&amp;#39; Vue.use(VueJsonp) 接口调用 const KEY =XXXXXXXXXXXXXX; //key 秘钥自己申请let url = &amp;#39;https://apis.map.qq.com/ws/geocoder/v1?&amp;amp;poi_options=address_format=short&amp;amp;get_poi=0&amp;#39;;let locationdata = lat+&amp;#39;,&amp;#39;+lng //纬度，经度this.$jsonp(url,{key:KEY,location:locationdata}).then(json =&amp;gt; {let address = json.result.address;console.log(address) //附近街道地址信息}).catch(err =&amp;gt; {console.log(err)})} 本来到了这里应该是完美无缺，开开心心的接受腾讯地图返回的信息就可以了，但是！！，出来了莫名其妙的问题，当请求接口是返回的确是 Unexpected token : 这样的错误。</description>
    </item>
    
    <item>
      <title>微信公众号 jssdk 报错</title>
      <link>https://www.yunxicoding.top/post/wechat/jssdk-config/</link>
      <pubDate>Fri, 10 May 2019 21:02:48 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/wechat/jssdk-config/</guid>
      <description>引入 wx-js-sdk
import wx from &amp;#39;weixin-js-sdk&amp;#39;; 设置微信 config 部分代码：
this.$axios.get(&amp;#34;后台获取 config 地址&amp;#34;, {}).then((response) =&amp;gt; {...wx.config(configData);wx.ready(() =&amp;gt; {this.getLocation();})...}) 前端 console 中报错如下 getLocation:fail, the permission value is offline verifying
经过一番排查锁定为签名问题查询微信官方文档签名规则如下：
签名生成规则如下：参与签名的字段包括noncestr（随机字符串）, 有效的jsapi_ticket, timestamp（时间戳）, url（当前网页的URL，不包含#及其后面部分） 。对所有待签名参数按照字段名的ASCII 码从小到大排序（字典序）后，使用URL键值对的格式（即key1=value1&amp;amp;key2=value2…）拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密，字段名和字段值都采用原始值，不进行URL 转义。
即signature=sha1(string1)。 示例：
noncestr=Wm3WZYTPz0wzccnWjsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qgtimestamp=1414587457url=http://mp.weixin.qq.com?params=value 由于后台项目使用 easywechat SDK， easywechat 默认会使用 $_SERVER[&#39;HTTP_HOST&#39;] 和 $_SERVER[&#39;REQUEST_URI&#39;] 作为 url 签名参数， 这里也就是将接口地址作为签名的 url ， 而我们项目是前后端分离，实际调用者确是前端项目所部署的 url 因而导致签名不正确。
知晓问题解决就很好办了，后台签名的时候接受 前端当前页面的 url 然后通过 $app-&amp;gt;jssdk-&amp;gt;setUrl($url); 来设置 签名的 url，这样就解决了此问题。</description>
    </item>
    
    <item>
      <title>Ubuntu 终端oh my fish的agnoster主题乱码</title>
      <link>https://www.yunxicoding.top/post/linux/oh-my-fish-agnoster/</link>
      <pubDate>Sat, 04 May 2019 07:33:28 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/oh-my-fish-agnoster/</guid>
      <description>执行以下命令来安装缺失的字体：
wget https://raw.githubusercontent.com/powerline/powerline/develop/font/10-powerline-symbols.conf
wget https://raw.githubusercontent.com/powerline/powerline/develop/font/PowerlineSymbols.otf
sudo mkdir /usr/share/fonts/OTF
sudo cp 10-powerline-symbols.conf /usr/share/fonts/OTF/ sudo mv 10-powerline-symbols.conf /etc/fonts/conf.d/
sudo mv PowerlineSymbols.otf /usr/share/fonts/OTF/
然后重启终端,恢复正常</description>
    </item>
    
    <item>
      <title>Oh My Fish 一个可以让你赏心悦目的 fish shell 框架</title>
      <link>https://www.yunxicoding.top/post/linux/oh-my-fish/</link>
      <pubDate>Fri, 03 May 2019 22:37:27 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/oh-my-fish/</guid>
      <description>安装 fish shell 以 ubuntu 为例
sudo apt-add-repository ppa:fish-shell/release-3sudo apt-get updatesudo apt-get install fish 其他系统安装点击链接
安装 oh my fish curl -L https://get.oh-my.fish | fish 运行上述命令，就尅安装 oh my fish 了，接下来来讲解一下 oh my fish 的基本使用方式
操作说明 omf update [omf] [&amp;lt;package&amp;gt;...]
更新 oh my fish 包仓库以及所有安装过的包
执行该命令未加任何参数的时候则更新自身和所有安装过的包 仅仅更新 oh my fish omf update omf. 对于选择更新，你仍然可以在更新包的列表中加入 “omf” 来更新 oh my fish 自身 omf install [&amp;lt;name&amp;gt;|&amp;lt;url&amp;gt;]
安装一个或更多的包
执行 omf install URL 可以直接通过 URL 安装包 如何没传入参数，则冲 bundle 中安装缺少的包 omf repositories [list|add|remove]</description>
    </item>
    
    <item>
      <title>docker 常见错误</title>
      <link>https://www.yunxicoding.top/post/docker/faq/</link>
      <pubDate>Sun, 28 Apr 2019 07:22:17 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/faq/</guid>
      <description>/var/run/docker.sock: no such file or directory /var/run/docker.sock 的访问权限只 root 和 docker 用户组, 所以你需要把自己的用户添加到 docker 组 命令: $ sudo gpasswd -a ${uesrname} docker</description>
    </item>
    
    <item>
      <title>vim-plug 安装</title>
      <link>https://www.yunxicoding.top/post/ide/vim/vim-plug-install/</link>
      <pubDate>Sun, 21 Apr 2019 07:33:57 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ide/vim/vim-plug-install/</guid>
      <description>vim-plug 是一个极简的 vim 插件管理器
安装 vim-plug 的安装十分简单只需要一行命令就可以搞定
vim Linux curl -fLo ~/.vim/autoload/plug.vim --create-dirs \https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim Windows (PowerShell) md ~\vimfiles\autoload$uri = &amp;#39;https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim&amp;#39;(New-Object Net.WebClient).DownloadFile($uri,$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(&amp;#34;~\vimfiles\autoload\plug.vim&amp;#34;)) Neovim Linux curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim Windows (PowerShell) md ~\AppData\Local\nvim\autoload$uri = &amp;#39;https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim&amp;#39;(New-Object Net.WebClient).DownloadFile($uri,$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(&amp;#34;~\AppData\Local\nvim\autoload\plug.vim&amp;#34;)) 使用 在 vim 配置文件 ~/vimrc 或 Neovim 配置文件 ~/.config/nvim/init.vim 中增加 vim-plug 配置项
开始部分 call plug#begin() Plug 命令用于定义使用的插件列表 结束部分 call plug#end() 更新 &amp;amp;runtimepath 并且初始化插件系统 自动执行 filetype plugin indent on 和 syntax enable.</description>
    </item>
    
    <item>
      <title>electron 改变窗体大小</title>
      <link>https://www.yunxicoding.top/post/electron/change-window-from-render/</link>
      <pubDate>Sat, 13 Apr 2019 20:21:44 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/electron/change-window-from-render/</guid>
      <description>相关链接:
electron-vue 集成 element-ui
在开发 electron 的时候遇到了需要在 render 中修改 BrowserWindow 窗口大小的方式，经过一番尝试，有两种方法实现:
通过 ipcRenderer 与 ipcMain 的通讯来实现 通过 render 的 remote 模块来实现 ipcRenderer 和 ipcMain 实现 实现原理是 render 进程通过 ipcRenderer 与 ipcMain 进行通讯以通知 main 进程操作窗体操作。
在 render 引入 ipcRenderer
let {ipcRenderer} = require(&amp;#39;electron&amp;#39;) 发送同步消息给 main 进程
ipcRenderer.sendSync(&amp;#39;synchronous-message&amp;#39;,&amp;#39;logined&amp;#39;) 在 main 中监听同步消息，并处理 logined 消息操作
ipcMain.on(&amp;#39;synchronous-message&amp;#39;, (event, arg) =&amp;gt; {if (arg === &amp;#39;logined&amp;#39;) {mainWindow.resize(1000, 1000)}}) remote 方式是实现 引入 remote 模块</description>
    </item>
    
    <item>
      <title>electron-vue  集成 element-ui</title>
      <link>https://www.yunxicoding.top/post/electron/install/</link>
      <pubDate>Thu, 04 Apr 2019 20:21:44 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/electron/install/</guid>
      <description>简介 什么是 electron Electron 是由 Github 开发，用 HTML，CSS 和 JavaScript 来构建跨平台桌面应用程序的一个开源库。 Electron 通过将 Chromium 和 Node.js 合并到同一个运行时环境中，并将其打包为 Mac ，Windows 和 Linux 系统下的应用来实现这一目的。
Electron 于 2013 年作为构建 Github 上可编程的文本编辑器 Atom 的框架而被开发出来。这两个项目在2014春季开源。
目前它已成为开源开发者、初创企业和老牌公司常用的开发工具
什么是 electron-vue electron-vue 是为了要避免使用 vue 手动建立起 electron 应用程序。electron-vue 充分利用 vue-cli 作为脚手架工具，加上拥有 vue-loader 的 webpack、electron-packager 或是 electron-builder，以及一些最常用的插件，如vue-router、vuex 等等
什么是 elecment ui Element UI 是一套采用 Vue 2.0 作为基础框架实现的组件库，一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的组件库，提供了配套设计资源，帮助你的网站快速成型
安装 electron-vue # 安装 vue-cli 和 脚手架样板代码npm install -g vue-cli //如果你已经安装忽略此处vue init simulatedgreg/electron-vue my-project# 安装依赖并运行你的程序cd my-projectyarn # 或者 npm installyarn run dev # 或者 npm run dev 运行结果如下:</description>
    </item>
    
    <item>
      <title>VS Code golang debug 配置</title>
      <link>https://www.yunxicoding.top/post/ide/vs-code/debug-go/</link>
      <pubDate>Mon, 25 Mar 2019 18:24:21 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ide/vs-code/debug-go/</guid>
      <description>实现效果 安装 delve windows go get -u github.com/go-delve/delve/cmd/dlv
linux 方式一：
go get -u github.com/go-delve/delve/cmd/dlv
方式二
$ git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve$ cd $GOPATH/src/github.com/go-delve/delve$ make install 注意: 若果你go版本为1.5需要设置GO15VENDOREXPERIMENT=1
OSX $ go get -u github.com/go-delve/delve/cmd/dlv 执行上述代码前，确保你电脑上有编译工具
设置 launch.json 配置文件 ctrl+shift+p 输入 Debug: Open launch.json 打开 launch.json 文件，如果第一次打开,会新建一个配置文件，默认配置内容如下
{&amp;#34;version&amp;#34;: &amp;#34;0.2.0&amp;#34;,&amp;#34;configurations&amp;#34;: [{&amp;#34;name&amp;#34;: &amp;#34;Launch&amp;#34;,&amp;#34;type&amp;#34;: &amp;#34;go&amp;#34;,&amp;#34;request&amp;#34;: &amp;#34;launch&amp;#34;,&amp;#34;mode&amp;#34;: &amp;#34;auto&amp;#34;,&amp;#34;program&amp;#34;: &amp;#34;${fileDirname}&amp;#34;,&amp;#34;env&amp;#34;: {},&amp;#34;args&amp;#34;: []}]} 常见属性如下</description>
    </item>
    
    <item>
      <title>vscode 自定义代码片段</title>
      <link>https://www.yunxicoding.top/post/ide/vs-code/snippet/</link>
      <pubDate>Sat, 09 Mar 2019 17:54:58 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/ide/vs-code/snippet/</guid>
      <description>实现效果 起因 最近在写一个全新的项目，在项目中频繁创建各种类，这就导致很多重复的东西需要频繁的写，例如类名，命名空间，继承关系&amp;hellip;那么有没有一种 办法能解决这个问题呢？
提出设想 我想起了，最初用 sublime text 的时候，可以利用代码片段功能大段的生成html代码，当时就觉得十分的方便，那么 vscode 有没有这个功能呢?经过 google 之后我知道 vscode 是有代码片段功能的。既然有了想法，也具备了基础实施条件，那么接下来开始尝试实现之前的想法。
资料查询 经过一番 google 后发现对于 vscode snippet 介绍都在相对基础的简单应用（只是一些插入固定代码和光标介绍），者显然无法实现我们生成类名和明明空间的想法，google 无果，那么只能看看 vscode 官方文档果然有意想不到的收获，看完官网介绍后，基本就确定此路是可行的。
snippet 示例 在 File &amp;gt; Preferences (Code &amp;gt; Preferences on macOS) 中选择 User Snippets 在弹出框里选择对应的代码片段语言，我这里使用的是php
&amp;#34;Print to console&amp;#34;: {&amp;#34;prefix&amp;#34;: &amp;#34;log&amp;#34;,&amp;#34;body&amp;#34;: [&amp;#34;console.log(&amp;#39;$1&amp;#39;);&amp;#34;,&amp;#34;$2&amp;#34;],&amp;#34;description&amp;#34;: &amp;#34;Log output to console&amp;#34;} 在打开的 php.json 中有示例代码：
Print to console 代码片段名称 prefix 插件前缀 body 插件内容可以是字符串，也可以为数组，若为数组每个元素都做为单独的一行插入。 description 插件描述 Snippet 语法 制表位(Tabstops) 使用制表位(Tabstops)可是在代码片段中移动光标位置，使用$1,$2来指定光标的位置,数字代表光标的移动的顺序，值得注意的时$0代表光标的最后位置。如果有多个相同的制表位(Tabstops)会在编译器里同时出现多个光标（类似编译器的块编辑模式）。</description>
    </item>
    
    <item>
      <title>编译安装php常见问题</title>
      <link>https://www.yunxicoding.top/post/php/make-install-faq/</link>
      <pubDate>Sun, 24 Feb 2019 15:04:42 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/php/make-install-faq/</guid>
      <description>configure: error: libxml2 not found. 详细错误如下:
configure: error: libxml2 not found. Please check your libxml2 installation.make: *** No targets specified and no makefile found. Stop.make: *** No rule to make target &amp;#39;install&amp;#39;. Stop. 解决方法：
$ sudo apt-get install libxml2-dev configure: error: png.h not found. 详细错误如下:
configure: error: png.h not found.make: *** No targets specified and no makefile found. Stop.make: *** No rule to make target &amp;#39;install&amp;#39;. Stop.</description>
    </item>
    
    <item>
      <title>virtualbox 分享文件夹，无权访问问题</title>
      <link>https://www.yunxicoding.top/post/linux/not-permission/</link>
      <pubDate>Sun, 24 Feb 2019 14:26:03 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/not-permission/</guid>
      <description>背景 在 windows 下使用 virtualbox 安装 linux 系统 实现 windows 下开发虚拟机里跑测试的效果
起因 由于项目使用文件目录是可以在 windows 和 linux 下共享的，因此使用 virtualbox 共享文件夹功能来实现项目文件夹在两个系统间共享，配置好共享文件后，发现在虚拟机的 linux 系统中没有权限操作目录.
百度之后发现原来是因为共享文件夹挂在后的文件是属于 vboxsf 组下的，而登陆用户不属于该组，因而无权对目录进行操作。
解决方法 既然知道原因，解决办法也是非常简单的就是将当前登录的用户加入 vboxsf 用户组即可：
$ sudo usermod -aG vboxsf $(whoami)
需要注意的是该操作需要重启系统才能生效。</description>
    </item>
    
    <item>
      <title>gorm常见问题</title>
      <link>https://www.yunxicoding.top/post/gorm/faq/</link>
      <pubDate>Sat, 22 Dec 2018 10:38:17 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/gorm/faq/</guid>
      <description>sql: Scan error on column index 5, name &amp;ldquo;created_at&amp;rdquo;: unsupported Scan, storing driver.Value type []uint8 into type *time.Time DATETIME转换问题 执行Rows.Scan()时报告错误
原因是在调用sql.Open()时没有将parseTime设置为True。加入parseTime即可修复问题： db, err := sql.Open(&amp;quot;mysql&amp;quot;, &amp;quot;user:password@tcp(127.0.0.1:3306)/dbname?parseTime=true&amp;quot;)
invalid memory address or nil pointer dereference on simple learning example. Maybe a bug with latest version 代码实例
package mainimport (&amp;#34;net/http&amp;#34;&amp;#34;strconv&amp;#34;&amp;#34;github.com/gin-gonic/gin&amp;#34;&amp;#34;github.com/jinzhu/gorm&amp;#34;_ &amp;#34;github.com/jinzhu/gorm/dialects/postgres&amp;#34;)var db *gorm.DBfunc init() {var err errordb, err := gorm.Open(&amp;#34;postgres&amp;#34;, &amp;#34;host=localhost user=postgres dbname=postgres sslmode=disable password=pass&amp;#34;)if err !</description>
    </item>
    
    <item>
      <title>Error 1292: Incorrect datetime value: &#39;0000-00-00&#39; for column &#39;created_at&#39; at row 1</title>
      <link>https://www.yunxicoding.top/post/sql/gorm-err/</link>
      <pubDate>Sat, 22 Dec 2018 08:48:49 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/sql/gorm-err/</guid>
      <description>使用 gorm 插入数据时爆出 Error 1292: Incorrect datetime value: &#39;0000-00-00&#39; for column &#39;created_at&#39; at row 1 的错误信息，搜索之后发现原来是因为 msyql5.7 及以上不允许时间戳字段插入 ‘0000-00-00’ 数据，修复方法也比较简单，去掉sql_model 中的 NO_ZERO_DATE 配置即可
方式一 sql 配置 使用 select @@GLOBAL.sql_mode; 查出原有的配置信息，将配置信息去除 NO_ZERO_DATE 并复制
使用 SET GLOBAL sql_mode = &#39;去除 NO_ZERO_DATE 后的剩余配置项&#39;
经过以上步骤就可以完成设置了。
方式二 配置文件配置 打开 mysql 配置文件： $ sodo vim /etc/my.cnf 在 [mysqld] 配置项下插入 sql_mode 配置 sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION重启 mysql 完成配置
sql_model 参数详解 ONLY_FULL_GROUP_BY：对于GROUP BY聚合操作，如果在SELECT中的列，没有在GROUP BY中出现，那么这个SQL是不合法的，因为列不在GROUP BY从句中NO_AUTO_VALUE_ON_ZERO：该值影响自增长列的插入。默认设置下，插入0或NULL代表生成下一个自增长值。如果用户 希望插入的值为0，而该列又是自增长的，那么这个选项就有用了。STRICT_TRANS_TABLES：在该模式下，如果一个值不能插入到一个事务表中，则中断当前的操作，对非事务表不做限制NO_ZERO_IN_DATE：在严格模式下，不允许日期和月份为零NO_ZERO_DATE：设置该值，mysql数据库不允许插入零日期，插入零日期会抛出错误而不是警告。ERROR_FOR_DIVISION_BY_ZERO：在INSERT或UPDATE过程中，如果数据被零除，则产生错误而非警告。如 果未给出该模式，那么数据被零除时MySQL返回NULLNO_AUTO_CREATE_USER：禁止GRANT创建密码为空的用户NO_ENGINE_SUBSTITUTION：如果需要的存储引擎被禁用或未编译，那么抛出错误。不设置此值时，用默认的存储引擎替代，并抛出一个异常PIPES_AS_CONCAT：将&amp;#34;||&amp;#34;视为字符串的连接操作符而非或运算符，这和Oracle数据库是一样的，也和字符串的拼接函数Concat相类似ANSI_QUOTES：启用ANSI_QUOTES后，不能用双引号来引用字符串，因为它被解释为识别符 参考链接 Error 1292 on update a row</description>
    </item>
    
    <item>
      <title>Centos 服务配置详解</title>
      <link>https://www.yunxicoding.top/post/linux/centos-service/</link>
      <pubDate>Thu, 20 Dec 2018 17:11:12 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/linux/centos-service/</guid>
      <description>systemd 介绍 CentOS 7的服务systemctl脚本存放在：/usr/lib/systemd/，有系统（system）和用户（user）之分，即：/usr/lib/systemd/system ，/usr/lib/systemd/user
创建 Service 文件 以 Supervisor 加入服务为例
[Unit] Description=Supervisor[Service] Type=forking ExecStart=/usr/bin/supervisord -c /etc/supervisor/supervisord.confExecStop=/usr/bin/supervisorctl shutdownExecReload=/usr/bin/supervisorctl reloadKillMode=process Restart=on-failure RestartSec=42s[Install] WantedBy=multi-user.target 参数介绍 Unit：
NAME DESCRIPTION ExecStartPre 在单元（Unit）开始前执行命令 ExecStart 在单元（Unit）开始时执行主命令 ExecStartPost 在 ExecStart 命令执行完毕之后执行 ExecReload 在单元（Unit）Reload 时执行主命令 ExecStop 在单元（Unit） Stop 时执行命令 ExecStopPost 在 ExecStop 配置命令执行完毕后执行 RestartSec 服务重启失败重试间隔时间 Type : 启动类型simple、forking、oneshot、notify、dbus
Type=simple（默认值）：systemd认为该服务将立即启动。服务进程不会fork。如果该服务要启动其他服务，不要使用此类型启动，除非该服务是socket激活型。 Type=forking：systemd认为当该服务进程fork，且父进程退出后服务启动成功。对于常规的守护进程（daemon），除非你确定此启动方式无法满足需求，使用此类型启动即可。使用此启动类型应同时指定 PIDFile=，以便systemd能够跟踪服务的主进程。 Type=oneshot：这一选项适用于只执行一项任务、随后立即退出的服务。可能需要同时设置 RemainAfterExit=yes 使得 systemd 在服务进程退出之后仍然认为服务处于激活状态。 Type=notify：与 Type=simple 相同，但约定服务会在就绪后向 systemd 发送一个信号。这一通知的实现由 libsystemd-daemon.so 提供。 Type=dbus：若以此方式启动，当指定的 BusName 出现在DBus系统总线上时，systemd认为服务就绪。</description>
    </item>
    
    <item>
      <title>公众号通过api发送小程序消息</title>
      <link>https://www.yunxicoding.top/post/wechat/push-mini/</link>
      <pubDate>Mon, 17 Dec 2018 18:34:50 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/wechat/push-mini/</guid>
      <description>接口调用请求说明 http请求方式: POSThttps://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN 发送普通消息的小程序链接 {&amp;#34;touser&amp;#34;:&amp;#34;OPENID&amp;#34;,&amp;#34;msgtype&amp;#34;:&amp;#34;text&amp;#34;,&amp;#34;text&amp;#34;:{&amp;#34;content&amp;#34;:&amp;#34;文本内容&amp;lt;a href=&amp;#34;http://www.qq.com&amp;#34; data-miniprogram-appid=&amp;#34;appId&amp;#34; data-miniprogram-path=&amp;#34;pages/index/index&amp;#34;&amp;gt;点击跳小程序&amp;lt;/a&amp;#34;}} 说明：
data-miniprogram-appid 项，填写小程序appid，则表示该链接跳小程序； data-miniprogram-path项，填写小程序路径，路径与app.json中保持一致，可带参数； 对于不支持data-miniprogram-appid 项的客户端版本，如果有herf项，则仍然保持跳href中的网页链接； data-miniprogram-appid对应的小程序必须与公众号有绑定关系 返回结果 {&amp;quot;errcode&amp;quot;:0,&amp;quot;errmsg&amp;quot;:&amp;quot;ok&amp;quot;}
发送效果 公众号推送小程序卡片 {&amp;#34;touser&amp;#34;:&amp;#34;OPENID&amp;#34;,&amp;#34;msgtype&amp;#34;:&amp;#34;miniprogrampage&amp;#34;,&amp;#34;miniprogrampage&amp;#34;:{&amp;#34;title&amp;#34;:&amp;#34;title&amp;#34;,&amp;#34;appid&amp;#34;:&amp;#34;appid&amp;#34;,&amp;#34;pagepath&amp;#34;:&amp;#34;pagepath&amp;#34;,&amp;#34;thumb_media_id&amp;#34;:&amp;#34;thumb_media_id&amp;#34;}} 返回结果 {&amp;quot;errcode&amp;quot;:0,&amp;quot;errmsg&amp;quot;:&amp;quot;ok&amp;quot;}
发送效果 参考链接 公众号文档</description>
    </item>
    
    <item>
      <title>redis实现分布式锁</title>
      <link>https://www.yunxicoding.top/post/distributed/redis-lock/</link>
      <pubDate>Fri, 14 Dec 2018 17:47:08 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/distributed/redis-lock/</guid>
      <description>起因 消息队列处理任务中，由于消息队列中有相同的处理请求并且消费端存在多个，因此出现了并发消费的问题，于是产生了利用分布式锁来锁定消费，保证同一时间只有一个请求在处理，找了一下分布式锁的实现方式常见的有：
基于数据库 基于Zookeeper 基于redis 选型 基于数据库相对来说，更直观，实现起来也比较简单，至于要设置一个唯一索引，在插入时判断是否正常插入，不能插入则说明获取锁失败，缺点是会增加数据库开销，增加数据库压力，所以此方案PASS。基于Zookeeper实现分布式锁，会引新的软件，将会增加原有系统的复杂度，因而此方案也PASS。基于 redis 本身是对内存的操作，相对来说效率会非常高，并且原有系统也已经存在了 redis ，因而也不会增加原系统的复杂度，所以选择了 redis 来实现分布式锁。
背景介绍 SETNX key value
将 key 的值设为 value ，当且仅当 key 不存在。若给的 key 已经存在，则 SETNX 不做任何动作。SETNX 是『SET if Not eXists』(如果不存在，则 SET)的简写。
redis&amp;gt; EXISTS job # job 不存在(integer) 0redis&amp;gt; SETNX job &amp;#34;programmer&amp;#34; # job 设置成功(integer) 1redis&amp;gt; SETNX job &amp;#34;code-farmer&amp;#34; # 尝试覆盖 job ，失败(integer) 0redis&amp;gt; GET job # 没有被覆盖&amp;#34;programmer&amp;#34; 当 key 不存在时，返回 1， 如果设置的 key 存在时则返回 0 ，该操作本身具有原子性，因此可以利用 redis 此特性进行分布式锁实现的基础。</description>
    </item>
    
    <item>
      <title>gitalk评论插件自动初始化</title>
      <link>https://www.yunxicoding.top/post/hugo/auto-init-gitalk/</link>
      <pubDate>Thu, 29 Nov 2018 18:44:33 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/hugo/auto-init-gitalk/</guid>
      <description>这只是个预告，耐心等待把。😀</description>
    </item>
    
    <item>
      <title>hugo集成girment插件</title>
      <link>https://www.yunxicoding.top/post/hugo/enable-gitment/</link>
      <pubDate>Tue, 27 Nov 2018 18:57:45 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/hugo/enable-gitment/</guid>
      <description>起因 使 hugo 搭建博客已经有半年了，一直觉得用 markdown 才写博客才程序员应该有的范，所以看到 hugo 就觉得自己应该有一个这样的博客，后面就自己搭建起来。最近，写博客的频率越来越高，也越来越觉得交互对一个博客的重要性，于是集成评论系统的优先级变得越来越高。
最开始也想着直接集成现有的国内评论插件，后台喜欢的评论插件死的死，不死的也半死，于是把目光放在了 gitment 上，对于 gitment 的集成相对简单，其中也有一些小坑，于是便有了这篇文章,希望能够帮到有同样想法的小伙伴。
1. 简介 Gitment 是一款基于 GitHub Issues 的评论系统，支持在前端直接引入，不需要任何后端代码。可以在页面进行登录、查看、评论、点赞等操作，同时有完整的 Markdown / GFM 和代码高亮支持。尤为适合各种基于 GitHub Pages 的静态博客或项目页面Gitment是一款基于 GitHub Issues 的评论系统，支持在前端直接引入，不需要任何后端代码。可以在页面进行登录、查看、评论、点赞等操作，同时有完整的 Markdown / GFM 和代码高亮支持。尤为适合各种基于 GitHub Pages 的静态博客或项目页面。
2. 注册 到https://github.com/settings/applications/new ,中注册一个新的OAuth Application 其中 Authorization callback URL 写你的网站域名就可以了，例如我这里填写的是：http://www.artacode.com/ 注册完成后会得到 Client ID 和 Client Secret 两个值用于为们后面引入 gitment
3. 引入 增加评论开关：
sudo vim $HUGO_PATH/config.toml...[params]enableGitment = true...//保存退出 在 themes/beautifulhugo/layouts/_default/single.html 文件中，合适的位置增加如下代码</description>
    </item>
    
    <item>
      <title>etcd入门系列四：开启客户端证书访问</title>
      <link>https://www.yunxicoding.top/post/etcd/certificates/</link>
      <pubDate>Sun, 25 Nov 2018 14:26:14 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/etcd/certificates/</guid>
      <description>etcd入门系列 一. etcd在docker中的安装与使用
二. etcd 开启 https
三. 身份验证访问控制
四. 开启客户端证书访问
1. 生成客户端证书 生成client.json：
$ cfssl print-defaults csr &amp;gt; client.json编辑 client.json 的修改 CN 值为 cliet
...&amp;#34;CN&amp;#34;: &amp;#34;client&amp;#34;,&amp;#34;hosts&amp;#34;: [&amp;#34;&amp;#34;],... 生成客户端证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client将会得到如下文件
client-key.pemclient.csrclient.pem 2. 开启客户端证书检测 docker run -v /root/cfssl:/root/cfssl -p 2379:2379 --name etcd-net etcd /usr/local/bin/etcd -name etcd-net --client-cert-auth --trusted-ca-file=/root/cfssl/ca.pem --cert-file=/root/cfssl/server.pem --key-file=/root/cfssl/server-key.pem -advertise-client-urls https://0.0.0.0:2379 -listen-client-urls https://0.0.0.0:2379 --client-cert-auth:当这个选项被设置时，etcd 将为受信任CA签名的客户端证书检查所有的传入的 HTTPS 请求，不能提供有效客户端证书的请求将会失败。</description>
    </item>
    
    <item>
      <title>etcd入门系列三：身份验证访问控制</title>
      <link>https://www.yunxicoding.top/post/etcd/auth/</link>
      <pubDate>Sat, 24 Nov 2018 12:05:50 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/etcd/auth/</guid>
      <description>etcd入门系列 一. etcd在docker中的安装与使用
二. etcd 开启 https
三. 身份验证访问控制
四. 开启客户端证书访问
1. 简介 etcd 默认是没有开启访问控制的，如果我们开启外网访问的话就需要考虑访问控制的问题，etcd 提供了两种访问控制的方式：
基于身份验证的访问控制 基于证书的访问控制 这节主要是选择第一种方式，进行的讲解，由于之前文章中是采用http接口的方式通讯，为了更全面的了解 etcd 的使用，本节使用官方提供的 etcdctl 工具进行与服务器之间的通讯。
2. root 用户 root 是 etcd 的超级管理员，拥有 etcd 的所有权限，在开启角色认证之前为们必须要先建立好 root 用户。还需要注意的是 root 用户必须拥有 root 的角色,允许在 etcd 的所有操作.
3. root 角色 root 角色可以赋予任何用户，拥有 root 角色的用户有全局读写权限和集群身份验证配置权限，此外，还具有修改集群成员身份，碎片整理，建立快照等权限。
4. 用户操作 查看用户列表：
$ etcdctl --ca-file /root/cfssl/ca.pem --endpoints https://192.168.3.3:2379 user list创建用户：
$ etcdctl --ca-file /root/cfssl/ca.pem --endpoints https://192.168.3.3:2379 user add user1用户可以被赋予角色，也可以被撤销角色：
#赋予权限$ etcdctl --ca-file /root/cfssl/ca.</description>
    </item>
    
    <item>
      <title>etcd入门系列二：etcd开启https</title>
      <link>https://www.yunxicoding.top/post/etcd/enable-https/</link>
      <pubDate>Wed, 21 Nov 2018 13:44:07 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/etcd/enable-https/</guid>
      <description>etcd入门系列 一. etcd在docker中的安装与使用
二. etcd 开启 https
三. 身份验证访问控制
四. 开启客户端证书访问
上节(etcd在docker中使用)etcd已经可以正常使用,这节讲讲如何开启htpps
1 生成签名 1.1 下载 cfssl mkdir ~/bincurl -s -L -o ~/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64curl -s -L -o ~/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64chmod +x ~/bin/{cfssl,cfssljson}export PATH=$PATH:~/bin 需要注意的是,这里是以linux的64位为例,如果你是其他系统,请修改成对应系统的cfssl包
1.2 初始化证书 使用cfssl模板生成ca-config.json,ca-csr.json
mkdir ~/cfsslcd ~/cfsslcfssl print-defaults config &amp;gt; ca-config.jsoncfssl print-defaults csr &amp;gt; ca-csr.json 1.3 证书介绍 客户端证书(client certificate):用于服务器对客户端进行身份验证.例如 etcdctl, etcd proxy, docker 等客户端. 服务器证书(server certificate):服务器端使用,用于客户端验证服务器身份.例如 docker 服务器, kube-apiserver 对等证书(peer certificate):用于etcd集群间的双向通讯 1.</description>
    </item>
    
    <item>
      <title>etcd入门系列一：etcd 在 docker 中使用</title>
      <link>https://www.yunxicoding.top/post/etcd/install/</link>
      <pubDate>Mon, 19 Nov 2018 11:03:08 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/etcd/install/</guid>
      <description>etcd入门系列 一. etcd在docker中的安装与使用
二. etcd 开启 https
三. 身份验证访问控制
四. 开启客户端证书访问
简介 etcd是CoreOS团队于2013年6月发起的开源项目，它的目标是构建一个高可用的分布式键值(key-value)数据库。etcd内部采用raft协议作为一致性算法，etcd基于Go语言实现。
etcd作为服务发现系统，有以下的特点
简单：安装配置简单，而且提供了HTTP API进行交互，使用也很简单 安全：支持SSL证书验证 快速：根据官方提供的benchmark数据，单实例支持每秒2k+读操作 可靠：采用raft算法，实现分布式系统数据的可用性和一致性 etcd项目地址：https://github.com/coreos/etcd/ 构建镜像 1.创建dockersfile$ sudo touch Dockerfile
2.$ sudo vim Dockerfile编辑Dockerfile文件,写入内容如下:
FROM alpine:latestADD etcd /usr/local/bin/ADD etcdctl /usr/local/bin/RUN mkdir -p /var/etcd/RUN mkdir -p /var/lib/etcd/# Alpine Linux doesn&amp;#39;t use pam, which means that there is no /etc/nsswitch.conf,# but Golang relies on /etc/nsswitch.conf to check the order of DNS resolving# (see https://github.</description>
    </item>
    
    <item>
      <title>Windows修改命令行标题</title>
      <link>https://www.yunxicoding.top/post/windows/modify-command-title/</link>
      <pubDate>Thu, 15 Nov 2018 11:32:31 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/windows/modify-command-title/</guid>
      <description>修改命令行标题 cmd title new title
Console2 title new title
PowerShell [System.Console]::Title = &amp;quot;new title&amp;quot;</description>
    </item>
    
    <item>
      <title>swoft openWhere使用指南</title>
      <link>https://www.yunxicoding.top/post/swoft/where/</link>
      <pubDate>Thu, 15 Nov 2018 10:47:05 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/swoft/where/</guid>
      <description>open/close where 理解 openWhere 为where条件的左边括号即( closeWhere 为where条件的右边括号即) 实例 //默认连接符为ANDQuery::table(&amp;#34;order&amp;#34;)-&amp;gt;openWhere()-&amp;gt;where(&amp;#39;id&amp;#39;,&amp;#39;1&amp;#39;)-&amp;gt;where(&amp;#39;state&amp;#39;, 3)-&amp;gt;closeWhere()-&amp;gt;openWhere()-&amp;gt;where(&amp;#39;id&amp;#39;, 3)-&amp;gt;where(&amp;#39;state&amp;#39;, 2)-&amp;gt;closeWhere()-&amp;gt;one()-&amp;gt;getResult();//执行结果为:SELECT * FROM `order_1` WHERE ( `id` = &amp;#39;1&amp;#39; AND `state` = &amp;#39;3&amp;#39;) AND ( `id` = &amp;#39;3&amp;#39; AND `state` = &amp;#39;2&amp;#39;) LIMIT 0,1//设置连接符为orQuery::table(&amp;#34;order&amp;#34;)-&amp;gt;openWhere()-&amp;gt;where(&amp;#39;id&amp;#39;,&amp;#39;1&amp;#39;)-&amp;gt;where(&amp;#39;state&amp;#39;, 3)-&amp;gt;closeWhere()-&amp;gt;openWhere(QueryBuilder::LOGICAL_OR)-&amp;gt;where(&amp;#39;id&amp;#39;, 3)-&amp;gt;where(&amp;#39;state&amp;#39;, 2)-&amp;gt;closeWhere()-&amp;gt;one()-&amp;gt;getResult();//执行结果SELECT * FROM `order_1` WHERE ( `id` = &amp;#39;1&amp;#39; AND `state` = &amp;#39;3&amp;#39;) OR ( `id` = &amp;#39;3&amp;#39; AND `state` = &amp;#39;2&amp;#39;) LIMIT 0,1 </description>
    </item>
    
    <item>
      <title>Sqlite3 stdlib.h: No such file or directory</title>
      <link>https://www.yunxicoding.top/post/golang/sqlite3-error/</link>
      <pubDate>Sun, 23 Sep 2018 12:29:55 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/sqlite3-error/</guid>
      <description>sqlite3/backup.go:14:20: fatal error: stdlib.h: No such file or directory 错误提示 # github.com/mattn/go-sqlite3../../../.go/src/github.com/mattn/go-sqlite3/backup.go:14:20: fatal error: stdlib.h: No such file or directorycompilation terminated. 解决方法 sudo apt-get install g++
参考链接 mattn/go-sqlite3/issues</description>
    </item>
    
    <item>
      <title>xorm将表生成实体</title>
      <link>https://www.yunxicoding.top/post/golang/xorm-reverse/</link>
      <pubDate>Sun, 16 Sep 2018 17:24:20 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/golang/xorm-reverse/</guid>
      <description>xorm 工具 xorm 是一组数据库操作命令行工具。
源码安装 go get github.com/go-xorm/cmd/xorm
使用 使用帮助 查看帮助命令： xorm help reverse 帮助详情
usage: xorm reverse [-s] driverName datasourceName tmplPath [generatedPath] [tableFilterReg]according database&amp;#39;s tables and columns to generate codes for Go, C++ and etc.-s Generated one go file for every tabledriverName Database driver name, now supported four: mysql mymysql sqlite3 postgresdatasourceName Database connection uri, for detail infomation please visit driver&amp;#39;s project pagetmplPath Template dir for generated.</description>
    </item>
    
    <item>
      <title>docker mysql启动报错</title>
      <link>https://www.yunxicoding.top/post/docker/mysql/</link>
      <pubDate>Sat, 15 Sep 2018 16:22:17 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/mysql/</guid>
      <description>错误信息 2018-09-15T08:11:26.620005077Z 2018-09-15T08:11:26.619857Z 0 [ERROR] [FATAL] InnoDB: Table flags are 0 in the data dictionary but the flags in file ./ibdata1 are 0x4800!2018-09-15T08:11:26.620023933Z 2018-09-15 08:11:26 0x7f7203c4b740 InnoDB: Assertion failure in thread 140127666222912 in file ut0ut.cc line 942 解决方法 原因是宿主机data目录不为空照成; 删除默认data目录：/var/lib/mysql</description>
    </item>
    
    <item>
      <title>nodejs常见问题</title>
      <link>https://www.yunxicoding.top/post/nodejs/faq/</link>
      <pubDate>Sat, 15 Sep 2018 12:44:30 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/nodejs/faq/</guid>
      <description>错误1 npm ERR! Linux 4.15.0-34-genericnpm ERR! argv &amp;#34;/usr/bin/node&amp;#34; &amp;#34;/usr/bin/npm&amp;#34; &amp;#34;install&amp;#34; &amp;#34;--no-bin-links&amp;#34;npm ERR! node v8.10.0npm ERR! npm v3.5.2npm ERR! code EMISSINGARGnpm ERR! typeerror Error: Missing required argument #1npm ERR! typeerror at andLogAndFinish (/usr/share/npm/lib/fetch-package-metadata.js:31:3)npm ERR! typeerror at fetchPackageMetadata (/usr/share/npm/lib/fetch-package-metadata.js:51:22)npm ERR! typeerror at resolveWithNewModule (/usr/share/npm/lib/install/deps.js:456:12)npm ERR! typeerror at /usr/share/npm/lib/install/deps.js:457:7npm ERR! typeerror at /usr/share/npm/node_modules/iferr/index.js:13:50npm ERR! typeerror at /usr/share/npm/lib/fetch-package-metadata.js:37:12npm ERR! typeerror at addRequestedAndFinish (/usr/share/npm/lib/fetch-package-metadata.</description>
    </item>
    
    <item>
      <title>docker常用命令</title>
      <link>https://www.yunxicoding.top/post/docker/command/</link>
      <pubDate>Sat, 15 Sep 2018 09:06:07 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker/command/</guid>
      <description>docker 查看日志 sudo docker logs -f -t --tail 20 mysql
docker拷贝内容到宿主机 docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH 例如：docker cp 容器名:容器路径 宿主机路径</description>
    </item>
    
    <item>
      <title>docker常见问题</title>
      <link>https://www.yunxicoding.top/post/docker-error/</link>
      <pubDate>Sun, 09 Sep 2018 15:54:18 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/docker-error/</guid>
      <description>docker pull 失败 错误信息 Get https://registry-1.docker.io/v2/: dial tcp: lookup registry-1.docker.io: no such host
解决方法 将dns修改成8.8.8.8就可以解决此问题</description>
    </item>
    
    <item>
      <title>Supervisor 安装与使用</title>
      <link>https://www.yunxicoding.top/post/supervisor/</link>
      <pubDate>Tue, 04 Sep 2018 11:22:14 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/supervisor/</guid>
      <description>简介 supervisor管理进程，是通过fork/exec的方式将这些被管理的进程当作supervisor的子进程来启动，所以我们只需要将要管理进程的可执行文件的路径添加到supervisor的配置文件中就好了。此时被管理进程被视为supervisor的子进程，若该子进程异常中断，则父进程可以准确的获取子进程异常中断的信息，通过在配置文件中设置autostart=ture，可以实现对异常中断的子进程的自动重启。
安装 sudo apt-get install supervisor
配置 进入配置文件目录 : cd /etc/supervisor/conf.d 新建配置文件 sudo vim test.conf 配置内如如下：
[program:cow] command=/home/argericy/workspace/test #要执行的命令autostart=true #自动启动autorestart=true #自动重启user=root #执行命令用户log_stderr=truelogfile=/var/log/test.log #日志文件stopsignal=INT[supervisord] 参数解释 ;[program:theprogramname];command=/bin/cat ; 程序执行命令，需要;process_name=%(program_name)s ; process_name expr (default %(program_name)s);numprocs=1 ; 进程启动数量，默认为1;directory=/tmp ; 工作目录，如果配置了工作目录，command 配置项 可以直接执行工作目录里的命令;umask=022 ; 设置进程权限 默认为：none;priority=999 ; 设置优先级 默认：999;autostart=true ; 在 supervisor 启动时启动程序，默认为：true;startsecs=1 ; 程序启动 startsecs 秒 后视为程序启动成功， 默认为：1;startretries=3 ; 当程序启动失败后最大尝试次数,默认为：3;autorestart=unexpected ; 其值为可以设置为 true 、false 、expected 设置为 true 无论什么情况下都自动重启， 设置为 false 则不自动重启，设置 unexpected 则配合 exitcodes 使用，只有在 exitcodes 配置的中的退出代码 才会自动重启;exitcodes=0,2 ; autorestart 设置为 unexpected 此项生效，默认为： 0,2;stopsignal=QUIT ; 用于 kill 进程的信号 默认为： TERM;stopwaitsecs=10 ; 停止进程等待秒数，默认为：10;stopasgroup=false ; 发送停止信号给 UNIX 进程组 默认为：false;killasgroup=false ; 发送立刻中止信号给 UNIX 进程组，默认为：false;user=chrism ; 设置启动程序的用户;redirect_stderr=true ; redirect proc stderr to stdout (default false);stdout_logfile=/a/path ; 输入日志配置;stdout_logfile_maxbytes=1MB ; 日志容量大与1m则分割日志;stdout_logfile_backups=10 ; 备份文件数量，默认为10;stderr_logfile=/a/path ; 标准错误日志输出;stderr_logfile_maxbytes=1MB ; 日志容量大与1m则分割日志;stderr_logfile_backups=10 ; 备份文件数量，默认为10;serverurl=AUTO ; override serverurl computation (childutils) 使用 supervisorctl Supervisorctl 是 supervisord 的一个命令行客户端工具，启动时需要指定与 supervisord 使用同一份配置文件，否则与 supervisord 一样按照顺序查找配置文件。</description>
    </item>
    
    <item>
      <title>freeRADIUS搭建</title>
      <link>https://www.yunxicoding.top/post/freeradius/</link>
      <pubDate>Sun, 19 Aug 2018 11:05:29 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/freeradius/</guid>
      <description>系统要求 操作系统 CentOS 7.0 x86_64 网址：http://www.centos.org/
所需软件 FreeRADIUS 3.0 网址：http://freeradius.org/ 目前最新版本为3.0.6 MariaDB 网址：https://mariadb.com/ 目前最新版本为10.3.9 注：MariaDB数据库管理系统是MySQL的一个分支，主要由开源社区在维护，采用GPL授权许可。开发这个分支的原因之一是：甲骨文公司收购了MySQL后，有将MySQL闭源的潜在风险，因此社区采用分支的方式来避开这个风险。 MariaDB的目的是完全兼容MySQL，包括API和命令行，使之能轻松成为MySQL的代替品。MariaDB虽然被视为MySQL数据库的替代品，但它在扩展功能、存储引擎以及一些新的功能改进方面都强过MySQL。 注意事项 本文提到的所有操作，需要以root身份执行。
FreeRADIUS 3.0的安装 根据官网上的说明，建议采用二进制安装包的形式安装，而不推荐从源代码编译的方式。因此，本次采用二进制安装包的形式安装。.
安装FreeRADIUS 3.0 服务器程序 以root身份登录到终端，执行以下命令：yum install freeradius 输入“y”，继续安装。 安装完毕。 安装FreeRADIUS工具包（freeradius-utils） 这个工具包是用于测试freeradius的，后面会用到。安装方法同前，执行一下命令： yum install freeradius-utils
FreeRADIUS 3.0的测试 修改用户管理配置文件 该文件是/etc/raddb/users，使用vi工具打开，命令如下: sudo vim /etc/raddb/users 打开文件后，查找 steve Cleartext-Password :=&amp;quot;testing&amp;quot; （76-84行）, 取消该段内容的注释。
修改防火墙配置 修改防火墙配置，允许freeradius所使用的端口1812，1813通过。命令如下：
iptables -A INPUT -p udp --dport 1812 -j ACCEPTiptables -A INPUT -p udp --dport 1813 -j ACCEPT 以调试方式启动freeradius 执行以下命令：radiusd –X</description>
    </item>
    
    <item>
      <title>第一篇博客</title>
      <link>https://www.yunxicoding.top/post/firstpost/</link>
      <pubDate>Sun, 19 Aug 2018 11:05:29 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/firstpost/</guid>
      <description>it‘s my life 最喜欢的句子 世界上只有一种英雄主义，就是在认清了生活的本质之后依然热爱生活
最喜欢的乐队 最信奉的信条 大道至简 世上至所有有专业和非专业的划分就是因为这世界太复杂需要专业的人帮我们去把复杂的事情简单化，从而促使我们生活的便利。 将复杂的东西简单话，应该是每个专业的人应该不遗余力要做的事情，而不是去故作高深，弄出一堆复杂的东西去呈现他人。生活中也总少不了有人把简单的事情复杂化，或把复杂的东西搞的更复杂。普通人把这样的人当高人，专业的人不解为何舍近求远。</description>
    </item>
    
    <item>
      <title>hugo安装及部署</title>
      <link>https://www.yunxicoding.top/post/hugoinstall/</link>
      <pubDate>Mon, 06 Aug 2018 19:27:54 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/hugoinstall/</guid>
      <description>&lt;h1 id=&#34;前言&#34;&gt;前言&lt;/h1&gt;
&lt;p&gt;Hugo是一个用Go编写的快速而现代的静态站点生成器，旨在使网站创建再次变得有趣。&lt;br&gt;
Hugo的优势就在于其生成的站点是静态的，因此网站的访问速度是非常快的，其次使用Hugo，可以让你专注于写文章，至于主题和seo Hugo已经很好的解决了这些问题。下面就来讲一下如何用Hugo生成一个站点并用github page部署&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>swoft单元测试</title>
      <link>https://www.yunxicoding.top/post/swoftunit/</link>
      <pubDate>Sun, 05 Aug 2018 11:08:39 +0800</pubDate>
      
      <guid>https://www.yunxicoding.top/post/swoftunit/</guid>
      <description>&lt;h2 id=&#34;11-为什么要使用单元测试&#34;&gt;1.1 为什么要使用单元测试&lt;/h2&gt;
&lt;p&gt;在编写代码的过程中，一定会反复调试保证它能够编译通过。但代码通过编译，只是说明了它的语法正确。无法保证它的语义也一定正确，没有任何人可以轻易承诺这段代码的行为一定是正确的。幸运的是，单元测试会为我们的承诺做保证。编写单元测试就是用来验证这段代码的行为是否与我们期望的一致。有了单元测试，我们可以自信地交付自己的代码，减少后顾之忧。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>微信分享</title>
      <link>https://www.yunxicoding.top/post/wxshare/</link>
      <pubDate>Sat, 14 Jul 2018 09:53:18 +2200</pubDate>
      
      <guid>https://www.yunxicoding.top/post/wxshare/</guid>
      <description>&lt;h3 id=&#34;微信分享步骤&#34;&gt;微信分享步骤&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;引入jｓ&lt;/li&gt;
&lt;li&gt;设置白名单&lt;/li&gt;
&lt;li&gt;引入sdk&lt;/li&gt;
&lt;li&gt;实现分享功能&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
  </channel>
</rss>
