Redis 事件机制是如何实现的?
前言我们都知道,Redis 是单线程(非严谨),你是否想过,一个线程要如何处理来自各个客户端的各种请求呢?它忙的过来吗?没错,它还真的能忙过来,并且还井井有条。其中多亏了 IO 多路复用,而不仅仅是它,事件机制在其中也是一个不错的设计。
之前我提到过有关于 IO 多路复用对于 Redis 的影响,IO多路复用和多线程会影响Redis分布式锁吗? 其中有部分内容其实已经提到了,所以本文会更加关注于事件机制本身。
PS:Redis 高版本已经支持多线程处理某些事情,为了简化,这里不做讨论,故下文出现的单线程仅是描述那些必须单线程执行的场景。
前置知识
IO 多路复用
尝试思考首先,让我们来思考一下,如果是我们自己来实现,会尝试如何去做。
对于请求连接处理的思考最笨的方法,那么就是来一个客户端 accept 一次,然后给什么请求做什么事情,先来先做,做完走人,对吧。那显然这样太慢了,要知道作为一个缓存,这样设计要把人给急死。
当然,我们也可以说,来一个我开一个线程单独处理你,相当于你一来我就单独找人为你服务,而服务的人最终会将请求给到一个处理中心,让处理中心统一去处理,然后将结果返回。但显 ...
Redis 主从复制的机制浅析
前言今天继续来看看有关 Redis 的一个问题,主从复制。通常,对于大多数的场景来说,读比写更多,于是对于缓存的水平扩展,其中的一个方式 “主从复制” 就是一个常见的思路。有了主从复制,那么可以扩展出很多从节点来应对大量的读请求。那么问题来了 Redis 的主从复制是如何实现的呢?
PS:本文仅关心复制的机制,不关心主节点下线重新选等等异常情况
前置知识
你需要知道 Redis 的持久化方式,RDB 和 AOF
Redis 执行命令的基本思路
审题题目本身不复杂,提问者问这个问题的想法可能会有下面几个方面
了解 Redis 的主从复制机制的话,如果在实际使用过程中出现问题就更容易排查
在设计复制机制的时候需要注意和考虑什么问题
这样的设计是否能应用在别的场景中
尝试思考假设你完全没有看过 Redis 源码来思考这个问题,可以从下面几个角度去尝试分析,并猜测答案。
首先,想到一个关系户,也就是我们常用的 Mysql,它也有主从复制,如果你了解 binlog 那么可以尝试从这里着手,虽然不同,但思路应该是差不多的。
然后,简化问题,主从复制,无非就是将数据发送过去,对方接受保存
不 ...
Redis 的 set nx 底层怎么实现的?
前言首先,非常难得这个系列重新开始更新了,因为之前一直在纠结选题,很多时候我会觉得一些面试题或者是提问很没有实际意义,就好像是为了八股文而八股文,而后面渐渐发现,只要你去追寻背后的一些思考,还是能留下些东西的。
审题这个问题其实本身很”简单”,那么只有两种可能一种你看过 Redis 的源码,一种是没看过进行猜测。不过首先我们可以说一些前奏:
Redis 一开始是有 setnx 这个命令的,后来废弃了,而将 nx 作为 set 的一个参数项,同时也就支持指定过期时间
这个命令的功能就是 set 一个 kv,如果 k 存在则失败,如果 k 不存在就成功 set 这个 kv
我们常常会用 Redis 的 set nx 来实现分布式锁,所以估计提问者想确保你了解原理,从而使用分布式锁的时候更加安心,或者想通过这个问题来引出分布式锁的问题
猜测我们首先可以大胆猜测一下实现方式。可以直接先简化一下问题,本质就是给你一个 map,然后实现一个 setnx 方法,当 k 存在则直接失败。最关键的问题就是解决并发问题。
那解决并发的问题,能想到的就是要么锁,要么 cas,要么直接队列卡死,对 ...
如何使用 xorm 在执行前改写 SQL
前言有时候你需要再 SQL 执行之前对于 SQL 语句进行改写,有可能是修改表名字段名,有可能只是添加注释,这些看起来奇怪的操作其实有时候是为了帮助在数据库之前的 proxy 来实现某些功能,比如最常见的分库分表,读写分离,多租户等等。
举个具体的例子:有些数据库中间件支持在 SQL 语句之前添加注释来实现读写分离https://help.aliyun.com/document_detail/477438.html支持在SQL语句前加上/*FORCE_MASTER*/或/*FORCE_SLAVE*/强制指定这条SQL的路由方向
所以当我们使用 orm 库的时候,就需要有一个类似钩子的东西,能在执行之前想办法将 sql 改写为所需要的样子,这就是今天的需求。
尝试过程
如果你只想知道如何使用,可跳过本段,直接去看最后的实现部分
一开始我做了各种尝试,由于 xorm 本身其实并没有相关文档说明,寻找并尝试了半天,虽然最后实现了,但是路径比较曲折。
尝试 1 ContextHook最开始我想到的就是肯定是 Hook,不错,如我所料,确实有 Hook,并且里面有执行的 SQL,我非常高兴 ...
Tailscale 构建私有网络访问家中设备
前言在此前,我都是通过一些硬件设备来构建一个私有网络,并且能有一个稳定的公网 IP,外部可以通过设备厂商对应的外部资源来构建一个私有网络,随时随地访问家中设备,如:NAS 。但,人生无常,大肠包小肠,最近很不稳定,于是准备了一个后手方案,防止意外。
之前,就有了解过各种方案,其中 Tailscale 是其中一个,比较青睐它的协议本身,并且现在已经可以自建,协议本身也开源,于是最近就折腾起来了。
一开始了解到 Tailscale 并不是因为它本身,而是它写的一篇有关 NAT 的一篇博客,我觉得是原理解释的非常清晰的一篇博客了,所以我就顺便去看了它本身是做什么的。https://tailscale.com/blog/how-nat-traversal-works/
有关它本身的原理我建议直接看官方文档,直接看配图就能懂 https://tailscale.com/kb/1151/what-is-tailscale/
安装
安装非常简单,我不过多赘述,记得先去官网注册一个账号
Linuxhttps://tailscale.com/download/linux
Mac
由于 AppS ...
《程序员的底层思维》读后感
前言偶然得到这本书,之前确实没有听说过这本书。只要是书,我都如获至宝,读完之后,也有些感触。
方法论或者模型到底有用吗?本书介绍了许多思维方式,在很多看来会比较虚,由于将的都是方法论和模型,只有中间穿插了个人经历和实际项目。那么,很多人就会问了,有用吗?
很负责任的告诉你,有用。
因为很多人看完之后,会觉得在心里没有留下什么东西,又没有实际对自己的工作有所帮助,但其实书中很多思维方式都是经验总结。只有当你触发特殊场景的时候才会“刷”到存在感。
阶段 1在思维上面,我也经历了几个阶段。一开始就想所有人一样,问题来了就思考如何解决,想到解决方案了,对比一下,没问题,解决。
阶段 2学到了一些模型,感觉有用,于是在思考问题的时候,就套用各种模型。比如常见的四象限,或者金字塔,又或是冰山。然后就发现了一个问题,用了和没用好像一样?对,好像就是一样的,即使不用我感觉也是一样的。
阶段 3于是我很长时间都弃用了,而又一次让我捡起来的时候,是之前一家公司的一个运维同学。在对比某两个部署方案的时候,我虽然不在运维组,但是我也尝试思考了一下。之后他在说明方案的时候,就是用了一个多维度的模型,当模型出现的 ...
MacOS 禁止 option 输出特殊字符
前言我之前很烦恼 MacOS Option + 任意键 会输出 类似 åçΩçƒåß 这样的特殊字符。我根本没必要去输入这些特殊字符,很多 IDE 的键盘快捷操作会使用 option 加字母的方式来操作,比如 git 提交,在 IDEA 里面就是 option + i 的操作,这样就会在 commit 信息里面带有一个特殊字符。然后官方有没有设置可以关闭,这个问题一直困扰我很久了,最近才得到一个可行的解决方案。
解决方案下载自定义键盘布局 layouthttps://gist.github.com/haosdent/573ea124e5ea666fc576
复制到对应目录
1~/Library/Keyboard\ Layouts/x_layout.keylayout
添加对应自定义键盘布局
然后就可以了,只要你使用这个键盘布局进行输入的时候,那么 option 键 + 字母就会失效。
如果你和我一样使用了第三方的输入法,还需要在第三方输入法对应的配置项中找到键盘布局并修改,我目前使用的是搜狗输入法,配置位置如下:
不过,需要注意的是,有时候突然使用搜狗输入法这个键盘布局会失效(依旧 ...
MacOS 下 zsh 执行长时间命令完成后自动提醒
前言最近正在使用 Warp 作为常用的终端,有一些细节体验做的真的很不错,无论是命令提示还是补全都很厉害。其中有一个细节我认为对于我来说非常实用,就是长时间执行完成之后的命令提醒。
举个例子🌰:当你在终端进行 make 编译或者打包镜像的时候,往往需要比较长的时间,而你一般不会一直等着它执行完成,而是执行完成之后就切到别的地方去工作(摸鱼)了,然后你无法及时知道命令执行完成了,而只能是不是过来看下执行情况。
而在 warp 执行命令完成之后,就有这样一个通知提醒,我就能时刻关注到命令什么时候执行完成了。
当然,这必须依赖于你使用 warp,但我们常常在 IDE 下方的命令行中执行命令,而非单独使用其他终端,因为切换窗口也比较麻烦。于是乎我就想,能否利用 zsh 来实现这个功能呢?
实现1vim ~/.zshrc
123456789101112131415161718# 设置要匹配的字符串string_to_match=("docker build" "go build" "echo linkinstar")# 定义函数f ...
《Go语言设计模式》读后感
前言其实对于设计模式,我早早在大学的时候就啃过《Head First 设计模式》《大话设计模式》。当时虽然对于设计模式本身的使用不够,但对于为什么会有设计模式已经设计模式的意义已经深入人心。
当年写的博客:你所学习的设计模式到底有什么用?到底怎么用? (现在看来还有点羞耻,当年大学的我居然是这样的我)
转到当下,其实在实际的工作中都是一种潜移默化的影响,有时候我真的没有意识到这是某种设计模式,而我会这样去编写我的代码。
本书的优点以 go 语言去实现了各种设计模式,去讲述了各种设计模式,有一说一,从一个初学者的角度它是 OK 的,常用的设计模式均有涉及。对我来说复习了一遍,加深了印象。对于设计模式其中具体的示例代码来说对于小白来说是友好的,这些案例我在网上也常见到,特别看到访问者模式的“圆形”“正方形”的时候。不过从我的角度来说,其实我更希望看到的案例是,直接那 K8S 源码里面的的访问者模式来说明,比如 JSON 格式和 YAML 格式的访问等等。当然作者肯定有自己的考虑,如果这种案例其实需要读者对于这些开源框架有一定的基础,否则直接上来读,这样的案例不够容易上手模仿。
可以改进的 ...
Golang 实现与 crypto-js 一致的 AES 简单加解密
前言最近一直在折腾 Golang 的 AES 加密解密,最初的一个小需求只是寻求一个简单直接的加密工具而已,但是找着找着发现里面的坑太深了…
吐槽:对于加密解密,其实我们很多时候并没有特别高的要求(复杂)。一开始,我最直接的一个想法就是:
调用一个方法,传递一个秘钥,完成加密;
调用一个方法,传递一个秘钥,完成解密,
就可以了,但事实网上纷繁复杂的实现让我头疼。难道,就没有一个让我最省心、简单、最快、实现一个加解密的方法吗?
目标
我要一个对称加密,加解密用的 key 一致
加密后的数据 = 加密方法(数据, key)
解密后的数据 = 解密方法(数据, key)仅此而已,但寻变网络各种类库,没意外,各有各的问题,下面我列举几个我在做的过程中遇到的问题和坑
问题
AES 有各种加密模式 CBC、ECB、CTR、OCF、CFB 选哪个?都安全吗?
AES 在某些加密模式下需要指定 IV 也就是初始向量(那我岂不是又要弄一个配置项?)
AES 对于 key 的长度 和 IV 的长度都有要求 (这个很烦,就像我定一个密码还非得是固定长度的)
AES 需要加密的数 ...
如何搭建文章结构——常见技术文章的逻辑框架
不知不觉,写博客 6 年了,在写博客的这些年里面,其实更多的是在技术上不断地学习,对于写作本身的总结,更多的体现在了文章本身的改变,而我还没有自我总结过一篇针对于技术写作的博客,正好最近有着这样的机会,需要做一个这方面的分享,下面就是分享的内容的讲稿,也正是我对于这些年写作的总结。
无论你现在处于写博客的什么阶段,希望下面的内容能帮助你这些:
逻辑清晰、快速成文
吸引读者、正向激励
开场白
输出倒逼输入
作为一个技术人,我们往往都沉浸于技术的海洋中学习。 在学习技术的过程中,我们不断的在输入知识,同时我们也需要关注输出。 输出是也一种学习的过程,无论是写博客、写文章、写书,还是做分享、做演讲,都是一种输出的过程。有时输出是你坚持学习的动力,有时是你巩固和总结学习的过程,有时是你向别人传递知识的方式。”费曼学习法” 其实就是输出的一种形式,它的核心思想是:如果你想要学习一件事,那么你就需要把这件事教给别人。
说到输出,其中一个非常重要的技能就是写作,写作是一种思考的过程,也是一种沟通的方式。 在写作的过程中,我们需要考虑的问题有很多,比如如何取标题、封面配图、如何搭建文章结 ...
技术文章如何取标题、封面、配图
不知不觉,写博客 6 年了,在写博客的这些年里面,其实更多的是在技术上不断地学习,对于写作本身的总结,更多的体现在了文章本身的改变,而我还没有自我总结过一篇针对于技术写作的博客,正好最近有着这样的机会,需要做一个这方面的分享,下面就是分享的内容的讲稿,也正是我对于这些年写作的总结。
无论你现在处于写博客的什么阶段,希望下面的内容能帮助你这些:
如何取合适的标题
配图的注意点
快速制作封面
引入当我们有了一个好的想法和内容,并且有了合适的写作框架,构建一篇不错的技术文章就不是什么难事了。但是,如果你想让你的文章有更多的阅读量,那么你就需要花一些时间来构建一个好的标题和封面配图。对于一篇文章,内容肯定是关键,但如果标题无法吸引读者,很多时候,读者就很难点进来看。
然后,就是对于封面和配图,正所谓 “一图胜千言” ,特别是在技术文章中,很多时候配图可以帮助读者更好的理解文章的内容。特别是在一些难以理解的架构设计或者代码设计上。
话不多说,那么,作为一个技术文章应该如何去拟定你的标题呢?
如何取标题有一段时间,我非常非常痛恨微信公众号的推送,因为那段时间里,无论是什么样的技 ...
Google Golang Style 个人小结
前几个月就看到 Google 有了 Golang 这个规范,但是一直没有时间去看。最近仔细看了一下,其中有几个点,之前搬砖的时候还没有注意到,所以记录一下。本文仅针对于我个人针对这个规范的小结,建议有时间的同学去看看原文,毕竟每个人查缺补漏的地方不一样。
原文:https://google.github.io/styleguide/go/
译文:https://gocn.github.io/styleguide/
包名称全小写
包名称Package names 不应该有下划线,例如,包 tabwriter 不应该命名为 tabWriter、TabWriter 或 tab_writer。 Link
我们有时候会不得不出现包名需要两个单词来描述的情况。在没有了解到这个规则之前,确实我很多命名的时候还是会选择使用下划线进行分隔来命名包名。原因有两个,一个是之前 C 的影响,一个是由于全小写难以辨认,故会使用下划线。所以,这个规则以后还是要多注意。
针对方法的命名
函数和方法名称不应使用 Get 或 get 前缀,除非底层概念使用单词“get”(例如 HTTP GE ...
IO多路复用和多线程会影响Redis分布式锁吗?
前言前置知识
Redis 虽然是单线程的,但是它利用了内核的 IO 多路复用,从而能同时监听多个连接
Redis6 出现了可以利用多个 IO 线程并发进行的操作
那么问题来了,这两者会导致我们的分布式锁的原子性有影响吗?
我们知道当我们使用 redis 作为分布式锁的时候,通常会使用 SET key value EX 10 NX 命令来加锁,获得锁的客户端才能成功 SET 这个 key,那么问题来了,这条命令在多线程的情况下是一个原子操作吗?
其实答案是显而易见的,因为 redis 的设计者肯定考虑到了向前兼容的问题,并且也不会让这样的特性消失,所以在问这个问题以前,我虽然不能肯定,但是还是能自信的回答,但没有足够的底气。 今天的目标就是找到真正的原因。
问题的两个方面上锁上锁,没啥多说的直接 SET key value EX 10 NX 就可以了
解锁解锁,有两种:
一种是客户端自行保证锁只有自己拿自己解,那么直接让自己去 DEL 就可以了
另一种是不信任客户端,那么可以使用 lua 脚本,先通过 get 确定对应 key 的值是否正确,如果正确再 del,整个 lua 脚 ...
博客装修(2023年3月)
最是一年春好处,绝胜烟柳满皇都。不知不觉又一年过去了,每年 3 月都是博客装修的季节,这次也不例外,这次的装修内容如下:
主题升级
支持隐藏文章
添加每日打卡
评论系统迁移
自定义 RSS 订阅模板
支持 algolia 搜索
hexo 和 butterfly 主题升级hexo 升级
之前使用的还是老版本的 hexo 由于这次想要升级主题,而主题需要 hexo 5.0 以上版本,所以就顺便升级了 hexo
1234567891011121314# 确认当前版本hexo version# 升级 hexonpm i hexo-cli -g# 重新安装依赖npm install -g npm-check# 检查依赖npm-check# 更新依赖npm install -g npm-upgradenpm-upgrade# 重新安装依赖npm update -gnpm update --save
需要注意的是 使用的 node 版本,以及其中一些命令需要以 sudo 权限执行版本对应
butterfly 主题升级
更新主题版本至 4.7.0 https://github.com/ ...
hexo butterfly 主题 添加 algolia 搜索
之前一直无法成功注册登录 algolia 一直用的 hexo 本地搜索,搜索结果不准确,algolia 有一定的免费额度,对于小网站来说还是挺友好的,接入也很方便。(无法注册成功登录的原因我注释在了最后)
安装插件地址在:https://github.com/LouisBarranqueiro/hexo-algoliasearch
1npm install hexo-algoliasearch --save
注册 Algolia 并获取配置https://www.algolia.com/
配置修改博客根目录的配置文件 _config.yml
123456789101112131415algolia: appId: "Z7A3XW4R2I" apiKey: "12db1ad54372045549ef465881c17e743" adminApiKey: "40321c7c207e7f73b63a19aa24c4761b" chunkSize: 5000 indexName: "my-hexo-blo ...
Golang 单元测试 - 其他小技
单元测试有很多技巧和科技,我都会慢慢汇总在这里
打桩测试当我们在编写单元测试的时候,有时我们非常想 mock 掉其中一个方法,但是这个方法又没有接口去定义和实现(无法用 github.com/golang/mock 来实现),这时就可以尝试看看我们的打桩黑科技。
代码
这里我们使用 github.com/agiledragon/gomonkey 来实现。
实际中,经常在代码中会遇到一些随机值的情况,比如验证码。为了方便测试,我们会想要 mock 掉随机值方法,让每次产生的值固定方便后续的测试。
1234567891011121314151617package mainimport ( "fmt" "testing" "github.com/agiledragon/gomonkey/v2" "go-demo/m/unit-test/other/rand")func init() { gomonkey.ApplyFunc(rand.Number, func() in ...
Golang 单元测试 - 接口层
上次我们已经搞定了逻辑层的单元测试,这次我们来康康接口层的单元测试。接口层主要负责的就是请求的处理,最常见的就是 HTTP 请求的处理。
但针对 接口层 的单元测试其实是可以五花八门的。它并不像逻辑层和数据层一样的通用,对于它的测试往往有很多路可以走。
由于使用的 HTTP 框架不同,单元测试的实现方式则不同。 既可以通过程序来模拟 HTTP 请求,也可以通过真实的 HTTP 请求来测试,通过借助外部的一些测试工具来实现。
所以本文只能给出一种思路,具体的实现方式还是要根据实际的框架来实现。
环境本文以常用的 gin 框架为例,使用一种个人比较喜欢也非常简单的方式来实现单元测试。特点主要有:
不需要启动路由服务
复用已有的项目内的请求结构
代码
由于之前已经贴过,所以 service 层的 代码这里就不赘述了
base case1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859package controlleri ...
Golang 单元测试 - 逻辑层
前面我们完成了最麻烦的数据层的单元测试,今天我们来看看单元测试中最容易做的一层,数据逻辑层,也就是我们通常说的 service 或者 biz 等,是描述具体业务逻辑的地方,这一层包含我们业务最重要的逻辑。
所以它的测试非常重要,通常它测试的通过就意味着你的业务逻辑能正常运行了。
而如何对它做单元测试呢? 因为,这一层的依赖主要来源于数据层,通常这一层会调用数据层的接口来获取或操作数据。 由于我们之前对于数据层已经做了单元测试,所以这一次,我们需要 mock 的不是数据库了,而是数据层。
Golang 提供了 github.com/golang/mock 来实现 mock 接口的操作,本文就是使用它来完成我们的单元测试。
准备工作安装 go install github.com/golang/mock/mockgen@v1.6.0
基本 case 代码首先我们还是基于上一次的例子,这里给出上一次例子中所用到的接口
12345678910111213141516171819202122232425262728293031323334353637383940 ...
Golang 单元测试 - 数据层
前言今天我们先来看看有关数据层(repo)的单元测试应该如何实践。
数据层,就是我们常常说的 repo/dao,其功能就是和数据库、缓存或者其他数据源打交道。它需要从数据源中获取数据,并返回给上一层。在这一层通常没有复杂业务的逻辑,所以最重要的就是测试各个数据字段的编写是否正确,以及 SQL 等查询条件是否正常能被筛选。
当然,数据层也基本上是最底层了,通常这一层的单元测试更加的重要,因为如果一个字段名称和数据库不一致上层所有依赖这个方法的地方全部都会报错。
由于数据层和数据源打交道,那么测试的麻烦点就在于,通常我们不能要求外接一定能提供一个数据源供我们测试:一方面是由于我们不可能随时都能连上测试服务器的数据库,另一方面我们也不能要求单元测试运行的时候只有你一个人在使用这个数据库,而且数据库数据干净。退一步讲,我们也没办法 mock,如果 mock 了 sql,那么测试的意义就不大了。
下面我们就以我们常见的 mysql 数据库为例,看看在 golang 中如何进行单元测试的编写。
准备工作的说明数据源首先,我们需要一个干净的数据源,由于我们没有办法依赖于外部服务器的数据库,那么我们就 ...
Golang 单元测试 - 前言
在测试上难以自动化的软件,很难成为好的软件。 – 《Google软件测试之道》
对于单元测试的想法对于单元测试,开发者总是有着非常矛盾的思想。
单元测试很好,它能帮助我们找到很多隐藏的bug
但写单元测试,比搬砖还累,我真的不想写~
没错,单元确实是一个磨炼意志的东西,如果不是在大公司,人力也不够,而由于单元测试往往是没有 KPI 的,所以经常在做完功能测试之后就快速上线不断迭代了。
但,如果想要成为一个出色的软件,或者说是作品,单元测试是保证可持续发展的地基。比如,很多的开源软件,为了保证可靠,单元测试覆盖率往往都有着很高的标准。
于是,从 JAVA 的 JUnitTest 的坑爬出来之后,我准备来写写,在 Golang 中我们如何做单元测试。不一定是最佳实践,但绝对算是不错的参考。本文先来说说单元测试的要求和注意点。
什么是单元测试笔者从书中总结了一句话:自动化验证一个独立模块的代码是否能满足预期要求的测试。
所以单元测试是一个最最底层的一个测试,思路就是保证最小的模块没有问题,只有底子稳了,上面的业务才不容易出现问题。
要求
总结自《代码整洁之道》
自动化自动化是一个最 ...
秒懂 caddy 插件机制实现原理
前言由于 Golang 是静态编译,所以 plugin 机制一直是一个难解的问题,官方提供的 plugin 机制又特别难用,但插件无疑是扩展原始功能的一种最方便的途径。于是乎,各路软件自家都有各种插件机制。caddy 使用 xcaddy 来实现插件机制,我们来看看它是如何做的。
结论首先上来先给结论,它必须重新编译,没办法,这也是一个必然选择。很多人听到这,只能叹气一下。
使用方式使用命令安装 xcaddygo install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
然后使用命令选择需要的插件进行重新编译:
12xcaddy build master \ --with github.com/caddyserver/ntlm-transport
其中 master 是指定 caddy 的版本
原理
其实原理非常简单,看源码一下就明白了
1. 生成 main.go在 xcaddy 运行 build 命令的时候首先会创建一个临时文件夹,然后按照模板写入一个 main.go 文件:
12345678910111213141516 ...
重构-改善既有代码的设计
最近正在重构项目,并且正在看《重构》,在实践的同时总结了一些点,或许能给你一些重构或者写代码上的一些思考。
我一直认为代码结构是一个因人而异的事情,很多时候我们其实判断一个代码的好坏往往是通过主观判断,比如同样是实现一个功能,100 行的代码并非一定比 50 行的差;我们没有一个合理的标杆去评判。
但是,最近我的想法变了,发现有些代码一定是毒药,早点发现他们,往往会对于我们以后需求的修改有莫大的帮助。
命名
如果把整个项目代码比作是房屋建造,命名就是砖头,命名的好坏直接决定了你代码 50% 的可读性。绝大部分的情况下,读者应该可以通过你函数的命名,直接了解到你这个函数的功能。
要求
命名要描述具体意图而非模糊的操作
功能命名不要出现技术名词
整个项目统一命名
命名要描述具体意图不好的比如:process、modify…通常我们需要使用一个动词+宾语,比如 modifyUsername,processFile而一个类(对象)的命名,通常使用名词
功能命名不要出现技术名词不好的比如:UserRoleMap显然 Map 是一个技术名词,而这个对象的类型其实往往已经可以清楚的标识这个 ...
hexo 博客 SEO 优化
hexo 博客 SEO 优化
由于我写博客的时间比较长,时间长了之后自然搜索引擎就收录,但如果一开始写博客,想要被搜索引擎主动收录还是需要做 SEO 的
这里记录一下 使用 hexo 如何做 SEO ,有一些主题自带了 SEO 优化建议首先查看自己当前使用的主题是否有这个配置项。
生成 sitemap12npm install hexo-generator-sitemap --save npm install hexo-generator-baidu-sitemap --save
然后发布后访问:https://linkinstars.com/sitemap.xmlhttps://linkinstars.com/baidusitemap.xml如果可以正常访问到 xml 文件就可以了
提交 sitemap百度普通收录登录 https://ziyuan.baidu.com/dailysubmit/index 然后选择自己合适的验证方式验证网站,这一步主要是为了验证当前你想要收录的网站属于你自己。
我这边都采用了 DNS 解析的验证方式,添加一个 CNAME 的解析就好了,非常方 ...
细说 kubernetes - 初识 statefulset
最近遇到一个问题:statefulset 部署能否只是升级部分的 pod?问题本身不难,但我发现没有说过有关 statefulset 的介绍,于是本文将介绍一些有关 statefulset 的点。问题的答案则放在文章的最后,这也是 statefulset 一个很有用的特性。
statefulset 特点首先让我们来简单说下 statefulset 的一些基本特点
特点statefulset 顾名思义是有状态的,它与 deployment 最大的不同就是这个。
稳定的、唯一的网络标识符。
稳定的、持久的存储。
有序的、优雅的部署和扩缩。
有序的、自动的滚动更新。由于特点是为了满足很多有状态的服务或者中间件:如 Zookeeper、Kafka 等,他们一般有一些数据是不希望随着服务的重启而消失的,所以就需要 statefulset 了。
顺序&唯一在 statefulset 中每个 pod 会有一个唯一标识符,故这些 pod 之间是不一样的,也就是唯一的,所以 pod 在重启或者是调度后是没有办法互相替换的。
12345678❯ kubectl get podsNAME ...
使用 xorm 实现多数据库支持坑点总结
如果你想让你的 go 项目支持不同类型的数据库如:MySQL,PostgreSQL,sqlite3… 那么除了使用 orm 框架帮你屏蔽很多 sql 细节外,还有什么坑点呢?最近我正在使用 xorm 做多数据库类型的适配总结了以下问题供参考。
PS: 本人除了对 MySQL 熟悉外,其他数据库仅停留在使用和了解阶段,当前测试仅覆盖 MySQL,PostgreSQL,sqlite3 三种类型
初始化以往我们进行数据库初始化一般就是导入需要初始化执行的 sql 文件,但由于需要支持多库,则无法使用功能,因为各个数据库结构不同,我们需要使用 xorm 的功能进行初始化操作。
12345678910111213engine, err := xorm.NewEngine(driver, connection) if err != nil { return err} if err = engine.Ping(); err != nil { return err } err = engine.Sync(tables...) if er ...
errgroup 的基本使用
我们在使用 go 编写代码的时候,在错误处理的时候,经常会写出很多 if err != nil ,其实有些时候我们可以使用一些技巧去避免,本文就来讨论两种常见的避免技巧,内部包装错误和 errgroup。
基本 case 实现123456789101112131415161718192021222324252627282930313233package main import "fmt" func StartUserService() error { fmt.Println("start user service") return nil } func StartGoodsService() error { fmt.Println("start goods service") return nil } func StartOrderService() error { fmt.Println("star ...
快速建立 smtp 服务
一般情况下,我们会使用各种云厂商或者第三方提供的 smtp 邮件服务,来发送邮件,如发送注册验证码,找回密码邮件等等。但有时我们想自建 smtp 服务来自己发送邮件,避免一些敏感信息被三方服务屏蔽。所以本文记录如何快速构建一个属于自己的 smtp 服务来发送邮件。
PS:当然,自建 smtp 意味着会有很多限制条件,比如你需要有对应端口、域名,并且发送的邮件可能会被识别为垃圾邮件,从而导致用户收不到或被扔进垃圾箱。
本文使用 docker 镜像快递搭建可以发送邮件的 smtp 服务
镜像我使用的镜像是:https://hub.docker.com/r/namshi/smtp
1docker pull namshi/smtp
123456789#/bin/bashname="smtp"docker rm -f $namedocker run --restart=always -d \ -e "RELAY_NETWORKS=:127.0.0.1/0" \ --name $name \ -p 10025:25 \ nam ...
《笨开发学习操作系统》7网络
单服务器理论最大网络连接数是多少?
你是否曾经有想过这个问题,我们的一台 web 服务器最多能连接多少个客户端,或者说是服务多少个用户?是不是说,无论用户数量有多少,只要 CPU 和内存足够,就能支持?
今天我们就来说说,操作系统的网络部分。(本文会一直围绕着这个问题来进行)
PS:由于网络部分的知识体系过于庞大,计算机网络出的书数不胜数,单单我想用一篇博客写完是不可能的,所以我选择了其中很多人最关心,也在实际中能运用到的一个知识点来拓展 —— I/O 多路复用
本文可能需要你有一些预备知识:
TCP 和 UDP 协议的基本概念
OSI 7 层网络模型的概念
引子我们知道 OSI 模型有着 7 层,每层都有着自己的职责,而网络的本质就是不同协议针对数据的不断包装,因为外部的网络环境复杂,只有将快递(数据包)包装完整,写清楚地址,才能尽可能的不丢失在茫茫互联网海洋中。
其中,传输层有着我们的 TCP 和 UDP 两个非常重要的协议,他们保证了我们的数据传输。
那么问题来了,我们知道 TCP 和 UDP 的协议本身还是比较复杂的,作为应用来说,我们不可能每个应用再去实 ...
kubernetes之ingress的基本使用
由于最近服务迁移,进行了各种调整,调整的过程中也顺便修改了 ingress 的相关配置,发现这块之前没有写过,于是今天就来看看 ingress 的基本使用。
其实 ingress 本身不复杂,只要理解了概念,很快就能上手。
PS:本文需要前置知识点 kubernetes 的 service
什么是 ingress
如果用一句话总结什么是 ingress,那我觉得:ingress 就是 service 的 service
其实 ingress 就是在 service 之前再加入了一层网关/路由,根据配置的不同规则,将请求路由到不同的 service 中,然后对外集群只暴露一个访问的地方
为什么需要 ingress在 k8s 集群中,我们知道,想要暴露一个 deployment 那么就需要创建一个 service,这个 service 会帮助我们路由到多个 pod 上。但 service 想要被外部访问,有下面几种方式:
ClusterIP 只能内部访问
NodePort 需要占用端口,当服务数量多,管理麻烦
LoadBalance 要么自己用轮子(metallb)也会占用端 ...
《笨开发学习操作系统》6输入输出系统
当你敲键盘的时候,为什么显示器会显示字符?
当我们第一次使用电脑的时候,你会觉得神奇吗?为什么我们在键盘上敲击或者使用鼠标进行点击,就能实现各种操作;为什么显示器会显示出这些图标?这些用户操作的背后,是谁在同一管理他们呢?
今天我们就来说说,这些看得见摸得到的人际交互设备与我们操作系统的输入输出系统的关系。
设备分类首先,我们来对我们常见的输入输出设备大致进行分类:
块设备:每个块有自己的地址,信息存储在对应的块中,如硬盘
字符设备:没有结构也无法寻址,都是直接传递的字节流,如鼠标
网络设备:也就是我们常说的 socket,数据单位是网络包。
然后我们将开始一步步的探索:首先,我们计算机中的大脑(CPU)要控制这些设备,比如想要控制显示器显示数字,还需要接收来自一些设备的控制,如键盘的输入等。那么形形色色的设备都需要进行管理,我们的 “大脑” 就需要 “手” 来帮忙,这就是设备控制器。
设备控制器键盘、鼠标、显示器…. 设备之间功能不同,为了统一管理,每个设备都有一个设备控制器。设备控制器可以帮助我们屏蔽不同设备之间的差异。
设备控制器有寄存器,这些寄存器用来与 CPU 进行通 ...
《笨开发学习操作系统》5文件系统
在 Linux 中一切皆文件
这是我们经常能听到很多大佬说的一句话,那为什么说 Linux 中都是文件呢?这句话究竟代表着什么具体的含义呢?在操作系统中,文件系统又扮演着一个什么样的角色?作为一个普通的开发者,我们究竟对文件系统要有怎么样的认识?今天我们就来看看这个大哥 —— 文件系统
文件在认识文件系统之前,首先来看看我们的文件是什么。当我们在 Linux 系统上创建的一个文件,究竟有哪些东西?是只有文件内容本身吗?
文件的两个数据结构首先,来认识今天的第一个重点:
index node: 索引节点
directory entry: 目录项
索引节点当我们创建一个文件的时候,其实不仅仅只是创建了文件本身的内容,还有很多相关的属性也被创建了,如:这个文件的访问权限,修改时间等等。我们的索引节点(index node) 就是我们常说的 inode,这个结构中就存放了文件的必要属性,也被称为文件的元数据。
1234567891011121314151617/* * Structure of an inode on the disk */struct ext4_inode { ...
《笨开发学习操作系统》4进程间通信
在操作系统中,两个进程之间是如何进行通信的?
随着我们的应用系统越来越大,单进程往往无法满足我们的要求,将一个大的系统拆分成多个功能模块,解耦,往往是一种常用的设计。无论是从将功能模块化、数据隔离等方面考虑,多进程协作都有着优势。
那么就意味着进程之间需要进行数据的传递,于是进程间通信(Inter-Process Communication)也就是我们常说的 IPC 就非常重要了。今天我们就来看看有哪些方式能实现 IPC。
大纲,我们主要围绕着 IPC 的方式展开,今天比较简单:
共享内存
管道
信号
信号量
消息队列
套接字
管道
1ps -ef |grep target
通过 shell 的管道符号 “ | “,将第一个命令的输出通过管道作为第二个命令的输入。
特点:
单方向:由一方发送,另一方接收
使用功能内存作为缓冲区,没有持久化
创建:
匿名管道:通过 pipe 系统调用创建
命名管道:通过 mkfifo 创建
其实本质是创建了两个文件描述符,然后通过内存作为缓冲区,来实现通信。
消息队列
UNIX 系统提供了 System V 消息队列来作为一种进程间通信 ...
go 中 struct 是否可以比较?
今天来水一篇,最近比较忙,一直没有时间写 go 相关的,今天从一个小问题入手,来说说 struct 的比较问题。
由于已经有很多其他的文章说过这个问题,我这里赘述就显得多余,所以我直接给出结论,并直接说明在实际中用的上的。
为什么要比较?原本这应该是某人想出的面试题,但是如果光光是解决这个问题的话,太应试了。大白话就是,谁没事去比较两个 struct 呢?为什么要比较呢?
那比较的原因,肯定是我们需要知道两个结构体是否相等。
比较的依据两个结构体是否相等,比价的依据有两个:
两个结构体的地址是否相等?(比较地址)
两个结构体中的所有字段是否都相等?(比较内容)
重点1:如果两个 struct 类型不同,一定是无法比较的,会直接编译报错,也没有人这么干吧。。
比较地址其实大多数情况下,我们不需要知道这个问题的答案,至少我无法想象到为什么要比较两个对象的地址是不是一样。
123456789101112131415package main import "fmt" type User struct { Name string ...
《笨开发学习操作系统》3内存
OOM 往往是我们经常遇到的 “严重” 问题之一,那内存究竟是如何被合理分配和使用的呢?
本期大纲
首先我们要知道一个重要概念就是 虚拟地址
虚拟地址和物理地址的映射关系
内存中放了什么东西
内存是如何分配的
内存不够了怎么办
前言你的电脑上或许此时插着一根 8G 的内存条,你经常在使用它,但你有没有想过操作系统是如何管理内存的?如果让你来分配使用,你是否会想着:给正在运行的游戏分配其中的 4G,给我的视频软件分配 2G,给音乐软件分配 1G,分配各自独立,互不干扰。但当我的游戏需要更多的内存的时候,是否我的视频就无法播放了呢?
那么对于操作系统来说,如何合理的分配和管理好内存就是我们今天要解决的问题。
虚拟地址首先要引出一个概念:虚拟地址。我们将实际在内存条上存储的地址称为:物理地址。所谓虚拟地址,就是我们人为创建的一个地址,对于进程来说,只能看见虚拟地址。
为什么需要虚拟地址就像前言中所描述的,如果我们直接都使用物理地址会出现什么问题呢?
随着程序的运行,使用的内存不断变化,我们无法预见到需要使用多少内存,无法事先分配;即使事先分配,也会造成使用浪费。
如果不事先分配, ...
《笨开发学习操作系统》2进程
进程和线程有什么区别?一个常常被问到的面试题
我们在实际的开发过程中,经常打交道的就是线程,而进程呢,通常就是我们整个运行的程序。对于他们两个来说其实并不陌生,你要让我说出个一二三也可以讲,但可能也都是从使用的角度,而今天我们就从 操作系统 的角度来重新认识一下他们两个(从内核的角度看进程和线程长什么样)。
大纲:
首先我会让你直观感受我们的进程和基本的分类
优先理解他们的数据结构
状态的变化是非常重要的一环
接着是重点:如何创建他们
最后再来看调度
你所需要把握的重点是:结构、创建和调度。这些对于以后的开发或是问题的解决都是有着密切联系的。
进程的直观感受首先让我们从实际角度来直观感受什么是进程,通过 ps -ef 命令可以查看当前进程的相关情况
1234567891011121314151617181920212223242526UID PID PPID C STIME TTY TIME CMDroot 1 0 0 2021 ? 00:38:48 /usr/lib/systemd/systemd ...