<?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>编程 · 技术 on 华说记录我的生活</title>
    <link>http://www.huasay.com/categories/%E7%BC%96%E7%A8%8B-%E6%8A%80%E6%9C%AF/</link>
    <description>Recent content in 编程 · 技术 on 华说记录我的生活</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh</language>
    <lastBuildDate>Thu, 14 Sep 2023 16:58:10 +0800</lastBuildDate>
    <atom:link href="http://www.huasay.com/categories/%E7%BC%96%E7%A8%8B-%E6%8A%80%E6%9C%AF/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>权限控制 - Casbin</title>
      <link>http://www.huasay.com/2023/09/14/casbin/</link>
      <pubDate>Thu, 14 Sep 2023 16:58:10 +0800</pubDate>
      <guid>http://www.huasay.com/2023/09/14/casbin/</guid>
      <description>最近在研究 iam 相关的内容，iam 即身份访问管理(identify access management) 身份: 用户在整个系统体系中扮演什么样的角色 访问管理：对身份的权限控制。 那既然谈到权限控制，不得不谈谈 casbin, casbin 目前是 github 上在权限控制处理方面 star 数是最多的，虽然之前没接触过 casbin,但也调研了一段时间，从易用性，扩展性，实现上来谈谈 casbin。
核心概念 权限模型目前主流的主要包括acl, rbac, abac。 简单先从这几个模型说起。而了解这几个模型之前，我们先约定一下一些术语。
subject 主体，在一般的权限体系中，主体一般是指操作者，比如某个用户，某个角色(其实角色最下面也是个用户)，当然更广义的讲，可能主体也可以代表其他需要约束的对象，比如在物联网中，他也可以代表一个iot设备。 resouce 资源，一切可操作的实体都可以当做是一种资源，比如aliyun上的我们购买的ecs实例，后台系统的一个user action 操作，主体对资源的操作，比如我们可以对ecs实例进行开机，关机，重启等操作，可以新增，删除，编辑一个user。 condition 操作的条件，一般基于主体与资源的约事条件，比如 user 的ip必须在172.168.0.1/24的段内 effect 影响效力，一般只有allow(允许)/deny(不允许) 有了这些术语，我们去解释对应的权限模型，可能更知道不同模型的区别 ACL acl(访问控制列表)，他代表的是，哪些用户（subject）或系统进程可以访问(action)资源(resource) 如果某个文件 apple.jpg（resource） 的 ACL 包含（Alice(subject): read(action),write(action)；Bob(subject): read(action)），这将赋予 Alice 读写文件的权限，而只赋予 Bob 读取文件的权限。
RBAC ABAC casbin 中的概念 理解casbin，最主要理解其核心思路，casbin中的核心思路是使用policy(策略)中的规则去应用到model(模型)中，model 是一个规则引擎，帮我们处理一个主体(subject)是否有操作(action)某个资源(subject)的权限。
policy策略：一组规则的集合, 规则 model模型：对每行规则进行解析
因此，对于我们使用者来说，大多时候仅仅只需要关注policy和model就行。
model 初览 在model层面，casbin又基于 PERM (Policy, Effect, Request, Matcher). 一个完整的model(perm)文件格式如下：</description>
    </item>
    <item>
      <title>swagger的使用</title>
      <link>http://www.huasay.com/2023/08/27/swagger_usage/</link>
      <pubDate>Sun, 27 Aug 2023 21:20:10 +0800</pubDate>
      <guid>http://www.huasay.com/2023/08/27/swagger_usage/</guid>
      <description>随着RESTful API的流行，Swagger已经成为了一种通用的API描述和文档格式。它可以帮助开发者描述、测试和文档化RESTful API。在本文中，我们将介绍在go中如何使用相应的库生成swagger文档。在golang中, 有两款可以生成swagger的库，一般我们选择https://github.com/swaggo/swag,还有另一款https://github.com/go-swagger/go-swagger,相对来说，使用起来swag更易用一点
安装 1 go install github.com/swaggo/swag/cmd/swag@latest 默认情况下，会安装在go path相关的目录下，该目录一般已经加入环境变量，若没有加入环境变量，我们可根据每种系统的不同，手动加入至环境变量中
检测是否安装成功：
1 swag -v 返回具体的版本号即成功:
1 swag.exe version v1.8.12 使用 路由绑定 我们使用的是gin框架，swag对gin也有集成封装, 我们可以在gin的路由绑定至对应的swagger文档
1 2 3 // swagger文档查看路径 url := ginSwagger.URL(&amp;#34;/swagger/doc.json&amp;#34;) // The url pointing to API definition r.GET(&amp;#34;swagger/*any&amp;#34;, ginSwagger.WrapHandler(swaggerFiles.Handler, url)) 依赖库：
1 2 swaggerFiles &amp;#34;github.com/swaggo/files&amp;#34; &amp;#34;github.com/swaggo/gin-swagger&amp;#34; 路由注释 1 2 3 4 5 6 7 8 9 10 // ListUser // @Summary 查询用户信息 // @Tags 用户 // @Description	查询用户信息 // @Produce	json // @Param UserId header string true &amp;#34;当前用户id&amp;#34; // @Param query query dto.</description>
    </item>
    <item>
      <title>go分享</title>
      <link>http://www.huasay.com/2023/05/24/share-go-routine/</link>
      <pubDate>Wed, 24 May 2023 22:49:21 +0800</pubDate>
      <guid>http://www.huasay.com/2023/05/24/share-go-routine/</guid>
      <description>go的goroutine go中最重要的概念之一就是协程，协程的创建方式也很简单，go func()就简单的创建了一个协程，虽然go语言中并没有规定协程的 创建数据限制，每个协程相对于线程的成本也很低，协程最小占用只有2Kb， 1000个协程才2M，所以成本极低。
