大话图解gin源码
最近在网上搜了一下,对于gin框架用的人还是比较多的,我自己之前也在使用,但是对于源码解析这块,我没有看到自己想看到的那种从框架入手的解析图,所以嘿嘿嘿,我的机会就来了,今天就带来最完整的gin源码图解。希望通过这篇博客你也能自己学会拆轮子。
PS:本文建立在你已经能熟练使用gin的基础之上,如果还没用过可以去官网看一下:https://gin-gonic.com/zh-cn/docs/然后gin是对golang的http包的封装,所以最好对http包也要有了解。
整体分析逻辑先来说明一下我整体拆解的逻辑,对于一个框架,我喜欢从下面几个方面去入手拆解:
启动方式
如何使用
实现与特点针对于gin,我也将从这几个方面去入手,就会得到下面几个问题,带着问题看源码是必备条件。首先启动的时候gin做了些什么?gin封装了什么然后怎么去实现的?gin整体结构是怎么样的,有哪些结构?…
然后使用一个比较小的demo,然后先从方法入手,进源码看。
12345678910111213141516func main() { router := gin.Default() ...
图解goroutine调度
其实这个话题我早就想做了,奈何这个问题确实有点复杂,看了很多文章才有了一点点自己的理解。从golang一开始的使用我就已经开始好奇了,这个goroutine到底是怎么实现的呢?怎么就能搞出一个和线程类似,但是性能又那么好的东西的呢?
模型三个小伙子在看整体结构之前,我先来介绍三个小伙子,golang为了实现goroutine,定义了这样三个小伙子,让他们帮忙去实现。
G表示goroutine,存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等;另外G对象是可以重用的。
MM代表着真正的执行计算资源。在绑定有效的P后,进入调度器循环;而调度器循环的机制大致是从各种队列、P的本地队列中获取G,切换到G的执行栈上并执行G的函数,调用goexit做清理工作并回到M,如此反复。M并不保留G状态,这是G可以跨M调度的基础。
P表示逻辑processor,P的数量决定了系统内最大可并行的G的数量(前提:系统的物理cpu核数>=P的数量);P的最大作用还是其拥有的各种G对象队列、链表、一些cache和状态。
整体结构模型我们先来看 ...
Golang的interface
由于golang中说interface的文章太多了,很多都已经说的很细节了,所以我再说感觉也有点难。于是总结出几个关键问题,供你参考,如果能做到准确无误有理有据的回答,那么interface应该是没有问题了。
问题
interface底层结构有哪两种,分别是什么样子的,里面保存了哪些信息?
其中tab是什么时候生成的?
从别的类型转换成interface,从interface转换成别的类型,这两者的过程是怎么样的?
两个interface之间是否可以比较?
golang底层是如何判断一个类型是否实现了一个interface?
1、底层结构12345678910111213141516171819202122232425262728293031type eface struct { // 16 bytes on a 64bit arch _type *_type data unsafe.Pointer}type iface struct { // 16 bytes on a 64bit arch tab *itab data ...
Golang之channel
go中的一个精髓就是就是channel,那么你有没有想过,它究竟是怎么实现的呢?我之前就怀疑过,是不是就是通过一个数组保存了一下传入的数据,然后在接收方读一读就完事了,那么阻塞又是怎么实现的呢?close的时候需要注意些什么呢?
结构首先我们来看一下channel的结构是怎么样的。
1234567891011121314151617181920type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index r ...
你可能不知道的redis
以下是针对redis的知识点整理,用于复习,主要以罗列为主,详细具体讲解可以参考书《Redis设计与实现》,你可以过一遍看看有无知识点遗漏。个人能力有限,如果你还有补充可以再下方评论指出,万分感谢。
基础知识点
数据类型string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)底层实现包括:SDS动态字符串,双向链表,数组加链表,渐进式hash,跳表
redis是单线程
redis默认有16个数据库,默认先用0,可以使用select命令切换
过期策略惰性删除:当这个key被访问到,但是已经过期,那就删除定期删除:过一定时间,拿出一定的key判断,进行删除,如果超过一定数量,继续拿出一定的key进行判断删除,时间存在上限
内存超限当redis使用超过内存限制会根据策略来执行:noeviction:不能put,但是可以del和get,默认是这个策略volatile-lru:在有过期时间的key中,淘汰最少用的(redis是近似lru算法,会随机取几个淘汰最少用的)volatile-ttl:在有过期时间的key中,淘汰过期时间最 ...
你可能不知道的mysql
以下是针对mysql的知识点整理,用于复习,主要以罗列为主,详细具体讲解可以参考书《高性能mysql》,你可以过一遍看看有无知识点遗漏。
执行sql过程客户端 -> 连接器 -> 分析器 -> 优化器 -> 执行器 -> 存储引擎连接器:连接上数据库,长连接分析器:分析语法(包含解析器和预处理器,解析器生成解析树,预处理器判断字段存在歧义)优化器:选择正确的索引进行优化执行执行器:执行具体sql返回结果
mysql的两个重要日志redo-log(重做日志):固定大小的循环缓存,InnoDB使用,即使重启,只要记录到了redo-log就不会丢失。防止mysql意外。bin-log:归档日志,所有sql都会记录,并且采用追加,满了之后新开,有两种方式,一种是记录sql语句(statement),一种是row,记录出现的事件。如果只记录sql语句会导致主从同步上面存在问题,从库执行相同的sql得到效果不同,所以还有一种混合的方式,mysql会自动判断当前语句是否会造成主从不同步的情况,如果会,那么就使用row记录如果不会就是用sql记录,因为row记录会增加 ...
Golang的垃圾回收
最近垃圾分类的话题热度一下子就上去了,很多人因为垃圾分类的问题很头痛。因为垃圾这个话题,那我就想来说说Golang里面的垃圾,于是就有了这篇博客,golang中的垃圾回收。
现阶段网上针对golang垃圾回收的解析已经很多了,所以我也没有必要仔仔细细的一点点说,还是那个原则,用最直白的话告诉你,垃圾到底是怎么收的。
GC的意义首先本文后续都会使用 GC 代替垃圾回收这几个字。我们知道创建对象会给他分配内存资源,如果这个对象不使用了,而这个内存资源却一直被占用的话,那么我们的电脑很快就会被放满,所以需要将这些垃圾对象进行回收。
什么才是垃圾要回收,那么我们必须知道什么才是垃圾,什么不是垃圾。在我们看来,一个对象以后都不用了,就是垃圾。在程序看来,一个对象没有被引用了,就是垃圾。
GC的流程首先说明一下,下面说的停,都是STW,stop the world,全世界暂停,所有运行的都停下来了。这个流程可以由下面这张图展示:
第一个过程先告诉所有人,停一下,我来记录一下当前状态。
第二个过程告诉所有人,你们继续,该干嘛干嘛,我标记一下要用的对象一开始所有点是白色,首先从根节点出发,标记相连的 ...
不是我吹,你可能连defer都不清楚
在golang中,对于defer,我之前的理解就是和java中的finally代码块一样,没什么难度,但是吧,当我最近看的一些神奇的问题,我就发现原来并非想的那么简单。
先举个栗子123456789101112131415161718192021222324252627282930313233343536373839404142package mainimport "fmt"func main() { fmt.Println(DeferFunc1(1)) fmt.Println(DeferFunc2(1)) fmt.Println(DeferFunc3(1)) DeferFunc4()}func DeferFunc1(i int) (t int) { t = i defer func() { t += 3 }() return t}func DeferFunc2(i int) int { t := i defer fun ...
Golang之context
当我们使用一些golang框架的时候,总能在框架中发现有个叫做context的东西。如果你之前了解过java的spring,那么你肯定也听说过其中有个牛逼的ApplicationContext。Context这个东西好像随时随地都在出现,在golang中也是非常重要的存在。今天我们就来看看这个神奇的Context。
定义
首先我们要知道什么是context?
很多人把它翻译成上下文,其实这个是一个很难描述很定义的东西,对于这种东西,我习惯用功能去定义它。我的定义是:context是用于在多个goroutines之间传递信息的媒介。官方定义:At Google, we developed a context package that makes it easy to pass request-scoped values, cancelation signals, and deadlines across API boundaries to all the goroutines involved in handling a request.
用法同样的我们先来看看它的一些基本用法,大 ...
Golang之reflect
反射 —— 如果你之前学过一些别的语言,比如java可能就会了解,反射是一个传说中很厉害的操作,算是一个高级用法。而同时,很多人也会告诉你,反射是一个危险的操作,那么在golang中,反射又是怎么操作的呢?今天就来说说golang中的反射reflect。
反射的定义首先问问自己,你知道什么是反射吗?如果你有一个清楚的定义,证明你已经对反射非常熟悉了。官方的定义很官方,我就说说我的:反射,反射,从字面理解就是通过镜子(或类似的东西)看到自己。而在编程中,反射指的是在运行的过程中看到自己。在实际的编程过程中我们知道,创建的这个变量或者对象是什么类型或者是什么样子的,同时很容易能对它进行操作。而在运行过程中,程序没有我们的眼睛,它并不知道这个东西是怎么样的,这个时候就需要运用到反射。通过反射我可以知道自己长什么样子。
反射的使用reflect.TypeOf如果你对反射还是有些模糊,那么看下面这个最简单的例子
1234func main() { a := 1.3 fmt.Println("a的类型是", reflect.TypeOf(a)) ...
Golang的strings.go源码解析 - Rabin-Karp了解一下?
strings包是我们经常在处理字符串的时候要用的,这次我们来看看它其中的一些方法具体是如何实现的。我就找到其中常用的几个方法,然后针对其中比较难的部分还有应用到一些特别算法的部分进行分析。
ToUpper先来看个简单的ToUpper,将所有字符转换成大写。这个如果让我们自己实现也没有什么难度,就是遍历每个字符转换成大写就可以。
12345678910111213141516171819202122232425262728// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.func ToUpper(s string) string { isASCII, hasLower := true, false for i := 0; i < len(s); i++ { c := s[i] if c >= utf8.RuneSelf { isASCII = false break } has ...
Golang的slice
今天来说个简单的,也不简单的东西,那就是切片。slice对于golang来说那真的是一个非常常用的东西了,很多地方都会用到它,今天就来说说,slice底层是如何实现的,又有哪些坑是需要提前注意的。
slice结构很多第一次接触golang的同学都会认为,数组和切片是差不多的东西,其实不是的,切片是数组的封装。
12345type slice struct { array unsafe.Pointer len int cap int}
上面这个就是slice的结构,顺便说一下:slice的源码位置是:go/src/runtime/slice.go
其中array是一个指针,指向底层的数组
len代表slice的长度
cap代表slice的容量
为什么会有长度和容量这个区分呢,这两个东西是用来干什么的呢?我们往下看。
slice的长度和容量我们先来看一个最简单的案例
1234sli := make([]int, 2)fmt.Printf("len=%d cap=%d\n", len(sli), cap( ...
浅入深出ETCD之【集群部署与golang客户端使用】
之前说了etcd的简介,命令行使用,一些基本原理。这次来说说现实一点的集群部署和golang版本的客户端使用。因为在实际使用过程中,etcd的节点肯定是需要2N+1个进行部署的,所以有必要说明一下集群的部署。
集群部署网上有很多集群部署的教程,有的很复杂,其实对于我们实际使用来说,其实配置并不复杂,下面举例一种最简单的集群配置。(简单到你想不到~)
下载https://github.com/etcd-io/etcd/releases还是在github上面找到需要下载的版本我使用的是etcd-v3.3.13-linux-amd64.tar.gz使用wget下载到linux你喜欢的目录,或者本地下载完成之后上传均可。
部署首先我找了三台机器,对应ip为192.168.4.224192.168.4.225192.168.4.226PS:提醒一下记得开发对应防火墙的端口
然后将下载的文件解压,之后进入解压后的目录,分别使用下面的命令启动。(注意下面的命令对应的是三台不同的机器,你需要修改对应为你自己的ip)
1234567891011121314151617181920212223$ ./ ...
浅入深出ETCD之【raft原理】
这次我们来说说,有关于etcd原理的一些事情。之前我们已经了解到了etcd是一个分布式的k-v存储,那么它究竟是如何保证数据是如何复制到每个节点上面去的呢?又是如何保证在网络分区的情况下能正常工作下去?raft协议到底是什么?带着这些问题我们继续往下看。
raft选举策略我们知道etcd使用raft协议来保证整个分布式的节点网络能正常的运转并且能正确的将数据复制到每个节点上面去。那么什么是raft协议嘞?
首先我们有这样一个背景:raft是想维护整一个网络,其中有一个领导人,这个领导人负责将收到的信息同步给网络中的其他所有节点,从而保证整个网络数据一致。
如果你有一定的英文基础,我建议直接查看下面这个网站,它用动画非常清楚的描述了raft选举的整个过程:http://thesecretlivesofdata.com/raft/
这个其实已经说明的超级棒了,如果你还看不懂,我下面会用最简单的几个要点来进行最简单的说明。
大多数理论首先说明一个理论,叫做大多数理论,很简单,举个栗子:
有10个人,如果你将苹果给其中的6个人(大多数),那么你随机选择5个人,一定有一个人会有苹果。
在 ...
浅入深出ETCD之【简介与命令行使用】
你知道etcd吗?随着k8s的使用广泛之后,etcd被非常多的人所知道,同时又因为它可靠的分布式特性被很多人喜欢。所以,我准备有几篇博文来记录一下,从基本使用到线上部署再到原理分析,做一个系列。那么,今天先来说说它的简介与命令行的使用。
简介ETCD是什么我个人总结为下面用几个要点:
高可用K-V存储,就类似于redis一样的键值对存储。
允许应用实时监听存储中的K-V变化。
能够容忍单点故障,能够应对网络分区。
etcd利用raft在集群中同步K-V信息,raft是强一致的集群日志同步算法。
总结:etcd是一个分布式高可用k-v存储,通过复制达到每个节点存储的信息一致,从而保证高可用。
数据复制这里简单说一下复制的具体流程:
(client为我们的客户端,用来发出存储请求,leader和follower都是etcd的节点)就如图上所看到的,我叫它两段式提交:
客户端请求leader发送存储的数据,然后leader节点要将信息通过日志复制给大多数的follower节点,如上图所示,只需要复制给两个(加上它自己是三个)那么就是大多数节点。
leader当复制完成之后才会本地 ...
Golang指针与unsafe
我们知道在golang中是存在指针这个概念的。对于指针很多人有点忌惮(可能是因为之前学习过C语言),因为它会导致很多异常的问题。但是很多人学习之后发现,golang中的指针很简单,没有C那么复杂。所以今天就详细来说说指针。
指针的使用123a := 1p := &afmt.Println(p)
输出:0xc42001c070
可以看到p就是一个指针,也可以说是a的地址。
1234a := 1var p *intp = &afmt.Println(p)
或者也可以写成这样,因为我知道,在很多人看来,看到*号才是指针(手动滑稽)
123a := 1p := &afmt.Println(*p)
输出:1
然后使用就直接通过*号就能去到对应的值了,就这么简单
指针的限制Golang中指针之所以看起来很简单,是因为指针的功能不多。我们能看到的功能就是指针的指向一个地址而已,然后对于这个地址也只能进行传递,或者通过这个的地址去访问值。
不能像C语言中一样p++,这样移动操作指针,因为其实这样操作确实不安全,很容易访问到奇怪的区域。
不同类型的指针不能相互赋值、转换、比 ...
大话图解golang map源码详解
网上分析golang中map的源码的博客已经非常多了,随便一搜就有,而且也非常详细,所以如果我再来写就有点画蛇添足了(而且我也写不好,手动滑稽)。但是我还是要写,略略略,这篇博客的意义在于能从几张图片,然后用我最通俗的文字,让没看过源码的人最快程度上了解golang中map是怎么样的。
当然,因为简单,所以不完美。有很多地方省略了细节问题,如果你觉得没看够,或者本来就想了解详细情况的话在文末给出了一些非常不错的博客,当然有能力还是自己去阅读源码比较靠谱。
那么下面我将从这几个方面来说明,你先记住有下面几个方向,这样可以有一个大致的思路:
基础结构:golang中的map是什么样子的,是由什么数据结构组成的?
初始化:初始化之后map是怎么样的?
get:如何获取一个元素?
put:如何存放一个元素?
扩容:当存放空间不够的时候扩容是怎么扩的?
基础结构图解这个就是golang中map的结构,其实真的不复杂,我省略了其中一些和结构关系不大的字段,就只剩下这些了。
大话大话来描述一些要点:
最外面是hmap结构体,用buckets存放一些名字叫bmap的桶(数量不定,是2的指数倍 ...
Golang 读写锁RWMutex 互斥锁Mutex 源码详解
Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现。
引子问题我一般喜欢带着问题去看源码。那么对于读写锁,你是否有这样的问题,为什么可以有多个读锁?有没有可能出现有协程一直无法获取到写锁的情况?带着你的疑问来往下看看,具体这个锁是如何实现的。
如果你自己想看,我给出阅读的一个思路,可以先看读写锁,因为读写锁的实现依赖于互斥锁,并且读写锁比较简单一些,然后整理思路之后再去想一下实际的应用场景,然后再去看互斥锁。
下面我就会按照这个思路一步步往下走。
基础知识点
知识点1:信号量信号量是 Edsger Dijkstra 发明的数据结构(没错就是那个最短路径算法那个牛人),在解决多种同步问题时很有用。其本质是一个整数,并关联两个操作:
申请acquire(也称为 wait、decrement 或 P 操作)释放release(也称 signal、increment 或 V 操作)
acquire操作将信号量减 1,如果结果值为负则线程阻塞,且直到其他线程进行了信号量累加为正数才 ...
感谢您为本博客的长远发展做出的贡献
感谢您为本博客的长远发展做出的贡献
本博客不会悬挂任何广告,若发现评论中含有任何广告,请勿点击,长远发展源于我的热爱和你的投币~ 😄
本博客当前主要开销
云服务器 (约 300/年)
图床 CDN 存储和流量 (约 24/年)
域名 (约 72/年)
评论资源 (约 80/年)
….
赞助者
赞助时间
赞助者
金额
友链
2019 年 06 月 27 日
好大一瓶芬达
20
-
2022 年 06 月 14 日
李*双
1
-
2023 年 03 月 12 日
黄*博
66
-
2023 年 04 月 28 日
张*恒
72
-
赞助方式
微信
支付宝
赞助时记得备注昵称或者友链 📮
面向问题编程
面向问题编程Redis
IO多路复用和多线程会影响Redis分布式锁吗?
Redis 的 set nx 底层怎么实现的?
Redis 主从复制是如何实现的?
Redis 事件机制是如何实现的?
Redis Sorted Set 实现与应用
Redis List 设计与实现
假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如何将它们全部找出来?
Go
Golang 中 能否将 slice 作为 map 的 key?
MySQL
什么是索引下推?
读书吃饭睡觉
读书吃饭睡觉
程序员真是一个活到老学到老的职业,读书的时候别忘记吃饭睡觉哦~
《笨开发学习操作系统》
操作系统的学习总是让人觉得自己像个“笨蛋”,希望通过编写这个专栏来让自己变“聪明”一点 😂,希望你也可以
《笨开发学习操作系统》0 前言
《笨开发学习操作系统》1 启动
《笨开发学习操作系统》2 进程
《笨开发学习操作系统》3 内存
《笨开发学习操作系统》4 进程间通信
《笨开发学习操作系统》5 文件系统
《笨开发学习操作系统》6 输入输出系统
《笨开发学习操作系统》7 网络
《Go 单元测试指北》
Go 单元测试的小总结
《Go 单元测试指北》0 前言
《Go 单元测试指北》1 数据层
《Go 单元测试指北》2 逻辑层
《Go 单元测试指北》3 接口层
《Go 单元测试指北》4 其他小技
《一起读 kubernetes 源码》(WIP🚧)
学习读源码的方法和技巧,理解为什么要读源码 🤔
《一起读 kubernetes 源码》序
《一起读 kubernetes 源码》kubelet 如何创建 pod?
《一起读 kubernetes 源码》probe 监控 po ...