但创建容易的同时也应该注意以下几点：
对于panic捕获的安全性 我们一般使用go func()直接创建协程了，但一般在代码中，很少直接这么使用，一般会在外面再封装一层。具体的封装方式类似这样：
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package thread import ( &amp;#34;fmt&amp;#34; &amp;#34;runtime/debug&amp;#34; ) func GoSafe(fn func()) { go RunSafe(fn) } func RunSafe(fn func()) { // 可以將panic上报给sentry等报警平台，或者钉钉，飞书机器人的钩子等 defer func() { if p := recover(); p != nil { fmt.Printf(&amp;#34;RunSafe capture crash,msg: %s \n %s &amp;#34;, fmt.Sprint(p), string(debug.Stack())) } }() fn() } 原因很简单，go语言外层的defer是捕获不了内部goroutine的异常的，所以如果内部的goroutine发生panic，那会导致整个程序直接crash。 因此，使用goroutine的话最好再加一层安全封装，以防止潜在的panic导致的crash。</description>
    </item>
    <item>
      <title>Yaml 语法</title>
      <link>http://www.huasay.com/2023/05/18/yaml-synax/</link>
      <pubDate>Thu, 18 May 2023 23:30:48 +0800</pubDate>
      <guid>http://www.huasay.com/2023/05/18/yaml-synax/</guid>
      <description> yaml是工作中遇到比较多的标记语言，特别在k8s横行其道的年代， yaml简直就是标配， 到处都是yaml。yaml的语法还是简单的，在此只 是做点小记录
yaml的基本使用 简单的对象关系 1 name: &amp;#34;hua&amp;#34; ## 在同一行，:后一定要加空格 行内对象嵌套关系 1 info: {name: &amp;#34;hua&amp;#34;, age: 32} 数组关系 1 2 3 - Bird - Cat - Dog 行内数组 1 animals: [Bird, Cat, Dog] 子数组 1 2 3 4 - - Bird - Cat - Dog </description>
    </item>
    <item>
      <title>网关traefik入门级使用</title>
      <link>http://www.huasay.com/2023/05/12/gateway-traefik-usage/</link>
      <pubDate>Fri, 12 May 2023 20:42:55 +0800</pubDate>
      <guid>http://www.huasay.com/2023/05/12/gateway-traefik-usage/</guid>
      <description>基本概念 EntryPoints EntryPoints 是进入traefik的入口点，定义端口接收数据包，可以是tcp也可以udp的包。 Routers Middlewares 附加在路由之上，中间件可以在发送至后端服务器之前处理请求或响应。 Services 简单部署 可以写一个docker-compose.yaml文件编排traefik与后端服务whoami
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 version: &amp;#39;3&amp;#39; services: reverse-proxy: # The official v2 Traefik docker image image: traefik:v2.10 # Enables the web UI and tells Traefik to listen to docker command: --api.insecure=true --providers.docker --accesslog=true ports: # The HTTP port - &amp;#34;80:80&amp;#34; # The Web UI (enabled by --api.</description>
    </item>
    <item>
      <title>网关traefik调研</title>
      <link>http://www.huasay.com/2023/05/11/gateway-traefik/</link>
      <pubDate>Thu, 11 May 2023 21:30:20 +0800</pubDate>
      <guid>http://www.huasay.com/2023/05/11/gateway-traefik/</guid>
      <description></description>
    </item>
    <item>
      <title>网关gloo调研</title>
      <link>http://www.huasay.com/2023/05/08/gateway-gloo/</link>
      <pubDate>Mon, 08 May 2023 22:12:37 +0800</pubDate>
      <guid>http://www.huasay.com/2023/05/08/gateway-gloo/</guid>
      <description></description>
    </item>
    <item>
      <title>golang 面试题集</title>
      <link>http://www.huasay.com/2023/02/22/interview-go-overview/</link>
      <pubDate>Wed, 22 Feb 2023 23:32:10 +0800</pubDate>
      <guid>http://www.huasay.com/2023/02/22/interview-go-overview/</guid>
      <description> Go 中的 rune 和 byte 有什么区别？ Go 中的 string 和 []byte 有什么区别？ 什么是深拷贝和浅拷贝？ Go 中的 slice 和 array 有什么区别？slice的扩容机制与扩容时机，使用slice该注意什么 说说 Go 中闭包的底层原理？ 说一下 GMP 模型的原理 Go 的默认栈大小是多少？最大值多少？ Go 中的分段栈和连续栈的区别？ 简述一下 Go 栈空间的扩容/缩容过程？ GMP 为什么要有 P ？ Go 中的 GC 演变是怎样的？ 哪些情况会导致协程泄露？ 内存分配原理 gin框架的路由实现原理 go中http库的实现原理 代码题：使用n个并发，输出[]string{&amp;ldquo;a&amp;rdquo;, &amp;ldquo;b&amp;rdquo;, &amp;ldquo;c&amp;rdquo;, &amp;ldquo;d&amp;rdquo;, &amp;ldquo;e&amp;rdquo;, &amp;ldquo;f&amp;rdquo;, &amp;ldquo;g&amp;rdquo;} 代码题：实现一个简单的协程池 代码题：使用并发编排，使abc三个各输出100次 map的底层实现原理，如果一个协程在delete, 一个协程在读，会出现什么问题，原因是什么 uinptr和unsafe.Pointer的区别 singleFlight存在的坑 </description>
    </item>
    <item>
      <title>算法笔记</title>
      <link>http://www.huasay.com/2023/02/22/algorithm-overview/</link>
      <pubDate>Wed, 22 Feb 2023 23:32:10 +0800</pubDate>
      <guid>http://www.huasay.com/2023/02/22/algorithm-overview/</guid>
      <description> 滑动窗口 双指针 贪心 动态规划 排序 二叉树 </description>
    </item>
    <item>
      <title>算法笔记 - 二叉树</title>
      <link>http://www.huasay.com/2023/02/22/algorithm-tree/</link>
      <pubDate>Wed, 22 Feb 2023 23:32:10 +0800</pubDate>
      <guid>http://www.huasay.com/2023/02/22/algorithm-tree/</guid>
      <description>递归模板 1 2 3 4 5 6 7 8 9 10 func traverse(root *TreeNode) { if root == nil { return } // 前序位置 traverse(root.Left) // 中序位置 traverse(root.Right) // 后序位置 } 层序遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // 输入一棵二叉树的根节点，层序遍历这棵二叉树 func levelTraverse(root *TreeNode) { if root == nil { return } q := make([]*TreeNode, 0) q = append(q, root) // 从上到下遍历二叉树的每一层 for len(q) &amp;gt; 0 { sz := len(q) // 从左到右遍历每一层的每个节点 for i := 0; i &amp;lt; sz; i++ { cur := q[0] q = q[1:] // 将下一层节点放入队列 if cur.</description>
    </item>
    <item>
      <title>[转]golang面试 - Rune与Byte的区别</title>
      <link>http://www.huasay.com/2022/11/04/diff_rune_byte/</link>
      <pubDate>Fri, 04 Nov 2022 23:32:25 +0800</pubDate>
      <guid>http://www.huasay.com/2022/11/04/diff_rune_byte/</guid>
      <description>以下文章转自 https://iswbm.com/517.html
一个字符串是由若干个字符组合而成的，比如 hello，就由 5 个字符组成。
在 Go 中字符类型有两种，分别是：
byte 类型：字节，是 uint8 的别名类型 rune 类型：字符，是 int32 的别名类型 byte 和 rune ，虽然都能表示一个字符，但 byte 只能表示 ASCII 码表中的一个字符（ASCII 码表总共有 256 个字符），数量远远不如 rune 多。
rune 表示的是 Unicode字符中的任一字符，而我们都知道，Unicode 是一个可以表示世界范围内的绝大部分字符的编码， 这张表里几乎包含了全世界的所有的字符，当然中文也不在话下。
能表示的字符更多，意味着它占用的空间，也要更大，所占空间是 4个 byte 的大小。
下面以一段代码来验证一下他们的占用空间的差异
1 2 3 4 5 6 7 8 var a byte = &amp;#39;A&amp;#39; var b rune = &amp;#39;B&amp;#39; fmt.Printf(&amp;#34;a 占用 %d 个字节数\n&amp;#34;, unsafe.Sizeof(a)) fmt.Printf(&amp;#34;b 占用 %d 个字节数\n&amp;#34;,unsafe.Sizeof(b)) // output a 占用 1 个字节数 b 占用 4 个字节数 参考文档： https://iswbm.</description>
    </item>
    <item>
      <title>golang面试 - channel全解析</title>
      <link>http://www.huasay.com/2022/11/04/interview-channel/</link>
      <pubDate>Fri, 04 Nov 2022 23:32:25 +0800</pubDate>
      <guid>http://www.huasay.com/2022/11/04/interview-channel/</guid>
      <description>channel 什么情况下会出现panic 1 2 3 1. 试图往已close的channel继续发送 2. 试图关闭已关闭的channel 3. 试图关闭一个nil的channel 向为nil的channel发送数据会怎样 1 2 3 给定一个 nil channel c: &amp;lt;-c 从 c 接收将永远阻塞 c &amp;lt;- v 发送值到 c 会永远阻塞 channel close后读的问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 关闭后继续读数据，已在缓冲区内的可以读取到数据，而后得到的是零值(对于int，就是0)。 z 我们可以通过for循环遍历channel，来获取到已经写入的值 for _, v := range channel{ } 对于可能获取到零值，我们可以使用断言判断，ok为false代表channel已关闭后读取的 value, ok := &amp;lt;- channel 也可以在close channel之后有意将channel 置为 nil， 如此再使用，并不会读取到零值 对于不带缓冲的ch，和带缓冲的一样，channel close掉之后并不影响读，只影响写入 channel的底层结构 底层结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 type hchan struct { qcount uint // total data in the queue 代表 chan 中已经接收但还没被取走的元素的个数。内建函数 len 可以返回这个字段的值。 dataqsiz uint // size of the circular queue 循环队列的大小 buf unsafe.</description>
    </item>
    <item>
      <title>golang面试 - string与[]byte的区别，如何互相转化</title>
      <link>http://www.huasay.com/2022/11/04/diff_string_byte/</link>
      <pubDate>Fri, 04 Nov 2022 23:32:25 +0800</pubDate>
      <guid>http://www.huasay.com/2022/11/04/diff_string_byte/</guid>
      <description>string是一个8位字节的集合，通常但不一定代表UTF-8编码的文本。string可以为空，但是不能为nil。string的值是不能改变的。string类型本质也是一个结构体，底层本质就是一个byte类型的数组。 byte就是uint8的别名，它是用来区分字节值和8位无符号整数值
string与[]byte的区别 对于[]byte与string而言，两者之间最大的区别就是string的值不能改变。string在底层都是结构体stringStruct{str: str_point, len: str_len}，string结构体的str指针指向的是一个字符常量的地址， 这个地址里面的内容是不可以被改变的，因为它是只读的，但是这个指针可以指向不同的地址。 string的好处：以在不加锁的控制下，多次使用同一字符串，在保证高效共享的情况下而不用担心安全问题
对于[]byte来说，以下操作是可行的：
1 2 b := []byte(&amp;#34;hello world&amp;#34;) b[1] = &amp;#39;i&amp;#39; string修改操作是被禁止的：
1 2 s := &amp;#34;hello world&amp;#34; s[1] = &amp;#39;i&amp;#39; string支持这样的操作：
1 2 s := &amp;#34;hello world&amp;#34; s = &amp;#34;iello world&amp;#34; string与[]byte的互相转化 string的底层数据结构类型：
1 2 3 4 type stringStruct struct { str unsafe.Pointer len int } []byte对应的底层数据结构（本质上是个slice）：
1 2 3 4 5 type slice struct { array unsafe.Pointer len int cap int } 两者对应的结构非常相似。</description>
    </item>
    <item>
      <title>golang面试 - 米哈游第三方编制一面复盘</title>
      <link>http://www.huasay.com/2022/11/04/interview-mhywb/</link>
      <pubDate>Fri, 04 Nov 2022 23:32:25 +0800</pubDate>
      <guid>http://www.huasay.com/2022/11/04/interview-mhywb/</guid>
      <description>代码题 以n个协程，去输出[]string{&amp;ldquo;a&amp;rdquo;,&amp;ldquo;b&amp;rdquo;,&amp;ldquo;c&amp;rdquo;,&amp;ldquo;d&amp;rdquo;,&amp;ldquo;e&amp;rdquo;, &amp;ldquo;f&amp;rdquo;, &amp;ldquo;g&amp;rdquo;}。 代码题真得很简单，自己手生的很，竟然没写出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func main() { var wg sync.WaitGroup sem := make(chan struct{}, 5) // 创建一个大小为5的信号量 letters := []string{&amp;#34;a&amp;#34;, &amp;#34;b&amp;#34;, &amp;#34;c&amp;#34;, &amp;#34;d&amp;#34;, &amp;#34;e&amp;#34;, &amp;#34;f&amp;#34;, &amp;#34;g&amp;#34;} for _, letter := range letters { sem &amp;lt;- struct{}{} // 尝试写入信号量，如果已满，则阻塞 wg.Add(1) go func(l string) { defer wg.Done() fmt.Println(l) time.Sleep(5 * time.Second) &amp;lt;-sem // 认领信号量 }(letter) } wg.</description>
    </item>
    <item>
      <title>golang面试 - 深拷贝与浅拷贝</title>
      <link>http://www.huasay.com/2022/11/04/diff_deep_copy/</link>
      <pubDate>Fri, 04 Nov 2022 23:32:25 +0800</pubDate>
      <guid>http://www.huasay.com/2022/11/04/diff_deep_copy/</guid>
      <description>深拷贝是指两个变量，如果其内部引用其他的地址，那么深拷贝会将其内部的指针所指向的值重新分配地址用于存储这个值，因此他们内部的一些引用指针会变化。
浅拷贝是指两个变量，实际上其内部引用部分的地址并没有变化。
常见的浅拷贝，像slice的赋值，map的赋值
1 2 s := []int{1，2，3} s1 = s 如上所示，s1实际上就是对s的浅拷贝
对于深拷贝，我们在代码里应该这么使用
1 2 3 s := []int{1，2，3} var s1 []int copy(s, &amp;amp;s1) </description>
    </item>
    <item>
      <title>golang面试 - gc</title>
      <link>http://www.huasay.com/2022/11/04/interview-gc/</link>
      <pubDate>Fri, 04 Nov 2022 23:31:55 +0800</pubDate>
      <guid>http://www.huasay.com/2022/11/04/interview-gc/</guid>
      <description>相关文章 https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/ https://www.cnblogs.com/luozhiyun/p/14564903.html https://www.kancloud.cn/aceld/golang/1958308#Go_V13mark_and_sweep_21 https://www.bilibili.com/video/BV1wz4y1y7Kd?p=14&amp;amp;spm_id_from=pageDriver https://cloud.tencent.com/developer/article/1756163 https://blog.haohtml.com/archives/26358
https://mp.weixin.qq.com/s/5xjH-LJ53XiNm2sMNQZiGQ
GCMark	标记准备阶段，为并发标记做准备工作，启动写屏障	STW GCMark	扫描标记阶段，与赋值器并发执行，写屏障开启	并发 GCMarkTermination	标记终止阶段，保证一个周期内标记任务完成，停止写屏障	STW GCoff	内存清扫阶段，将需要回收的内存归还到堆中，写屏障关闭	并发 GCoff	内存归还阶段，将过多的内存归还给操作系统，写屏障关闭	并发
Go 语言中对 GC 的触发时机存在两种形式：
主动触发，通过调用 runtime.GC 来触发 GC，此调用阻塞式地等待当前 GC 运行完毕。
被动触发，分为两种方式：
使用系统监控，当超过两分钟没有产生任何 GC 时，强制触发 GC。
使用步调（Pacing）算法，其核心思想是控制内存增长的比例。</description>
    </item>
    <item>
      <title>golang面试 - gmp</title>
      <link>http://www.huasay.com/2022/11/04/interview-gmp/</link>
      <pubDate>Fri, 04 Nov 2022 23:31:55 +0800</pubDate>
      <guid>http://www.huasay.com/2022/11/04/interview-gmp/</guid>
      <description>相关文章 https://mp.weixin.qq.com/s/jIWe3nMP6yiuXeBQgmePDg https://tonybai.com/2017/06/23/an-intro-about-goroutine-scheduler/ https://tonybai.com/2020/03/21/illustrated-tales-of-go-runtime-scheduler/ https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/ https://www.kancloud.cn/aceld/golang/1958305#GoroutineGMP_164 https://segmentfault.com/a/1190000023869478 https://www.luozhiyun.com/archives/448 https://zboya.github.io/post/go_scheduler/ https://www.bilibili.com/video/BV19r4y1w7Nx?p=2&amp;amp;spm_id_from=pageDriver https://mp.weixin.qq.com/s/XiqVIR3U5ZmRD7xwJZKipA
进程，线程，协程的区别 进程： 进程是程序的一次执行过程，是程序在执行过程中的分配和管理资源的基本单位，每个进程都有自己的地址空间,进程是系统进行资源分配和调度的一个独立单位。 每个进程都有自己的独立内存空间，由于进程比较重量，占据独立的内存，所以上下文进程间的切换开销（栈、寄存器、虚拟内存、文件句柄等）比较大，但相对比较稳定安全。
线程：在同一个进程中的不同线程共享相同的地址空间(虚拟内存，代表切换不需要切换页表)，每个线程有独立的栈区，程序计数器（执行指令的信息），栈指针以及函数运行的寄存器。 初始栈大小8M，ulimit - s可以查看。调度由系统调度。从用户态切换到内核态，上下文切换消耗相对大
协程：用户态线程，不由内核管理，由go的底层调度器控制，协程初始栈大小2K。由用户调度
goroutine初始栈大小，上下文如何切换，存在哪里 协程初始栈大小2K，上下文由g0去调度找到对应可以运行的goroutine，存在gorountine的gobuf中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 组织下面试的语言 gmp是go为了提高并发能力实现的协程调度模型 g代表我们常说的协程，代码里的体现就是go关键字 m代表操作系统的线程，系统最大值默认为10000 p代表逻辑处理器，默认与cpu的核数想同，当然也可以通过runtime.GOMAXPROCS()来设置p的数量，m一般需要与p关联才能执行g。 调度策略： p会切换到g0栈上，执行调度函数，查找到可执行的goroutine，然后调用gogo函数，切换到goroutine的执行流程上。 1. 每隔61次调度轮回从全局队列找，避免全局队列中的g被饿死。 2. 从p.runnext获取g，从p的本地队列中获取。 3. 调用 findrunnable 找g，找不到的话就将m休眠，等待唤醒。 findrunnable 查找G的过程： 1. 调用 runqget ，尝试从P本地队列中获取G，获取到返回 2. 调用 globrunqget ，尝试从全局队列中获取G，获取到返回 3.</description>
    </item>
    <item>
      <title>linux 常用命令</title>
      <link>http://www.huasay.com/2022/10/16/linux/</link>
      <pubDate>Sun, 16 Oct 2022 21:49:21 +0800</pubDate>
      <guid>http://www.huasay.com/2022/10/16/linux/</guid>
      <description>linux常用命令 1 ls cp rm mkdir touch vim chmod chgrp chroot ss, tcp ping netstat top等 vim 常用命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0 跳至行首，不管有无缩进，就是跳到第0个字符 (常用) $ 跳至行尾 (常用) gg 跳至文首 (常用) G 调至文尾 (常用) dd 删除光标所在行 (常用) n+[Enter]	n 为数字。光标向下移动 n 行(常用) :1,$s/word1/word2/g	从第一行到最后一行寻找 word1 字符串，并将该字符串取代为 word2 ！(常用) dw 删除一个字(word) 𝑦𝑦复制一行 /pattern 向后搜索字符串pattern n 下一个匹配(如果是/搜索，则是向下的下一个，?</description>
    </item>
    <item>
      <title>Golang 面试资料整理</title>
      <link>http://www.huasay.com/2022/10/15/golang-interview-doc/</link>
      <pubDate>Sat, 15 Oct 2022 14:38:46 +0800</pubDate>
      <guid>http://www.huasay.com/2022/10/15/golang-interview-doc/</guid>
      <description>golang基础 defer golang底层 GMP模型 Sync包实现 Interface底层实现 Map底层实现 数据库 mysql mongo redis 消息队列 rabbitMQ kafka rocketMQ 微服务 konga 服务发现 服务熔断 服务降级 docker与k8s docker docker实现原理 docker的常用命令 docker编排 k8s istio 计算机网络 算法 排序算法 查找算法 树 </description>
    </item>
    <item>
      <title>Redis Zset 有序集合</title>
      <link>http://www.huasay.com/2021/10/31/redis-zset/</link>
      <pubDate>Sun, 31 Oct 2021 13:53:50 +0800</pubDate>
      <guid>http://www.huasay.com/2021/10/31/redis-zset/</guid>
      <description>zset是redis中常用的数据结构，一般使用场景是有序集合，排行榜等有序的需求我们会考虑使用。
背景： 最近也是做了相关的需求，用户发动态，将最新动态中的图片，视频或音频资源收集起来，列表中会展示用户最新4条动态的资源缩略图。因为动态还有个审核，所以 一般采集的时候会按审核的时间点采集，但展示却得按照创建时间展示。因此采用了zset的方案，score的值为创建时间，当审核完成时，会将该动态的资源信息以member存储至 zset中，取的时候，按照score(创建时间)由大到小读取
方案实施 存储，简单使用zadd命令，将动态资源信息(一个结构体，json格式化后以字符串形式)存储至zset中，createdAt为score 1 2 3 4 # ZADD KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN # 将一个或多个成员元素及其分数值加入到有序集当中 # ZADD file fileInfo createdAt -&amp;gt; ZADD file &amp;#34;{url:&amp;#39;xxx.jpg&amp;#39;,type:1}&amp;#34; 1635661958 维持zset大小，因为只需要取最新4条动态的资源，因此，对于旧的资源信息，可以删除4名开外的动态信息 1 2 3 4 5 6 # ZREMRANGEBYRANK key start stop # 移除有序集中，指定排名(rank)区间内的所有成员。 -&amp;gt; ZRemRangeByRank file 0 -5 # 因为默认是按从小到大排序的，我们如果要取最新的4条动态，需要从排名最小的元素开始删除， # 0代表排名最旧的，排最前的，-5表示排名倒数第5的， # 当zset中的元素超过4时，-5其实就相当于0,所以整个语句相当于删除第4名之后的动态资源信息 获取从大到小顺序的最新4条动态 1 2 3 # ZREVRANGE key start stop [WITHSCORES] # 按分数值递减(从大到小)来排列 -&amp;gt; ZREVRANGE file 0 3 不足之处 因为member的值中有url，有类型等信息，zset的member的长度是过大的，这就导致了并不能用上quickList，只能使用上skipList</description>
    </item>
    <item>
      <title>Mac OS下常备软件</title>
      <link>http://www.huasay.com/2021/10/31/mac-software/</link>
      <pubDate>Sun, 31 Oct 2021 01:50:58 +0800</pubDate>
      <guid>http://www.huasay.com/2021/10/31/mac-software/</guid>
      <description>基本软件 Homebrew chrome V2rayU,世界更精彩 iterm2,最好用的terminal ohmyzsh，最好用的shell zsh语法高亮插件，必备 zsh自动补全插件，必备 清理app git工具 Command Line Tools（需登录AppleID） Magnet alfred，听说是神器 工作软件 vscode，最好用的编辑器 golang postman 切换host ssh连接工具 最好用的redis工具 </description>
    </item>
    <item>
      <title>Golang 学习资料汇总，从入门到放弃</title>
      <link>http://www.huasay.com/2021/10/26/golang-learn-blog/</link>
      <pubDate>Tue, 26 Oct 2021 14:38:46 +0800</pubDate>
      <guid>http://www.huasay.com/2021/10/26/golang-learn-blog/</guid>
      <description>入门 go的一些使用小例子 无闻讲师的基础视频 go入门指南 go web基础 effective go 进阶 go内存模型 go程序诊断 标准库 标准库文档 标准库包的使用例子 go语言标准库 书籍或阅读清单 go开发者阅读清单 深入解析Go go语言编程之旅 go语法树入门 go语言设计-欧长坤 gopher博客推荐 draveness,go底层实现原理，讲得很详细 鸟窝，rpcx作者博客，go并发编程专栏 煎鱼大大，可以关注下公众号 李文周的博客 曹春晖的golang笔记 峰云golang专栏 大彬的blog gophers推荐清单 </description>
    </item>
    <item>
      <title>Interview_algorithm</title>
      <link>http://www.huasay.com/2021/03/05/interview_algorithm/</link>
      <pubDate>Fri, 05 Mar 2021 16:24:55 +0800</pubDate>
      <guid>http://www.huasay.com/2021/03/05/interview_algorithm/</guid>
      <description>排序算法 树 </description>
    </item>
    <item>
      <title>面试准备</title>
      <link>http://www.huasay.com/2021/03/04/interview-ready/</link>
      <pubDate>Thu, 04 Mar 2021 15:08:06 +0800</pubDate>
      <guid>http://www.huasay.com/2021/03/04/interview-ready/</guid>
      <description>语言 php 框架（laravel） socket编程，异步协程通信swoole php8 语言特性 php 底层实现原理（某个函数的实现，比如in_array; 某个语言结构，比如array） Golang Web框架 gin(目前使用的比较广泛的框架) 优点，与echo相比的优势 channel 通道 interface 接口 可看看B站泄漏的go代码结构 底层的一些实现原理及区别，比如array与slice 数据库及NoSql mysql的索引优化等 es相关可看看 redis相关使用场景 容器 docker相关 k8s相关 队列 rabbitMQ,目前公司消息队列使用 kafka，目前公司大数据存储使用 rocketMQ 阿里生态 计算机网络及网络协议 TCP的三次握手 socket相关 mqtt相关 wireshark抓包及tcpdump抓包 算法 letcode刷题 </description>
    </item>
    <item>
      <title>vim常用快捷键使用指南</title>
      <link>http://www.huasay.com/2020/12/22/vim-use-guide/</link>
      <pubDate>Tue, 22 Dec 2020 09:24:23 +0800</pubDate>
      <guid>http://www.huasay.com/2020/12/22/vim-use-guide/</guid>
      <description>自己在工作中经常使用的vim快捷键
快捷键 描述 0 跳至行首，不管有无缩进，就是跳到第0个字符 (常用) $ 跳至行尾 (常用) gg 跳至文首 (常用) G 调至文尾 (常用) dd 删除光标所在行 (常用) n+[Enter] n 为数字。光标向下移动 n 行(常用) :1,$s/word1/word2/g 从第一行到最后一行寻找 word1 字符串，并将该字符串取代为 word2 ！(常用) dw 删除一个字(word) yy 复制一行 p 粘贴 /pattern 向后搜索字符串pattern n 下一个匹配(如果是/搜索，则是向下的下一个，?搜索则是向上的下一个) N 上一个匹配(同上) :w 将缓冲区写入文件，即保存修改 :wq 保存修改并退出 :x 保存修改并退出 :q 退出，如果对缓冲区进行过修改，则会提示 :q! 强制退出，放弃修改 :set nu 显示行号 :set nonu 与 set nu 相反，为取消行号！ i 从目前光标所在处输入 [Esc] 退出编辑模式，回到一般模式中(常用) u 复原前一个动作。(常用) [Ctrl]+r 重做上一个动作。(常用) </description>
    </item>
    <item>
      <title>计算机网络 - tcp协议</title>
      <link>http://www.huasay.com/2020/08/21/computer-network-series-tcp/</link>
      <pubDate>Fri, 21 Aug 2020 00:54:04 +0800</pubDate>
      <guid>http://www.huasay.com/2020/08/21/computer-network-series-tcp/</guid>
      <description>定义 TCP提供一种面向连接的、可靠的字节流服务。所谓面向连接，即应用程序在使用TCP协议之前，必须先建立TCP连接，在传送数据完毕后，必须释放已经建立的连接。这个过程可以通俗的比喻成通话过程，在通话之前，必须先拨通号码，对方也必须接听，之后才可以通话。 所谓可靠的服务，即表示TCP连接传送的数据可以做到无差错，不丢失，不重复等。
握手 TCP的传输需要先建立连接，整个建立连接的过程一般称为三次握手
为什么是三次，不是两次，甚至更多 建立连接的过程是要保证双方可通信。比如邀请女神约会，我们都得先确定可以去，女神也得确定我可以去，不然谁先到约会地点就谁尴尬，容易被放鸽子。
我 &amp;ndash;&amp;gt; 明天晚上可以一起看电影么？ &amp;ndash;&amp;gt; 女神
女神 &amp;ndash;&amp;gt; 可以啊 &amp;ndash;&amp;gt; 我
如果两次握手是这样的，第一，女神这边确认是可以收到消息的了，我这边是确认可以发送消息的了。但女神那侧也并不确认自己是发送成功了，那怎么判断能发送成功，只能通过我这方再次发送通知过去才可以。
我 &amp;ndash;&amp;gt; 收到啦，一言为定 &amp;ndash;&amp;gt; 女神
当进行了第三次握手的时候，我们双方才能确定，前两次的话是生效了，三次握手的目标就是使双方心里都有底，确保双方最基本的发送与接收功能正常。女神可以确认自己的收发能力都OK，我也确认了自己的收发能力OK，才能最基本上的保证不被放鸽子，双方心里都有底。
为什么不是更多？
因为三次握手是确保双方通信条件都OK的最基本的方式，如果更多次，当然也可以，但是，会增加网络的消耗，4次，甚至5次&amp;hellip;N（N&amp;gt;5）次，也只能确保前N-1是正常的，也不能确保之后通信就是完全正常，所以3次是最好的握手判断。
三次握手的过程 TCP 报文 源端口与目的端口，各占2字节，16位 序号，4个字节，0~$$2^32$$ -1。起始序号在连接建立时设置，首部中的序号是指本报文段所发送的数据的第一个字节的序号。
例如，一个报文段第一个字节序号是301，最后一个字节序号是400，则下一个报文段的数据序号就得401. 确认序号，4个字节，期望收到对方下一个段文段的第一个字节的序号。
例如，收到了对方发来的报文段，序号字段是501，数据长度是200字节，则期望收到对方下一个数据序号为701，此时，就将发送给对方的确认序号设置成701. 首部长度，4位。首部长度最小为20个字节，但因为包含选项（不定长)，但最大长度只能是60个字节。因为该值长度为4位，4位最大值表示15，也代表整个首部最长为60字节，选项长度最大为40字节 保留位，6位，为今后所用，目前应置为0 紧急URG（URGent）当URG = 1时，表明紧急指针字段有效，与紧急指针字段配合使用，代表该报文段中有紧急数据，需加急处理 确认ACK（ACKnowledgment）当ACK = 1时确认号字段有有效，ACK = 0时，确认号无效，在连接建立后，所有传送的报文段都必须置ACK = 1 推送PSH（PuSH）两应用程序进行交互式通信时使用，当PSH = 1时，接收方应尽快的交付接收应用进程，不用等缓存满了再向上交付。该操作很少使用 复位RST (ReSeT) 当RST = 1时，表明TCP连接中出现严重差错（如主机崩溃），必须释放连接，然后再重新建立连接。RST = 1还用来拒绝非法报文段或拒绝打开一个连接 同步SYN（SYNchronization）在连接建立时用来同步序号。SYN = 1而ACK = 0时，说明这是一个连接请求报文段。对方若同意建立连接，则应在响应报文中设置SYN = 1 ，ACK = 1. 终止FIN （FINis）用来释放一个连接，当FIN = 1时，表明报文段的发送方数据已发送完毕，并要求释放连接 窗口 2字节，用于告诉对方，允许对方发送的数据量大小，自己的缓存空间还可接收多少数据量。 校验和 2字节，检验首部与数据 紧急指针 2字节。URG = 1时配合使用，本报文中紧急数据的末尾在报文中的位置 选项 最长40字节 挥手 为什么是4次，不是3次，甚至更少 4次挥手过程 如何实现可靠性 </description>
    </item>
    <item>
      <title>计算机网络系列 - 笔记</title>
      <link>http://www.huasay.com/2020/08/15/computer-network-series/</link>
      <pubDate>Sat, 15 Aug 2020 23:02:52 +0800</pubDate>
      <guid>http://www.huasay.com/2020/08/15/computer-network-series/</guid>
      <description>此系列用于记录计算机网络相关的知识要点
一来，复习巩固这一块的知识，防止每次用到相关知识却各种的查找资料。
二来，加强这一块的学习，自己之前也有很多不懂的地方，需要记录与学习
目前分以下下节整理
物理层 数据链接层 传输层 3.1 传输层 - TCP 3.2 传输层 - UDP 网络层 应用层 常用协议 DHCP 常用协议 ICMP 常用协议 ARP DNS </description>
    </item>
    <item>
      <title>Leetcode 26 最长公共前缀</title>
      <link>http://www.huasay.com/2020/07/29/leetcode-14/</link>
      <pubDate>Wed, 29 Jul 2020 14:26:49 +0800</pubDate>
      <guid>http://www.huasay.com/2020/07/29/leetcode-14/</guid>
      <description>编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀，返回空字符串 &amp;ldquo;&amp;quot;。
示例 1:
1 2 输入: flower&amp;#34;,&amp;#34;flow&amp;#34;,&amp;#34;flight&amp;#34;] 输出: &amp;#34;fl&amp;#34; 示例 2:
1 2 3 输入: [&amp;#34;dog&amp;#34;,&amp;#34;racecar&amp;#34;,&amp;#34;car&amp;#34;] 输出: &amp;#34;&amp;#34; 解释: 输入不存在公共前缀。 说明:
所有输入只包含小写字母 a-z 。
解题思路:
leetcode官方提供了不少解决方案. 主要解决方案有: 横向扫描法, 纵向扫描法,分治法, 二分查找法, 字典树. 个人觉得纵向扫描法是最容易想到的,而且效率也不错.
纵向扫描法:
纵向扫描时即依次遍历所有字符串的每一列，比较相同列上的字符是否相同，如果相同则继续对下一列进行比较，如果不相同则当前列不再属于公共前缀，当前列之前的部分为最长公共前缀。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func longestCommonPrefix(strs []string) string { strsLen := len(strs) if strsLen == 0 { return &amp;#34;&amp;#34; } for i := 0; i &amp;lt; len(strs[0]); i++ { for j := 1; j &amp;lt; strsLen; j++ { if i == len(strs[j]) || strs[0][i] !</description>
    </item>
    <item>
      <title>Leetcode 350 两个数组的交集 II</title>
      <link>http://www.huasay.com/2020/07/28/leetcode-350/</link>
      <pubDate>Tue, 28 Jul 2020 23:19:42 +0800</pubDate>
      <guid>http://www.huasay.com/2020/07/28/leetcode-350/</guid>
      <description>给定两个数组，编写一个函数来计算它们的交集。
示例 1：
1 2 输入：nums1 = [1,2,2,1], nums2 = [2,2] 输出：[2,2] 示例 2:
1 2 输入：nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出：[4,9] 说明：
输出结果中每个元素出现的次数，应与元素在两个数组中出现次数的最小值一致。 我们可以不考虑输出结果的顺序。 进阶：
如果给定的数组已经排好序呢？你将如何优化你的算法？ 如果 nums1 的大小比 nums2 小很多，哪种方法更优？ 如果 nums2 的元素存储在磁盘上，磁盘内存是有限的，并且你不能一次加载所有的元素到内存中，你该怎么办？ 解题思路【哈希法】：
将其中一个数组转换成哈希Map的类型，key为数组的值，value为数组中该数出现的次数。
循环遍历另一个数组，通过Map中是否有该值来判断，如果有相同的值，则记录该值，同时将Map对应key的value值减1.
时间复杂度：O(max(n,m))
空间复杂度：O(min(n,m))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func intersect(nums1 []int, nums2 []int) []int { m := map[int]int{} ret := []int{} for _, v := range nums1 { m[v]++ } for _, v := range nums2 { if m[v] !</description>
    </item>
    <item>
      <title>Leetcode 26 删除排序数组中的重复项</title>
      <link>http://www.huasay.com/2020/07/27/leetcode-26/</link>
      <pubDate>Mon, 27 Jul 2020 15:18:01 +0800</pubDate>
      <guid>http://www.huasay.com/2020/07/27/leetcode-26/</guid>
      <description>给定一个排序数组，你需要在 原地 删除重复出现的元素，使得每个元素只出现一次，返回移除后数组的新长度。不要使用额外的数组空间，你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
1 2 3 给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 你不需要考虑数组中超出新长度后面的元素。 示例 2:
1 2 3 给定 nums = [0,0,1,1,1,2,2,3,3,4], 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 你不需要考虑数组中超出新长度后面的元素。 说明:
为什么返回数值是整数，但输出的答案是数组呢? 请注意，输入数组是以“引用”方式传递的，这意味着在函数里修改输入数组对于调用者是可见的。 你可以想象内部操作如下:
1 2 3 4 5 6 7 // nums 是以“引用”方式传递的。也就是说，不对实参做任何拷贝 int len = removeDuplicates(nums); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 for (int i = 0; i &amp;lt; len; i++) { print(nums[i]); } 思路:</description>
    </item>
    <item>
      <title>golang channel</title>
      <link>http://www.huasay.com/2020/07/25/golang-channel/</link>
      <pubDate>Sat, 25 Jul 2020 11:10:05 +0800</pubDate>
      <guid>http://www.huasay.com/2020/07/25/golang-channel/</guid>
      <description>channel是golang中重要的组成部分。也是协程通信间通信的方式
channel常见理论面试题 channel 什么情况下会出现panic 试图往已close的channel继续发送 试图关闭已关闭的channel 试图关闭一个nil的channel 向为nil的channel发送数据会怎样 给定一个 nil channel c:
&amp;lt;-c 从 c 接收将永远阻塞
c &amp;lt;- v 发送值到 c 会永远阻塞
channel close后读的问题 关闭后继续读数据，已在缓冲区内的可以读取到数据，而后得到的是零值(对于int，就是0)。
我们可以通过for循环遍历channel，来获取到已经写入的值
1 2 3 for _, v := range channel{ } 对于可能获取到零值，我们可以使用断言判断，ok为false代表channel已关闭后读取的
1 value, ok := &amp;lt;- channel 也可以在close channel之后有意将channel 置为 nil， 如此再使用，并不会读取到零值
对于不带缓冲的ch，和带缓冲的一样，channel close掉之后并不影响读，只影响写入
channel底层源码剖析 go的运行时源码在runtime/chan.go文件下，可以在该文件下查看具体的数据结构，send(channel &amp;lt;-),receive(-&amp;gt; channel)，close channel等操作
数据结构 1 2 3 4 5 6 7 8 9 10 11 12 type hchan struct { qcount uint // total data in the queue 代表 chan 中已经接收但还没被取走的元素的个数。内建函数 len 可以返回这个字段的值。 dataqsiz uint // size of the circular queue 循环队列的大小 buf unsafe.</description>
    </item>
    <item>
      <title>linux 常用命令</title>
      <link>http://www.huasay.com/2018/04/12/bash/</link>
      <pubDate>Thu, 12 Apr 2018 22:06:21 +0800</pubDate>
      <guid>http://www.huasay.com/2018/04/12/bash/</guid>
      <description> Tab 键：自动补全命令或文件名； 2. Ctrl + C：强制退出当前进程或程序，中断正在运行的命令； Ctrl + Z：挂起当前进程或程序，将其放到后台执行； Ctrl + D：退出 shell 终端； Ctrl + L：清屏，与 clear 命令相同； Ctrl + A：将光标移动至命令行的开头； Ctrl + E：将光标移动至命令行的结尾； Ctrl + U：删除已输入的命令行，回到光标所在行的开头； Ctrl + K：删除光标所在位置到行末的所有字符； Ctrl + W：删除光标所在位置的前一个单词； Ctrl + R：搜索之前执行过的命令； !!：调出上一次执行的命令。 Ctrl + Y：粘贴最后一次删除的内容 Alt + B：将光标向后移动一个单词 Alt + F：将光标向前移动一个单词 Ctrl + B：将光标向后移动一个字符 Ctrl + F：将光标向前移动一个字符 !command：调出上一次以command为首的命令。 </description>
    </item>
  </channel>
</rss>
