├── .gitignore ├── 2021-7-7 └── main.go ├── 2024-7-29 ├── circle.txt ├── main.go └── rectangle.txt ├── LICENSE ├── README.md ├── crypto └── README.md ├── design-pattern ├── command │ └── main.go ├── factory │ ├── inter.go │ ├── kingston.go │ ├── mian.go │ └── nvidia.go ├── observer │ └── main.go ├── proxy │ └── proxy.go ├── singleton │ └── singleton.go └── strategy │ └── main.go ├── geth ├── balance │ └── main.go ├── check-address │ └── main.go ├── create-wallet │ └── main.go ├── keystore │ ├── main.go │ └── tmp │ │ └── UTC--2024-04-17T09-49-17.022953000Z--ac31ced7ffc7be4d92ec494b0ec3966848e791b3 └── listen-block │ └── event_listener.go ├── go-reflect └── main.go ├── go-rpc-custom ├── client.go ├── param.go ├── rpc_test.go ├── server.go ├── session.go └── session_test.go ├── go-rpc ├── client │ └── client.go └── server │ └── server.go ├── go.mod ├── go.sum └── state-machine ├── machine.go └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | ### Go template 2 | /.idea 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /2021-7-7/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "sync" 8 | "sync/atomic" 9 | ) 10 | 11 | func main() { 12 | // 题目:将6,2,10,32,9,5,18,14,30,29从小到大进行排列。 13 | // mpSort() 14 | WaitGroupDemo() 15 | } 16 | 17 | // 冒泡排序 18 | func mpSort() { 19 | // 定义数组 20 | arr := [10]int{6, 2, 10, 32, 9, 5, 18, 14, 30, 29} 21 | for i := 0; i < len(arr); i++ { 22 | for j := 0; j < len(arr)-i-1; j++ { 23 | if arr[j] > arr[j+1] { 24 | arr[j], arr[j+1] = arr[j+1], arr[j] 25 | } 26 | } 27 | } 28 | fmt.Println(arr) 29 | } 30 | 31 | // 选择排序 32 | func xzSort() { 33 | 34 | } 35 | 36 | // 快排 37 | func kpSort() { 38 | 39 | } 40 | 41 | // 堆排 42 | func dpSort() { 43 | 44 | } 45 | 46 | func ChanTask() { 47 | // 题目:100个任务,每次执行10个,当err大于3时,取消任务 48 | task := make(chan struct{}, 100) 49 | ch := make(chan struct{}, 10) 50 | ctx, cancel := context.WithCancel(context.TODO()) 51 | 52 | var errNUm int32 53 | defer close(ch) 54 | defer close(task) 55 | for i := 0; i < 100; i++ { 56 | ch <- struct{}{} 57 | go func() { 58 | if i > 100 { 59 | atomic.AddInt32(&errNUm, 1) 60 | return 61 | } 62 | fmt.Println(i) 63 | <-ch 64 | task <- struct{}{} 65 | }() 66 | // 取消 67 | if errNUm >= 3 { 68 | cancel() 69 | } 70 | } 71 | for i := 0; i < 100; i++ { 72 | select { 73 | case <-task: 74 | case <-ctx.Done(): 75 | break 76 | } 77 | } 78 | } 79 | 80 | // sync.waitGroup 示例 81 | func WaitGroupDemo() { 82 | var sw sync.WaitGroup 83 | var errNum int32 = 0 84 | ctx,cancel := context.WithCancel(context.Background()) 85 | urls := []string{ 86 | "https://www.baidu.com", 87 | "https://www.jd.com", 88 | "https://www.baidu.com", 89 | "https://www.jd.com", 90 | "htp://www.zhenaiwanghhh.com", 91 | "h://www.zhenaiwanghhh.com", 92 | "ht://www.zhenaiwanghhh.com", 93 | "https://www.jd.com", 94 | } 95 | for _, url := range urls { 96 | sw.Add(1) 97 | go func(ctx context.Context, errNum *int32, url string) { 98 | fmt.Printf("task start \n") 99 | defer sw.Done() 100 | fmt.Println(*errNum) 101 | if *errNum >= 2 { 102 | cancel() 103 | } 104 | go func(ctx context.Context) { 105 | select { 106 | case <-ctx.Done(): 107 | fmt.Printf("cancel task") 108 | return 109 | } 110 | }(ctx) 111 | _, err := http.Get(url) 112 | 113 | if err != nil { 114 | fmt.Printf("err %v \n", err) 115 | atomic.AddInt32(errNum,1) 116 | return 117 | } 118 | fmt.Printf("task end \n") 119 | }(ctx, &errNum, url) 120 | } 121 | sw.Wait() 122 | } 123 | 124 | func ChanDemo() { 125 | 126 | } 127 | -------------------------------------------------------------------------------- /2024-7-29/circle.txt: -------------------------------------------------------------------------------- 1 | circle 10 -------------------------------------------------------------------------------- /2024-7-29/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "math" 7 | "os" 8 | "strconv" 9 | "strings" 10 | "sync" 11 | ) 12 | 13 | // 定义一个名为 Shape 的接口,该接口包括两个方法:Area() 用于计算图形的面积,Perimeter() 14 | // 用于计算图形的周长。实现两个结构体 Rectangle 和 Circle,它们均满足 Shape 接口。编写一个函数 printShapeInfo, 15 | // 该函数接受一个实现了 Shape 接口的对象作为参数,并打印该形状的面积和周长。 16 | 17 | type Shape interface { 18 | Area() float64 19 | Perimeter() float64 20 | } 21 | 22 | type Rectangle struct { 23 | Width, Height float64 24 | } 25 | 26 | // Area 面积 27 | func (r Rectangle) Area() float64 { 28 | return r.Width * r.Height 29 | } 30 | 31 | // Perimeter 周长 32 | func (r Rectangle) Perimeter() float64 { 33 | return 2 * (r.Width + r.Height) 34 | } 35 | 36 | type Circle struct { 37 | radius float64 38 | } 39 | 40 | func (c Circle) Area() float64 { 41 | return math.Pi * c.radius * c.radius 42 | } 43 | 44 | func (c Circle) Perimeter() float64 { 45 | return 2 * math.Pi * c.radius 46 | } 47 | 48 | func printShapeInfo(s Shape) { 49 | fmt.Printf("Area: %f\n", s.Area()) 50 | fmt.Printf("Perimeter %f\n", s.Perimeter()) 51 | } 52 | 53 | // 编写一个 Go 程序,实现一个多生产者单消费者(MPSC)模型,使用 goroutine 和 channel。 54 | // 生产者从文件中读取形状信息(例如矩形的长和宽或圆的半径),并将形状对象发送到一个 channel 中。 55 | // 消费者从该 channel 接收形状对象,并调用 printShapeInfo 函数打印形状的面积和周长。 56 | // 57 | // 预期效果: 58 | // 面试者需要展示对并发编程的理解,确保多个生产者能够正确地和一个消费者协作,无数据丢失。 59 | 60 | func producer(filename string, ShapeChan chan<- Shape, wg *sync.WaitGroup) { 61 | defer wg.Done() 62 | f, err := os.Open(filename) 63 | if err != nil { 64 | fmt.Printf("open file err:", err) 65 | return 66 | } 67 | defer f.Close() 68 | scanner := bufio.NewScanner(f) 69 | for scanner.Scan() { 70 | line := scanner.Text() 71 | parts := strings.Fields(line) 72 | if len(parts) == 0 { 73 | continue 74 | } 75 | shapeType := parts[0] 76 | // 这里也可以直接使用文件名称来实现分类 77 | switch shapeType { 78 | case "rectangle": 79 | if len(parts) != 3 { 80 | fmt.Printf("data err") 81 | continue 82 | } 83 | width, err := strconv.ParseFloat(parts[1], 64) 84 | if err != nil { 85 | continue 86 | } 87 | height, err := strconv.ParseFloat(parts[2], 64) 88 | if err != nil { 89 | continue 90 | } 91 | ShapeChan <- Rectangle{width, height} 92 | 93 | case "circle": 94 | if len(parts) != 2 { 95 | fmt.Printf("data err") 96 | continue 97 | } 98 | radius, err := strconv.ParseFloat(parts[1], 64) 99 | if err != nil { 100 | continue 101 | } 102 | ShapeChan <- Circle{radius: radius} 103 | default: 104 | fmt.Println("type not supported") 105 | } 106 | } 107 | } 108 | 109 | func consumer(shapeChan <-chan Shape, done chan<- struct{}) { 110 | for shape := range shapeChan { 111 | printShapeInfo(shape) 112 | } 113 | done <- struct{}{} 114 | } 115 | 116 | func main() { 117 | r := Rectangle{Width: 10, Height: 5} 118 | printShapeInfo(r) 119 | 120 | shapeChan := make(chan Shape) 121 | doneChan := make(chan struct{}) 122 | var wg sync.WaitGroup 123 | files := []string{"rectangle.txt", "circle.txt"} 124 | for _, filename := range files { 125 | wg.Add(1) 126 | go producer(filename, shapeChan, &wg) 127 | } 128 | 129 | go consumer(shapeChan, doneChan) 130 | 131 | wg.Wait() 132 | close(shapeChan) 133 | 134 | <-doneChan 135 | } 136 | -------------------------------------------------------------------------------- /2024-7-29/rectangle.txt: -------------------------------------------------------------------------------- 1 | rectangle 10 19 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 luckygopher 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go的一些知识整理 2 | 3 | ### go语言基础 4 | 熟悉语法,撸个百十道基础面试题就差不多了。 5 | 6 | ### go语言进阶 7 | go中有哪些锁? 8 | ```markdown 9 | sync.Mutex 互斥锁 10 | sync.RWMutex 读写锁 11 | ``` 12 | CSP并发模型? 13 | ```markdown 14 | CSP并发模型它并不关注发送消息的实体,而关注的是发送消息时使用的channel, 15 | go语言借用了process和channel这两个概念,process表现为go里面的goroutine, 16 | 是实际并发执行的实体,每个实体之间是通过channel来进行匿名传递消息使之解藕, 17 | 从而达到通讯来实现数据共享。 18 | 19 | 不要通过共享内存来通信,而要通过通信来实现内存共享。 20 | 21 | 1、sync.mutex 互斥锁(获取锁和解锁可以不在同一个协程,当获取到锁之后, 22 | 未解锁,此时再次获取锁将会阻塞) 23 | 2、通过channel通信 24 | 3、sync.WaitGroup 25 | ``` 26 | GPM模型指的是什么?goroutine的调度时机有哪些?如果syscall阻塞会发生什么? 27 | ```markdown 28 | 在go中是通过channel通信来共享内存的。 29 | 30 | G:指的是Goroutine,也就是协程,go中的协程做了优化处理,内存占用仅几kb 31 | 且调度灵活,切换成本低。 32 | P:指的是processor,也就是处理器,感觉也可理解为协程调度器。 33 | M:指的是thread,内核线程。 34 | 35 | 调度器的设计策略: 36 | 1、线程复用:当本线程无可运行的G时,M-P-G0会处于自旋状态,尝试从全局队列 37 | 获取G,再从其他线程绑定的P队列中偷取G,而不是销毁线程;当本线程因为G进行 38 | 系统调用阻塞时,线程会释放绑定的P队列,如果有空闲的线程可用就复用空闲的 39 | 线程,不然就创建一个新的线程来接管释放出来的P队列。 40 | 2、利用并行:GOMAXPROCS设置P的数量,最多有这么多个线程分布在多个cpu上 41 | 同时运行。 42 | 3、抢占:在coroutine中要等待一个协程主动让出CPU才执行下一个协程,在Go 43 | 中,一个goroutine最多占用CPU 10ms,防止其他goroutine被饿死。 44 | 45 | go func的流程: 46 | 1、创建一个G,新建的G优先保存在P的本地队列中,如果满了则会保存到全局队列中。 47 | 2、G只能运行在M中,一个M必须持有一个P,M与P时1:1关系,M会从P的本地队列 48 | 弹出一个可执行状态的G来执行。 49 | 3、一个M调度G执行的过程是一个循环机制。 50 | 4、如果G阻塞,则M也会被阻塞,runtime会把这个线程M从P摘除,再创建或者 51 | 复用其他线程来接管P队列。 52 | 5、当G、M不在被阻塞,即系统调用结束,会先尝试找会之前的P队列,如果之前 53 | 的P队列已经被其他线程接管,那么这个G会尝试获取一个空闲的P队列执行,并放 54 | 入到这个P的本地队列。否则这个线程M会变成休眠状态,加入空闲线程队列,而G 55 | 则会被放入全局队列中。 56 | 57 | M0: 58 | M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量runtime.m0中, 59 | 不需要在heap上分配,M0负责执行初始化操作和启动第一个G,之后M0与其他的M一样。 60 | G0: 61 | G0是每次启动一个M都会第一个创建的goroutine,G0仅负责调度,不指向任何可执行 62 | 函数,每个M都会有一个自己的G0,在调度或者系统调用时会使用G0的栈空间,全局变量 63 | 的G0是M0的。 64 | 65 | N:1-----出现阻塞的瓶颈,无法利用多个cpu 66 | 1:1-----跟多线程/多进程模型无异,切换协程代价昂贵 67 | M:N-----能够利用多核,过于依赖协程调度器的优化和算法 68 | 69 | 70 | 同步协作式调度 71 | 异步抢占式调度 72 | 73 | ``` 74 | channel底层的数据结构是什么?发送和接收元素的本质是什么? 75 | ```go 76 | type hchan struct { 77 | qcount uint // *chan里元素数量 78 | dataqsiz uint // *底层循环数组的长度,就是chan的容量 79 | buf unsafe.Pointer // *指向大小为dataqsiz的数组,有缓冲的channel 80 | elemsize uint16 // chan中的元素大小 81 | closed uint32 // chan是否被关闭的标志 82 | elemtype *_type // chan中元素类型 83 | recvx uint // *当前可以接收的元素在底层数组索引(<-chan) 84 | sendx uint // *当前可以发送的元素在底层数组索引(chan<-) 85 | recvq waitq // 等待接收的协程队列(<-chan) 86 | sendq waitq // 等待发送的协程队列(chan<-) 87 | lock mutex // 互斥锁,保证每个读chan或者写chan的操作都是原子的 88 | } 89 | 90 | // waitq是sudog的一个双向链表,sudog实际上是对goroutine的一个封装。 91 | type waitq struct { 92 | first *sudog 93 | last *sudog 94 | } 95 | 96 | // channel的发送和接收操作本质上都是"值的拷贝"(只是拷贝它的值而已), 97 | ``` 98 | channel使用应该注意哪些情况,在哪些情况下会死锁/阻塞? 99 | ```markdown 100 | 1、一个无缓冲channel在一个主go程里同时进行读和写; 101 | 2、无缓冲channel在go程开启之前使用通道; 102 | 3、通道1中调用了通道2,通道2中调用了通道1; 103 | 4、读取空的channel; 104 | 5、超过channel缓存继续写入数据; 105 | 6、向已经关闭的channel中写入数据不会导致死锁,但会Panic异常。 106 | 7、close一个已经关闭的channel会Panic异常。 107 | ``` 108 | 那些类型不能作map的为key?map的key为什么是无序的? 109 | ```markdown 110 | map的key必须可以比较,func、map、slice这三种类型不可比较, 111 | 只有在都是nil的情况下,才可与nil (== or !=)。因此这三种类型 112 | 不能作为map的key。 113 | 114 | 数组或者结构体能够作为key????有些能,有些不能,要看字段或者元素是否可比较 115 | 116 | 1、map在扩容后,会发生key的搬迁,原来落在同一个bucket中的key可能分散,key的位置发生了变化。 117 | 2、go中遍历map时,并不是固定从0号bucket开始遍历,每次都是从一个随机值序号的bucket开始遍历, 118 | 并且是从这个bucket的一个随机序号的cell开始遍历。 119 | 3、哈希查找表用一个哈希函数将key分配到不同的bucket(数组的下标index)。不同的哈希函数实现也 120 | 会导致map无序。 121 | 122 | "迭代map的结果是无序的"这个特性是从go1.0开始加入的。 123 | ``` 124 | 如何解决哈希查找表存在的"碰撞"问题(hash冲突)? 125 | ```markdown 126 | hash碰撞指的是:两个不同的原始值被哈希之后的结果相同,也就是不同的key被哈希分配到了同一个bucket。 127 | 128 | 链表法:将一个bucket实现成一个链表,落在同一个bucket中的key都会插入这个链表。 129 | 130 | 开放地址法:碰撞发生后,从冲突的下标处开始往后探测,到达数组末尾时,从数组开始处探测,直到找到一个 131 | 空位置存储这个key,当找不到位置的情况下会触发扩容。 132 | ``` 133 | map是线程安全的么? 134 | ```markdown 135 | map不是线程安全的,sync.map是线程安全的。 136 | 137 | 在查找、赋值、遍历、删除的过程中都会检测写标志,一旦发现写标志"置位"等于1,则直接panic, 138 | 因为这表示有其他协程同时在进行写操作。赋值和删除函数在检测完写标志是"复位"之后,先将 139 | 写标志位"置位",才会进行之后的操作。 140 | 141 | 思考:为什么sync.map为啥是线程安全?? 142 | ``` 143 | map的底层实现原理是什么? 144 | ```markdown 145 | type hmap struct { 146 | count int // len(map)元素个数 147 | flags uint8 //写标志位 148 | B uint8 // buckets数组的长度的对数,buckets数组的长度是2^B 149 | noverflow uint16 150 | hash0 uint32 151 | buckets unsafe.Pointer // 指向buckets数组 152 | oldbuckets unsafe.Pointer // 扩容的时候,buckets长度会是oldbuckets的两倍 153 | nevacuate uintptr 154 | extra *mapextra 155 | } 156 | 157 | // 编译期间动态创建的bmap 158 | type bmap struct { 159 | topbits [8]uint8 160 | keys [8]keytype 161 | values [8]valuetype 162 | pad uintptr 163 | overflow uintptr 164 | } 165 | 166 | 在go中map是数组存储的,采用的是哈希查找表,通过哈希函数将key分配到不同的bucket, 167 | 每个数组下标处存储的是一个bucket,每个bucket中可以存储8个kv键值对,当每个bucket 168 | 存储的kv对到达8个之后,会通过overflow指针指向一个新的bucket,从而形成一个链表。 169 | ``` 170 | map的扩容过程是怎样的? 171 | ```markdown 172 | 相同容量扩容 173 | 2倍容量扩容 174 | 175 | 扩容时机: 176 | 1、当装载因子超过6.5时,表明很多桶都快满了,查找和插入效率都变低了,触发扩容。 177 | 178 | 扩容策略:元素太多,bucket数量少,则将B加1,buctet最大数量(2^B)直接变为 179 | 原来bucket数量的2倍,再渐进式的把key/value迁移到新的内存地址。 180 | 181 | 2、无法触发条件1,overflow bucket数量太多,查找、插入效率低,触发扩容。 182 | (可以理解为:一座空城,房子很多,但是住户很少,都分散了,找起人来很困难) 183 | 184 | 扩容策略:开辟一个新的bucket空间,将老bucket中的元素移动到新bucket,使得 185 | 同一个bucket中的key排列更紧密,节省空间,提高bucket利用率。 186 | ``` 187 | map的key的定位过程是怎样的? 188 | ```markdown 189 | 对key计算hash值,计算它落到那个桶时,只会用到最后B个bit位,再用哈希值的高8位 190 | 找到key在bucket中的位置。桶内没有key会找第一个空位放入,冲突则从前往后找到第一个空位。 191 | ``` 192 | iface和eface的区别是什么?值接收者和指针接收者的区别? 193 | ```markdown 194 | iface和eface都是Go中描述接口的底层结构体,区别在于iface包含方法。 195 | 而eface则是不包含任何方法的空接口:interface{} 196 | 197 | 注意:编译器会为所有接收者为T的方法生成接收者为*T的包装方法,但是链接器会把程序中确定不会用到的方法都裁剪掉。因此*T和T不能定义同名方法。 198 | 生成包装方法是为了接口,因为接口不能直接使用接收者为值类型的方法。 199 | 200 | 如果方法的接收者是值类型,无论调用者是对象还是对象指针,修改的都是对象的副本,不影响调用 201 | 者;如果方法的接收者是指针类型,则调用者修改的是指针指向的对象本身。 202 | 203 | 如果类型具备"原始的本质",如go中内置的原始类型,就定义值接收者就好。 204 | 如果类型具备"非原始的本质",不能被安全的复制,这种类型总是应该被共享,则可定义为指针接收者。 205 | ``` 206 | context是什么?如何被取消?有什么作用? 207 | ```markdown 208 | type Context interface { 209 | // 当context被取消或者到了deadline,返回一个被关闭的channel 210 | Done() <-chan struct{} 211 | // 在channel Done关闭后,返回context取消原因 212 | Err() error 213 | // 返回context是否会被取消以及自动取消时间(即deadline) 214 | Deadline() (deadline time.Time,ok boll) 215 | // 获取key对应的value 216 | Value(key interface{}) interface{} 217 | } 218 | 219 | type canceler interface { 220 | cancel(removeFromParent bool, err error) 221 | Done() <-chan struct{} 222 | } 223 | 224 | context:goroutine的上下文,包含goroutine的运行状态、环境、现场等信息。 225 | 226 | 实现了canceler接口的Context,就表明是可取消的。 227 | 228 | context用来解决goroutine之间退出通知、元数据传递的功能。比如并发控制和超时控制。 229 | 230 | 注意事项: 231 | 1、不要将Context塞到结构体里,直接将Context类型作为函数的第一参数,而且一般都 232 | 命名为ctx。 233 | 2、不要向函数传入一个nil的Context,如果你实在不知道传什么,标准库给你准备好了 234 | 一个Context:todo 235 | 3、不要把本应该作为函数参数的类型塞到Context中,Context存储的应该是一些共同 236 | 的数据。例如:登陆的session、cookie等 237 | 4、同一个Context可能会被传递到多个goroutine,Context是并发安全的。 238 | ``` 239 | slice的底层数据结构是怎样的? 240 | ```markdown 241 | type slice struct { 242 | array unsafe.Pointer // 底层数组的起始位置 243 | len int 244 | cap int 245 | } 246 | 247 | slice的元素要存在一段连续的内存中,底层数据是数组,slice是对数组的封装,它描述一个数组的片段。 248 | slice可以向后扩展,不可以向前扩展。 249 | s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)。 250 | make会为slice分配底层数组,而new不会为slice分配底层数组,所以array其实位置会是nil,可以通过append来分配底层数组。 251 | 252 | slice扩容方案计算: 253 | 1、预估扩容后的容量:即假设扩容后的 cap 等于扩容后元素的个数 254 | if 255 | oldCap * 2 < cap,则newCap = cap 256 | else 257 | oldLen < 1024,则newCap = oldCap * 2 258 | oldLen >= 1024,则newCap = oldCap * 1.25 259 | 260 | 2、预估内存大小(int一个元素占8子节,string一个元素占16子节) 261 | 假设元素类型是int,预估容量newCap = 5,那么预估内存 = 5 * 8 = 40 byte 262 | 263 | 3、匹配到合适的内存规格(内存分配规格为8、16、32、48、64、80....) 264 | 实际申请的内存为 48 byte, cap = 48 / 8 = 6 265 | ``` 266 | 你了解GC么?常见的GC实现方式有哪些? 267 | ```markdown 268 | GC即垃圾回收机制:引用计数、三色标记法+混合写屏障机制 269 | ``` 270 | go的GC有那三个阶段?流程是什么?如果内存分配速度超过了标记清除速度怎么办? 271 | ```markdown 272 | goV1.3之前采用的是普通标记清除,流程如下: 273 | 1、开始STW,暂停程序业务逻辑,找出不可达的对象和可达对象; 274 | 2、给所有可达对象做上标记; 275 | 3、标记完成之后,开始清除未标记的对象; 276 | 4、停止STW,让程序继续运行,然后循环重复这个过程,直到程序生命周期结束。 277 | 278 | goV1.5三色标记法,流程如下: 279 | 1、只要是新创建的对象,默认的颜色都标记为白色; 280 | 2、每次GC回收开始,从根节点开始遍历所有对象,把遍历到的对象从白色集合 281 | 放入灰色集合; 282 | 3、遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色 283 | 对象放入黑色集合; 284 | 4、重复3中内容,直到灰色集合中无任何对象; 285 | 5、回收白色集合中的所有对象。 286 | 287 | 犹如剥洋葱一样,一层一层的遍历着色,但同时满足以下条件会导致对象丢失: 288 | 条件1:一个白色对象被黑色对象引用; 289 | 条件2:灰色对象与白色对象之间的可达关系同时被解除。 290 | 291 | 强三色:强制性的不允许黑色对象引用白色对象。 292 | 弱三色:黑色对象可以引用白色对象,但白色对象存在其他灰色对象对它的引用,或者 293 | 可达它的链路上游存在灰色对象。 294 | 295 | goV1.8三色+混合写屏障机制,栈不启动屏障,流程如下: 296 | 1、GC开始将栈上的对象全部扫描并标记为黑色(之后不再进行重复扫描,无需STW); 297 | 2、GC期间,任何在栈上创建的新对象均标记为黑色; 298 | 3、被删除的对象和被添加的对象均标记为灰色; 299 | 4、回收白色集合中的所有对象。 300 | 301 | 总结: 302 | v1.3普通标记清除法,整体过程需要STW,效率极低; 303 | v1.5三色标记法+屏障,堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描 304 | 一次栈(需要STW),效率普通; 305 | v1.8三色标记法+混合写屏障,堆空间启动,栈空间不启动屏障,整体过程几乎不需要STW, 306 | 效率较高。 307 | 308 | 如果申请内存的速度超过预期,运行时就会让申请内存的应用程序辅助完成垃圾收集的扫描阶段, 309 | 在标记和标记终止阶段结束之后就会进入异步的清理阶段,将不用的内存增量回收。并发标记会 310 | 设置一个标志,并在mallocgc调用时进行检查,当存在新的内存分配时,会暂停分配内存过快 311 | 的哪些goroutine,并将其转去执行一些辅助标记的工作,从而达到放缓内存分配和加速GC工作 312 | 的目的。 313 | ``` 314 | 内存泄漏如何解决? 315 | ```markdown 316 | 1、通过pprof工具获取内存相差较大的两个时间点heap数据。htop可以查看内存增长情况。 317 | 2、通过go tool pprof比较内存情况,分析多出来的内存。 318 | 3、分析代码、修复代码。 319 | ``` 320 | 内存逃逸分析? 321 | ```markdown 322 | 在函数中申请一个新的对象,如果分配在栈中,则函数执行结束可自动将内存回收; 323 | 如果分配在堆中,则函数执行结束可交给GC处理。 324 | 325 | 案例: 326 | 函数返回局部变量指针; 327 | 申请内存过大超过栈的存储能力。 328 | ``` 329 | 你是如何实现单元测试的?有哪些框架? 330 | ```markdown 331 | testing、GoMock、testify 332 | ``` 333 | 334 | ### mysql 335 | sql语句中group by和order by谁先执行? 336 | ```markdown 337 | select (all | distinct) 字段或者表达式 (from子句) (where子句) (group by子句) (having子句) (order by子句) (limit子句); 338 | 339 | 1、from子句:构成提供给select的数据源,所以一般后面写表名 340 | 2、where子句:where子句是对数据源中的数据进行过滤的条件设置 341 | 3、group by子句:对通过where子句过滤后的数据进行分组 342 | 4、having子句:对分组后的的数据进行过滤,作用和where类似 343 | 5、order by子句:对取得的数据结果以某个标准(字段)进行排序,默认是asc(正序) 344 | 6、limit子句:对取出来的数据进行数量的限制,从第几行开始取出来几行数据 345 | 7、all | distinct 用于设置select出来的数据是否消除重复行,默认是all既不消除所有的数据都出来;distinct表示会消除重复行 346 | ``` 347 | mysql的存储引擎有哪些?都有什么区别? 348 | ```markdown 349 | MyISAM,InnoDB 350 | 区别: 351 | 1、MySAM不支持事务、不支持外键,而innodb支持 352 | 2、都是b+树作为索引结构,但是实现方式不同,innoDb是聚集索引,myisam是非聚集索引。 353 | 3、innoDb不保存表的具体行数,count(*)需要全表扫描,而myisam用一个变量保存了整个 354 | 表的行数,速度更快。 355 | 4、innodb组小的锁粒度是行锁,myisam最小的锁粒度是表锁。myisam一个更新语句会锁住 356 | 整张表,导致其他查询、更新都会被阻塞,因此并发当问受限。 357 | ``` 358 | 为什么需要B-树/B+树? 359 | ```markdown 360 | 因为传统的树是在内存中进行的数据搜索,而当数据量非常大时,内存不够用,大部分数据只能存放在 361 | 磁盘上,只有需要的数据才加载到内存中。一般情况下内存访问的时间约为50ns,而磁盘在10ms左右, 362 | 大部分时间会阻塞在磁盘IO上,因此要提高性能,得减少磁盘IO次数。 363 | ``` 364 | mysql索引底层实现? 365 | ```markdown 366 | MyISAM引擎使用B+树作为索引结构,叶子节点的data域存放的是数据记录的地址,需要再寻址一次才能 367 | 得到数据,是"非聚集索引"。 368 | 369 | InnoDB引擎也使用B+树作为索引结构,区别如下: 370 | 1、InnoDB的叶子节点data域保存了完整的数据记录,因此主键索引很高效,是"聚集索引"。 371 | 2、InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询做到覆盖索引 372 | 会很高效。 373 | 374 | hash索引底层的数据结构是哈希表,在绝大多数需求为单条记录查询的时候,可以选择哈希索引, 375 | 查询性能最快;其余大部分场景,建议都选择BTree索引。 376 | ``` 377 | 为什么MongoDB使用B-树,而mysql使用B+树? 378 | ```markdown 379 | Mg是类似json格式的数据模型,对性能要求高,属于聚合型数据库,而B-树恰好key和data域聚合 380 | 在一起,且最佳查询时间复杂度为O(1); 381 | 382 | mysql是一种关系型数据库,B+树的特性可以增加区间访问性,而B-树并不支持;B+树的查询时间复杂 383 | 度始终为O(log n),查询效率更加稳定。 384 | ``` 385 | B-树和B+树的区别? 386 | ```markdown 387 | 1、B+树非叶子节点只存储key的副本,相当于叶子节点的索引,真实的key和data域 388 | 都在叶子节点存储,查询时间复杂度固定为O(log n),而B-树节点内部每个key都带着data域, 389 | 查询时间复杂度不固定,与key在树中的位置有关,最好为O(1)。 390 | 391 | 2、B+树叶子节点带有顺序指针,两两相连大大增加区间访问性,利用磁盘预读提前将访问节点附近的数据读入内存,减 392 | 少了磁盘IO的次数,也可使用在范围查询,而B-树每个节点key和data在一起,则无法区间查找。 393 | 394 | 3、磁盘是分block的,一次磁盘IO会读取若干个block,具体和操作系统有关,磁盘IO数据大小是固定的,在一 395 | 次IO中,单个元素越小,量就越大。由于B+树的节点只存储key的副本,这就意味着B+树单次磁盘IO的数据大于B-树, 396 | 自然磁盘IO次数也更少。 397 | ``` 398 | 覆盖索引是什么? 399 | ```markdown 400 | 从索引中就能查到记录,而不需要索引之后再回表中查记录,避免了回表的产生,减少了树的搜索次数。 401 | ``` 402 | 索引设计的原则?需要注意事项? 403 | ```markdown 404 | 1、出现在where子句中的列,或者连接子句中指定的列。 405 | 2、基数较小的列,索引效果较差,没有必要在此列建立索引。 406 | 3、取值离散大的字段放在联合索引的前面,count()值越大说明字段唯一值越多,离散程度高。 407 | 4、尽量使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,节省索引空间。 408 | 5、应符合最左前缀匹配原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like) 409 | 就停止匹配,其中(=和in)可以乱序,mysql查询优化器会优化成索引可以识别的形式。 410 | 411 | 注意: 412 | 1、不要过度索引,并非索引越多也好,索引需要空间来存储,也需要定期维护,并且索引会降低 413 | 写操作的性能。 414 | 2、非必要情况,所有列都应该指定列为NOT NULL,使用零值作为默认值。 415 | ``` 416 | 什么是数据库事务? 417 | ```markdown 418 | 事务是一个不可分割的数据操作序列,也是数据库并发控制的基本单位,其执行结果必须使数据库 419 | 从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。 420 | ``` 421 | 事务的四大特性(ACID)? 422 | ```markdown 423 | 1、原子性:事务是最小的执行单位,包含的所有数据库操作要么全部成功,要么全部失败回滚; 424 | 2、一致性:执行事务前后数据都必须处于一致性状态; 425 | 3、隔离性:一个事务未提交的业务结果是否对于其它事务可见,对应有四种事务隔离级别。 426 | 4、持久性:一个事务一旦被提交了,那么对数据库中数据的改变就是永久性的,即使是在数据库 427 | 系统遇到故障的情况下也不会丢失提交事务的操作。 428 | ``` 429 | 事务隔离级别有哪些?如何解决脏读和幻读? 430 | ```markdown 431 | 数据库定义了四种不同的事务隔离级别,由低到高依次为: 432 | Read uncommitted 读取未提交-----三者均可能 433 | Read committed 读取已提交------解决了脏读,可能出现不可重复读和幻读 434 | Repeatable read 可重复读------可能出现幻读,解决了脏读和不可重复读 435 | Serializable 可串行化(对所涉及到的表加锁,并非行级锁) 436 | 437 | 脏读:读取未提交数据(A事务读到未提交的B事务数据) 438 | 解决:设置事务级别为Read committed 439 | 440 | 不可重复读:前后多次读取,数据内容不一致(原因:读取了其它事务更改的数据且提交了事务,针对update操作) 441 | 解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,才允许其它事务操作。 442 | 443 | 幻读:前后多次读取,数据总量不一致(原因:读取了其它事务新增的数据且提交了事务,针对insert和delete操作) 444 | 解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,才允许其它事务操作。 445 | 446 | mysql的事务隔离级别默认为Repeatable read(可重复读,可能会出现幻读) 447 | ``` 448 | 乐观锁和悲观锁的实现方式? 449 | ```markdown 450 | 乐观锁:基于数据版本号实现(基于mvcc理论) 451 | 悲观锁:数据库的锁机制 452 | ``` 453 | innoDB存储引擎的锁的算法有那三种? 454 | ```markdown 455 | Record lock: 单个行记录上的锁 456 | Gap lock: 间隙锁,锁定一个范围,不包括记录本身 457 | Next-key lock: record+gap 锁定一个范围,包含记录本身 458 | 459 | 注意:(间隙:范围查询中,存在于范围内,但不存在的记录,这称为间隙) 460 | ``` 461 | mysql的mvcc实现原理是什么? 462 | ```markdown 463 | MVCC只适用mysql事务隔离级别:Read committed 和 Repeatable Read,MVCC的 464 | 版本是在事务提交之后才会产生。 465 | 466 | 多版本并发控制:Undo log实现MVCC,并发控制通过锁来实现。 467 | 简单来说就是在每一行记录的后面增加两个隐藏列,记录创建版本号和删除版本号,而 468 | 每一个事务在启动的时候,都有一个唯一的递增的版本号,通过版本号来减少锁的争用。 469 | 470 | 1、在插入操作时,记录的创建版本号就是事务版本号; 471 | 2、在更新操作时,采用的是先标记旧的那行记录为已删除,并且删除版本号是事务版本 472 | 号,然后插入一行新的记录; 473 | 3、删除操作的时候,就把事务版本号作为删除版本号; 474 | 4、查询时,符合删除版本号大于当前事务版本号并且创建版本号小于或者等于当前事务版本号 475 | 这两个条件的记录才能被事务查询出来。 476 | ``` 477 | bin log、redo log、undo log作用是什么?有什么区别? 478 | ```markdown 479 | bin log: 480 | 记录mysql服务层产生的日志,常用来进行数据恢复、数据库复制。 481 | 482 | redo log: 483 | 记录了数据操作在物理层面的修改。mysql中大量使用缓存,缓存存在与内存中, 484 | 修改操作时会直接修改内存,而不是立刻修改磁盘,当内存和磁盘数据不一致时,称内存中 485 | 的数据为脏页。 486 | 487 | 为了保证数据的安全性,事务进行时会不断产生redo log,在事务提交时进行一次flush操作, 488 | 保存到磁盘中,redo log是按照顺序写入的,磁盘的顺序读写的速度远大于随机读写。当数据 489 | 库或主机失效重启时,会根据redo log进行数据的恢复,如果redo log中有事务提交,则进行 490 | 事务提交修改数据。这样实现了事务的原子性、一致性、持久性。 491 | 492 | undo log: 493 | 当进行数据修改时除了记录redo log,还会记录undo log,它记录了修改的反向操作,用于 494 | 数据的撤回操作,可以实现事务回滚,mvcc就是根据undo log实现回溯到某个特定的版本的 495 | 数据的。 496 | ``` 497 | 大表数据查询,如何优化? 498 | ```markdown 499 | 1、优化sql语句+索引 500 | 2、增加缓存,memcached、redis 501 | 3、做主从复制,读写分离 502 | 4、拆表---垂直拆分、水平拆分 503 | ``` 504 | Mysql数据库cpu飙升如何排查? 505 | ```markdown 506 | 1、首先top查看是否真是由于mysqld占用导致的。 507 | 2、show processlist,分析session情况,有没有激增,是不是有消耗资源的sql在运行。 508 | 3、找出消耗高的sql,explain查看执行计划。 509 | ``` 510 | 主从复制的实现步骤? 511 | ```markdown 512 | 1、主库db的操作事件被写入到 binlog 513 | 2、从库发起连接,连接到主库 514 | 3、此时主库创建一个 binlog dump thread 线程,把binlog的内容发送到从库 515 | 4、从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log 516 | 5、还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置 517 | 开始执行读取到的操作事件,将内容写入到slave的db 518 | ``` 519 | ### kafka 520 | kafka为什么性能高? 521 | ```markdown 522 | 1、kafka本身是分布式集群,同时采用了分区技术,具有较高的并发度; 523 | 2、顺序写入磁盘,Kafka 的 producer 生产数据,要写入到 log 文件中,写的过程是一直追加到文件末端,为顺序写。 524 | 3、零拷贝技术 525 | ``` 526 | kafka重复消费可能的原因以及处理方式? 527 | ```markdown 528 | 原因1:消费者宕机、重启等,导致消息已经消费但是没有提交offset; 529 | 原因2:消费者使用自动提交offset,但当还没有提交的时候,有新的消费者加入或者移除,发生了rebalance。 530 | 再次消费的时候,消费者会根据提交的偏移量来,于是重复消费了数据。 531 | 原因3:消息处理耗时,或者消费者拉取的消息量太多,处理耗时,超过了max.poll.interval.ms的配置时间, 532 | 导致认为当前消费者已经死掉,触发再均衡。 533 | 534 | 解决方案:消费者实现消费幂 535 | 1、消费表 536 | 2、数据库唯一索引 537 | 3、缓存消费过的消息ID 538 | ``` 539 | 触发重平衡(rebalanced)的情况? 540 | ```markdown 541 | 1、有新的消费者加入消费组、或已有消费者主动离开组 542 | 2、消费者超过session时间未发送心跳(已有 consumer 崩溃了) 543 | 3、一次poll()之后的消息处理时间超过了max.poll.interval.ms的配置时间,因为一次poll() 544 | 处理完才会触发下次poll()(已有 consumer 崩溃了) 545 | 4、订阅主题数发生变更 546 | 5、订阅主题的分区数发生变更 547 | ``` 548 | kafka消息丢失的原因以及解决方式? 549 | ```markdown 550 | 生产者丢失消息情况:可能因为网络问题并没有发送出去。 551 | 解决:可以给send方法添加回调函数,按一定次数、间隔重试。 552 | 553 | 消费者丢失消息情况:消费者自动提交offset,拿到消息还未真正消费,就挂掉了,但是offset却被自动提交了。 554 | 解决:关闭自动提交offset,每次在真正消费完消息之后之后再自己手动提交offset,这样解决了消息丢失,但会带来重复消费问题。 555 | 556 | kafka丢失消息情况: 557 | leader副本所在的 broker 突然挂掉,那么就要从 follower 副本重新选出一个 leader ,但是 leader 的数据还有一些 558 | 没有被 follower 副本的同步的话,就会造成消息丢失。 559 | 560 | 解决:设置 ack = all。ack是Kafka生产者很重要的一个参数。代表则所有副本都要接收到该消息之后该消息才算真正成功被发送。 561 | ``` 562 | Kafka中的消息有序吗? 563 | ```markdown 564 | kafka无法保证整个topic多个分区有序,但是由于每个分区(partition)内, 565 | 每条消息都有一个offset,故可以保证分区内有序 566 | ``` 567 | topic的分区数可以增加或减少吗?为什么? 568 | ```markdown 569 | topic的分区数只能增加不能减少,因为减少掉的分区也就是被删除的分区的数据难以处理. 570 | 571 | 注意:消费者组中的消费者个数如果超过topic的分区,那么就会有消费者消费不到数据. 572 | ``` 573 | kafka是怎么维护offset的? 574 | ```markdown 575 | 维护offset的原因: 576 | 由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置 577 | 的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。 578 | 579 | 维护offset的方式: 580 | Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中,从0.9版本开始, 581 | consumer默认将offset保存在Kafka一个内置的topic中,该topic为**__consumer_offsets**。 582 | 583 | 关于offset的常识: 584 | 消费者提交消费位移时提交的是当前消费到的最新消息的offset+1而不是offset。 585 | ``` 586 | kafka集群消息积压问题如何处理? 587 | ```markdown 588 | 从两个角度去分析: 589 | 1、如果是 Kafka 消费能力不足,则可以考虑增加 Topic 的分区数,并且同时提升消费 590 | 组的消费者数量,消费者数=分区数。(两者缺一不可) 591 | 2、如果是下游的数据处理不及时,提高每批次拉取的数量。如果是因为批次拉取数据过少 592 | (拉取 数据/处理时间<生产速度),也会使处理的数据小于生产的数据,造成数据积压。 593 | ``` 594 | ### redis 595 | redis单线程为什么效率也这么高? 596 | ```markdown 597 | 1. redis是基于内存的,内存的读写速度非常快. 598 | 2. redis是单线程的,省去了很多上下文切换线程的时间. 599 | 3. IO多路复用 600 | ``` 601 | redis有那五种常用的数据结构?应用场景以及实现原理是什么? 602 | ```markdown 603 | ziplist: 压缩列表,此数据结构是为了节约内存而开发 604 | intset: 整数集合,是集合键的底层实现方式之一 605 | quicklist:ziplist的一个双向链表 606 | skiplist:跳表 607 | 608 | 1. string 计数 sds(raw,embstr,int) 609 | 2. hash 缓存结构数据 quicklist(hashtable,ziplist) 610 | 3. list 异步消息队列 (ziplist,linkedlist) 611 | 4. set(无序、成员唯一) 计算共同喜好(交集)、统计访问ip (intset,hashtable) 612 | 5. zset(有序、成员唯一) 排行榜、延迟队列 (ziplist,skiplist) 613 | ``` 614 | redis的过期策略? 615 | ```markdown 616 | 定期删除+惰性删除策略 617 | 618 | - 定期删除策略:Redis 启用一个定时器定时监视所有的 key,判断key是否过期,过期的话就删除。 619 | 这种策略可以保证过期的 key 最终都会被删除,但是也存在严重的缺点:每次都遍历内存中所有的数据, 620 | 非常消耗 CPU 资源,并且当 key 已过期,但是定时器还处于未唤起状态,这段时间内 key 仍然可以用。 621 | 622 | - 惰性删除策略:在获取 key 时,先判断 key 是否过期,如果过期则删除。这种方式存在一个缺点: 623 | 如果这个 key 一直未被使用,那么它一直在内存中,其实它已经过期了,会浪费大量的空间。 624 | 625 | - 这两种策略天然的互补,结合起来之后,定时删除策略就发生了一些改变,不在是每次扫描全部的 626 | key 了,而是随机抽取一部分 key 进行检查,这样就降低了对 CPU 资源的损耗,惰性删除 627 | 策略互补了为检查到的key,基本上满足了所有要求。但是有时候就是那么的巧,既没有被定时器抽取到, 628 | 又没有被使用,这些数据又如何从内存中消失?没关系,还有内存淘汰机制,当内存不够用时,内存淘汰机制就会上场。 629 | ``` 630 | Redis中的批量操作Pipeline? 631 | ```markdown 632 | 非pipeline:client一个请求,redis server一个响应,期间client阻塞。 633 | Pipeline:redis的管道命令,允许client将多个请求依次发给服务器,过程中不需要等待请求的回复,而是在最后读取所有结果。 634 | ``` 635 | redis与mysql数据一致性解决方案? 636 | ```markdown 637 | 延迟双删策略: 638 | 1、先删除缓存,然后更新数据库,但可能在更新未完成之前,有请求穿透到db取了旧数据并写入了缓存,因此需要更新完数据库之后,延迟几十毫秒,再删一次缓存。 639 | 2、先更新数据库,再删除缓存,再延迟删一次。 640 | 如果删除失败则重试,比如放入队列循环删除。 641 | ``` 642 | 发生缓存穿透、击穿、雪崩的原因以及解决方案? 643 | ```markdown 644 | - 缓存穿透 645 | 是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据 646 | 则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时, 647 | 可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。(简单来说就是缓存和数据库 648 | 都不存在这个数据,这种情况称为穿透) 649 | 解决方案: 650 | 1、接口层增加校验,比如id<=0这种一定不存在的情况直接拦截掉。 651 | 2、如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存, 652 | 但它的过期时间会很短,最长不超过五分钟。 653 | 3、采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这 654 | 个bitmap拦截掉,从而避免了对底层存储系统的查询压力。 655 | 656 | - 缓存雪崩 657 | 缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发 658 | 到DB,DB瞬时压力过重雪崩。重启导致缓存失效,也可能出现并发到DB。 659 | 解决方案: 660 | 1、考虑用加锁(互斥锁),锁定key之后完成db的查询以及缓存的更新之后再释放锁定key,从而避免失效时大量的并发请求落到底层存储系统上。 661 | 2、缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。 662 | 3、重启导致缓存失效,我们可以采用缓存预热,提前使用一个接口更新好缓存,再启动服务。 663 | 664 | - 缓存击穿(场景:热点数据) 665 | 缓存中不存在,但数据库中有的数据(一般是缓存时间到期)。缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发 666 | 请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。与雪崩区别是击穿指并发查同一条数据。 667 | 解决方案: 668 | 1、使用互斥锁(SETNX) 669 | 2、设置热点数据永远不过期,数据是需要维护的。 670 | ``` 671 | redis的持久化方案RDB和AOF详解? 672 | ```markdown 673 | RDB:在指定的时间间隔内将内存中的数据集快照写入磁盘,它恢复时就是将快照文件直接读到内存里。 674 | AOF:持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。AOF文件中的命令全部以 Redis 协议的格式来保存, 675 | 新命令会被追加到文件的末尾,Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小 676 | ``` 677 | ### 网络协议 678 | 浏览器访问一个网站,都经历了怎样一个流程? 679 | ```markdown 680 | 1、DNS 解析:将域名解析成 IP 地址 681 | 2、TCP 连接:TCP 三次握手 682 | 3、发送 HTTP 请求 683 | 4、服务器处理请求并返回 HTTP 报文 684 | 5、浏览器解析渲染页面 685 | 6、断开连接:TCP 四次挥手 686 | ``` 687 | 什么是HTTP与HTTPS有什么区别? 688 | ```markdown 689 | 1、HTTP的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头 690 | 2、HTTP是不安全的,而 HTTPS 是安全的 691 | 3、HTTP标准端口是80 ,而 HTTPS 的标准端口是443 692 | 4、在OSI网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层 693 | 5、HTTP无法加密,而HTTPS 对传输的数据进行加密 694 | 6、HTTP无需证书,而HTTPS 需要CA机构颁发的SSL证书 695 | ``` 696 | 什么是Http协议无状态协议?怎么解决Http协议无状态协议? 697 | ```markdown 698 | 无状态协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息。 699 | 也就是说,当客户端一次HTTP请求完成以后,客户端再发送一次HTTP请求,HTTP并不知道当前客户端是一个”老用户“。 700 | 可以使用Cookie来解决无状态的问题,Cookie就相当于一个通行证,第一次访问的时候给客户端发送一个Cookie, 701 | 当客户端再次来的时候,拿着Cookie(通行证),那么服务器就知道这个是”老用户“ 702 | ``` 703 | HTTP请求报文与响应报文格式? 704 | ```markdown 705 | - 请求报文包含: 706 | 1、请求行:包含请求方法、URI、HTTP版本信息 707 | 2、请求头部字段 708 | 3、空行 709 | 4、请求内容实体 710 | 711 | - 响应报文包含: 712 | 1、状态行:包含HTTP版本、状态码、状态码的原因短语 713 | 2、响应头部字段 714 | 3、空行 715 | 4、响应内容实体 716 | ``` 717 | HTTP常见的状态码有哪些? 718 | ```markdown 719 | 1XX系列:指定客户端应相应的某些动作,代表请求已被接受,需要继续处理。由于 HTTP/1.0 协议中没有定义任何 720 | 1xx 状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送 1xx 响应。 721 | 722 | 2XX系列:代表请求已成功被服务器接收、理解、并接受。这系列中最常见的有200、201状态码。 723 | 200状态码:表示请求已成功,请求所希望的响应头或数据体将随此响应返回 724 | 201状态码:表示请求成功并且服务器创建了新的资源,且其 URI 已经随Location 头信息返回。假如需要的资源 725 | 无法及时建立的话,应当返回 ‘202 Accepted’ 726 | 727 | 202状态码:服务器已接受请求,但尚未处理 728 | 729 | 3XX系列:代表需要客户端采取进一步的操作才能完成请求,这些状态码用来重定向,后续的请求地址(重定向目标 730 | )在本次响应的 Location 域中指明。这系列中最常见的有301、302状态码。 731 | 301状态码:被请求的资源已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动 732 | 将请求者转到新位置。 733 | 734 | 302状态码:请求的资源临时从不同的URI响应请求,但请求者应继续使用原有位置来进行以后的请求 735 | 736 | 304自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。 如果网页自请求者上次请 737 | 求后再也没有更改过,您应将服务器配置为返回此响应(称为 If-Modified-Since HTTP 标头)。 738 | 739 | 4XX系列:表示请求错误。代表了客户端看起来可能发生了错误,妨碍了服务器的处理。常见有:401、404状态码。 740 | 401状态码:请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 741 | 403状态码:服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且 742 | 这个请求也不应该被重复提交。 743 | 744 | 404状态码:请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂 745 | 时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已 746 | 经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被 747 | 拒绝或者没有其他适合的响应可用的情况下。 748 | 749 | 5xx系列:代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬 750 | 件资源无法完成对请求的处理。常见有500、503状态码。 751 | 500状态码:服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在 752 | 服务器的程序码出错时出现。 753 | 503状态码:由于临时的服务器维护或者过载,服务器当前无法处理请求。通常,这个是暂时状态,一段时间会恢复 754 | ``` 755 | 网络的七层结构及其作用? 756 | ```markdown 757 | - 应用层(数据):确定进程之间通信的性质以满足用户需要以及提供网络与用户应用 758 | - 表示层(数据):主要解决用户信息的语法表示问题,如加密解密 759 | - 会话层(数据):提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制,如服务器验证用户登录便是由会话层完成的 760 | - 传输层(段):实现网络不同主机上用户进程之间的数据通信,可靠与不可靠的传输,传输层的错误检测,流量控制等 761 | - 网络层(包):提供逻辑地址(IP)、选路,数据从源端到目的端的传输 762 | - 数据链路层(帧):将上层数据封装成帧,用MAC地址访问媒介,错误检测与修正 763 | - 物理层(比特流):设备之间比特流的传输,物理接口,电气特性等 764 | ``` 765 | TCP/IP协议四层? 766 | ```markdown 767 | 应用层、传输层、网络层、数据链路层 768 | ``` 769 | TCP协议和UDP协议有什么区别? 770 | ```markdown 771 | CP和UDP协议属于传输层协议,主要区别: 772 | 1、TCP是面向连接的,UDP是无连接的; 773 | 2、TCP是可靠的,UDP是不可靠的; 774 | 3、TCP只支持点对点通信,UDP支持一对一、一对多、多对一、多对多的通信模式; 775 | 4、TCP是面向字节流的,UDP是面向报文的; 776 | 5、TCP有拥塞控制机制;UDP没有拥塞控制,适合媒体通信; 777 | 6、TCP首部开销(20个字节)比UDP的首部开销(8个字节)要大; 778 | ``` 779 | TCP协议的三次握手和四次挥手?为什么是三次和四次? 780 | ```markdown 781 | - 三次握手(我要和你建立链接,你真的要和我建立链接么,我真的要和你建立链接,成功) 782 | 783 | 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态, 784 | 等待Server确认。 785 | 786 | 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1, 787 | 随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。 788 | 789 | 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据 790 | 包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状 791 | 态,完成三次握手,随后Client与Server之间可以开始传输数据了。 792 | 793 | - 四次挥手(我要和你断开链接;好的,断吧。我也要和你断开链接;好的,断吧) 794 | 795 | 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。 796 | 797 | 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进 798 | 入CLOSE_WAIT状态。此时TCP链接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。 799 | 800 | 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。 801 | 802 | 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进 803 | 入CLOSED状态,完成四次挥手。 804 | 805 | **三次握手**:目的是为了防止已失效的链接请求报文突然又传送到了服务端,因而产生错误。 806 | 807 | **四次挥手**:TCP 协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP 是全双工模式,这就意味着,当 A 808 | 向 B 发出 FIN 报文段时,只是表示 A 已经没有数据要发送了,而此时 A 还是能够接受到来自 B 发出的数据;B 向 A 发 809 | 出 ACK 报文段也只是告诉 A ,它自己知道 A 没有数据要发了,但 B 还是能够向 A 发送数据。 810 | ``` 811 | http1.0、http1.1、http2.0有什么区别? 812 | ```markdown 813 | http1.0和http1.1区别: 814 | 1、http1.1默认开启长连接keep-alive,在一个TCP连接(一次完整的tcp握手和挥手)上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。 815 | 2、http1.0客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能。HTTP1.1支持只发送header信息(不带任何body信息), 816 | 如果服务器认为客户端有权限请求服务器,则返回100,客户端接收到100才开始把请求body发送到服务器;如果返回401,客户端就可以不用发送请求body了节约了带宽。 817 | 3、http1.1的请求消息和响应消息都支持host域,且请求消息中如果没有host域会报告一个错误(400 Bad Request)。 818 | 819 | http1.1和http2.0区别: 820 | 1、http2.0在1.1的基础上增加了多路复用,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。 821 | http1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。 822 | 2、http1.1不支持header数据的压缩,http2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。 823 | 3、http2.0引入了server push,它允许服务端推送资源给浏览器,免得客户端再次创建连接发送请求到服务器端获取。(例如:客户端向服务器发送一个获取html的请求, 824 | 不需要再次请求去获取html所依赖的css、js,而是在第一次html请求时,服务器端主动的都推送给客户端) 825 | ``` 826 | ProtoBuff协议相比其它有什么好处? 827 | ```markdown 828 | pb是一种轻便高效的"结构化数据"存储格式,可以用于"结构化数据"的序列化和反序列化。 829 | 1、跨语言,支持大多数语言开发,代码开源,运行稳定可靠。 830 | 2、性能好、效率高,占据空间和运行时间相比json和xml小,二进制序列化格式,数据压缩紧凑,占据字节数小。 831 | 3、支持向后向前兼容,比如向后兼容:A、B两模块,B升级有"statue"属性,可设置为非必填或者缺省,这样A就被兼容了。 832 | 4、适合数据大、传输速率敏感的场合使用。 833 | 5、支持数据类型多。 834 | 6、数据结构化定义灵活,可嵌套定义。 835 | ``` 836 | ### 分布式系统、微服务架构 837 | 什么是分布式事务? 838 | ```markdown 839 | 二阶段提交、三阶段提交 840 | ``` 841 | 分布式锁有哪些实现方式?分别会存在什么问题,哪种实现更好? 842 | ```markdown 843 | zk(ZooKeeper)锁实现原理: 844 | (1)创建一个目录mylock; 845 | (2)线程A想获取锁就在mylock目录下创建临时顺序节点; 846 | (3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; 847 | (4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点; 848 | (5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。 849 | 850 | redis事务: 851 | 1、事务提供了一种将多个命令打包,然后一次性有序(FIFO)执行的机制 852 | 2、事务执行过程不会被中断 853 | 3、带WATCH命令的事务会将客户端和被监视的键在数据库watched_keys字典中进行关联,当键被修改程序会将所有监视键的客 854 | 户端REDIS_DIRTY_CAS标识打开 855 | 4、在客户端提交EXEC命令时,会检查REDIS_DIRTY_CAS标识,标识打开事务将不会被执行 856 | 5、Redis事务具有ACID的特性(当服务器运行在AOF模式下,并且appendfsync选项值为always时才具有持久性 857 | 858 | redis 实现分布式锁: 859 | 2.6.12版本之后命令: SET key value EX 10 NX (合并了1、2两个步骤) 860 | 核心思路: 861 | 1、使用setnx设置互斥锁 862 | 2、为了避免异常情况导致死锁,因此需要为锁设置过期时间 863 | 3、为了避免删锁混乱,导致锁永久失效,需要为每个请求分配唯一的value值,再删锁时,验证是否属于自身的锁。 864 | 4、为了避免在删锁的操作过程中的异常情况,如锁过期,新的请求获得锁,此时删除的是新的锁。可以再执行任务中新启一个协程每隔10s去检查主程是否还持有锁, 865 | 如果还持有锁,则为锁进行续期。 866 | 867 | 基于lua脚本实现redis的乐观锁 868 | ``` 869 | 说下微服务架构有哪些组件,这些组件是如何实现自身功能的? 870 | ```markdown 871 | 872 | ``` 873 | 微服务架构如何设计? 874 | ```markdown 875 | 876 | ``` 877 | 如何防止超卖? 878 | ```markdown 879 | 1、使用 redis 的队列来实现。将要促销的商品数量以队列的方式存入 redis 中,每当用户抢到一件促销商品则从队列中删除一个数据,确保商品不会超卖 880 | 881 | 2、乐观锁,增加版本号,查询库存和更新库存时比较版本号 882 | ``` 883 | 884 | ### 数据结构与算法 885 | 题目:将6,2,10,32,9,5,18,14,30,29从小到大进行排列,使用冒泡排序 886 | ```go 887 | package main 888 | 889 | import "fmt" 890 | 891 | func main() { 892 | // 定义数组 893 | arr := [10]int{6, 2, 10, 32, 9, 5, 18, 14, 30, 29} 894 | for i := 0; i < len(arr); i++ { 895 | for j := 0; j < len(arr)-i-1; j++ { 896 | if arr[j] > arr[j+1] { 897 | arr[j], arr[j+1] = arr[j+1], arr[j] 898 | } 899 | } 900 | } 901 | fmt.Println(arr) 902 | } 903 | ``` 904 | 905 | 快排 906 | 907 | 选择排序 908 | 909 | 堆排 910 | 911 | 动态规划 912 | 913 | ### 其它 914 | 关系型数据库和非关系型数据库有什么区别? 915 | -------------------------------------------------------------------------------- /crypto/README.md: -------------------------------------------------------------------------------- 1 | ## 对称加密 2 | - 使用相同的密钥:加密和解密使用的是同一个密钥,因此称为对称加密。 3 | - 速度快:加密和解密的速度比非对称加密快,适合处理大数据量的加密。 4 | - 安全性依赖于密钥传输:双方必须事先安全地共享密钥,这样在通信过程中才能使用相同的密钥进行加密和解密。如果密钥在传输中被拦截,通信就可能不安全。 5 | - 常见算法:AES、DES 6 | 7 | ## 非对称加密 8 | - 使用不同的密钥:非对称加密使用一对密钥,公钥和私钥。公钥用于加密,私钥用于解密。公钥可以公开,私钥必须保密。 9 | - 速度慢:加密和解密的速度较慢,适合加密较小的数据或用来安全传递对称加密密钥。 10 | - 安全性高:即使公钥公开,只有持有私钥的人才能解密加密的数据,因此传输过程的安全性较高。 11 | - 常见算法:RSA、DSA、BLS 12 | 13 | ## 数字签名 14 | > [!IMPORTANT] 15 | > 生成数字签名有两种方法,一种是直接对消息签名的方法,另一种是对消息的散列值签名的方法。 16 | > 直接对消息签名的方法需要对整个消息进行加密,再加上非对称加密本来就速度较慢,所以这种方法非常耗时。 17 | 18 | #### 对消息散列值进行签名的逻辑 19 | ```mermaid 20 | graph TD 21 | subgraph "发送者A" 22 | A1["(1) A用单向散列函数计算消息散列值"] 23 | A2["单向散列函数"] 24 | A3["散列值"] 25 | A4["(2) A用自己的私钥加密散列值"] 26 | A5["A的私钥"] 27 | A6["加密"] 28 | A7["签名"] 29 | 30 | A1 --> A2 31 | A2 --> A3 32 | A3 --> A4 33 | A4 --> A6 34 | A5 --> A6 35 | A6 --> A7 36 | end 37 | 38 | subgraph "接收方B" 39 | B1["消息"] 40 | B2["单向散列函数"] 41 | B3["散列值"] 42 | B4["收到的签名"] 43 | B5["A的公钥"] 44 | B6["解密"] 45 | B7["解密收到的签名得到散列值"] 46 | B8["(5) B检查解密得到的散列值与A直接发送的消息的散列值进行对比"] 47 | B9["如果两者一致,则签名验证成功"] 48 | 49 | B1 --> B2 50 | B2 --> B3 51 | B4 --> B6 52 | B5 --> B6 53 | B6 --> B7 54 | B3 --> B8 55 | B7 --> B8 56 | B8 --> B9 57 | end 58 | 59 | A1 -->|"发送消息"| B1 60 | A7 -->|"(3) A将消息和签名发送给B"| B4 61 | 62 | classDef default fill:#f9f,stroke:#333,stroke-width:2px; 63 | classDef process fill:#bbf,stroke:#f66,stroke-width:2px; 64 | class A2,A6,B2,B6 process; 65 | ``` -------------------------------------------------------------------------------- /design-pattern/command/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | 设计模式-命令模式 5 | */ 6 | 7 | import "fmt" 8 | 9 | // Doctor 医生-命令接收者 10 | type Doctor struct{} 11 | 12 | func (d *Doctor) treatEye() { 13 | fmt.Println("医生治疗眼睛") 14 | } 15 | 16 | func (d *Doctor) treatNose() { 17 | fmt.Println("医生治疗鼻子") 18 | } 19 | 20 | // Command 抽象的命令 21 | type Command interface { 22 | Treat() 23 | } 24 | 25 | // CommandTreatEye 治疗眼睛的病单 26 | type CommandTreatEye struct { 27 | doctor *Doctor 28 | } 29 | 30 | func (cmd *CommandTreatEye) Treat() { 31 | cmd.doctor.treatEye() 32 | } 33 | 34 | // CommandTreatNose 治疗鼻子的病单 35 | type CommandTreatNose struct { 36 | doctor *Doctor 37 | } 38 | 39 | func (cmd *CommandTreatNose) Treat() { 40 | cmd.doctor.treatNose() 41 | } 42 | 43 | // Nurse 护士-调用命令者 44 | type Nurse struct { 45 | CmdList []Command // 收集的命令集合 46 | } 47 | 48 | // Notify 发送病单,发送命令的方法 49 | func (n *Nurse) Notify() { 50 | if n.CmdList == nil { 51 | return 52 | } 53 | 54 | for _, cmd := range n.CmdList { 55 | cmd.Treat() // 执行病单绑定的命令(这里会调用病单已经绑定的医生的诊断方法) 56 | } 57 | } 58 | 59 | // 病人 60 | func main() { 61 | // 依赖病单,通过填写病单,让医生看病 62 | doctor := new(Doctor) 63 | // 治疗眼睛的病单 64 | cmdEye := CommandTreatEye{doctor} 65 | // 治疗鼻子的病单 66 | cmdNose := CommandTreatNose{doctor} 67 | 68 | // 护士 69 | nurse := new(Nurse) 70 | // 收集管理病单 71 | nurse.CmdList = append(nurse.CmdList, &cmdEye) 72 | nurse.CmdList = append(nurse.CmdList, &cmdNose) 73 | 74 | // 执行病单指令 75 | nurse.Notify() 76 | } 77 | -------------------------------------------------------------------------------- /design-pattern/factory/inter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // ======= 实现层 ========== 6 | var _ ComputerFactory = (*IntelFactory)(nil) 7 | 8 | type IntelFactory struct{} 9 | 10 | func (i *IntelFactory) CreateGPU() GPU { 11 | return new(IntelGPU) 12 | } 13 | func (i *IntelFactory) CreateRAM() RAM { 14 | return new(IntelRAM) 15 | } 16 | func (i *IntelFactory) CreateCPU() CPU { 17 | return new(IntelCPU) 18 | } 19 | 20 | type IntelGPU struct{} 21 | 22 | func (i *IntelGPU) Display() { 23 | fmt.Println("我是intel显卡,有显示功能") 24 | } 25 | 26 | type IntelRAM struct{} 27 | 28 | func (i *IntelRAM) Storage() { 29 | fmt.Println("我是intel内存,有存储功能") 30 | } 31 | 32 | type IntelCPU struct{} 33 | 34 | func (i *IntelCPU) Calculate() { 35 | fmt.Println("我是intel的cpu,有计算功能") 36 | } 37 | -------------------------------------------------------------------------------- /design-pattern/factory/kingston.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var _ ComputerFactory = (*KingstonFactory)(nil) 6 | 7 | type KingstonFactory struct{} 8 | 9 | func (i *KingstonFactory) CreateGPU() GPU { 10 | return new(KingstonGPU) 11 | } 12 | func (i *KingstonFactory) CreateRAM() RAM { 13 | return new(KingstonRAM) 14 | } 15 | func (i *KingstonFactory) CreateCPU() CPU { 16 | return new(KingstonCPU) 17 | } 18 | 19 | type KingstonGPU struct{} 20 | 21 | func (i *KingstonGPU) Display() { 22 | fmt.Println("我是kingston显卡,有显示功能") 23 | } 24 | 25 | type KingstonRAM struct{} 26 | 27 | func (i *KingstonRAM) Storage() { 28 | fmt.Println("我是kingston内存,有存储功能") 29 | } 30 | 31 | type KingstonCPU struct{} 32 | 33 | func (i *KingstonCPU) Calculate() { 34 | fmt.Println("我是kingston的cpu,有计算功能") 35 | } 36 | -------------------------------------------------------------------------------- /design-pattern/factory/mian.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /** 4 | 设计模式:抽象工厂 5 | */ 6 | 7 | // ======抽象层========= 8 | 9 | type ComputerFactory interface { 10 | CreateGPU() GPU 11 | CreateRAM() RAM 12 | CreateCPU() CPU 13 | } 14 | 15 | type GPU interface { 16 | Display() 17 | } 18 | 19 | type RAM interface { 20 | Storage() 21 | } 22 | 23 | type CPU interface { 24 | Calculate() 25 | } 26 | 27 | func main() { 28 | var intelCpt, nvidiaCpt, kingstonCpt ComputerFactory 29 | intelCpt = new(IntelFactory) 30 | nvidiaCpt = new(NvidiaFactory) 31 | kingstonCpt = new(KingstonFactory) 32 | // 组合一: 33 | intelCpt.CreateCPU().Calculate() 34 | intelCpt.CreateGPU().Display() 35 | intelCpt.CreateRAM().Storage() 36 | 37 | // 组合二: 38 | intelCpt.CreateCPU().Calculate() 39 | nvidiaCpt.CreateGPU().Display() 40 | kingstonCpt.CreateRAM().Storage() 41 | } 42 | -------------------------------------------------------------------------------- /design-pattern/factory/nvidia.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var _ ComputerFactory = (*NvidiaFactory)(nil) 6 | 7 | type NvidiaFactory struct{} 8 | 9 | func (i *NvidiaFactory) CreateGPU() GPU { 10 | return new(NvidiaGPU) 11 | } 12 | func (i *NvidiaFactory) CreateRAM() RAM { 13 | return new(NvidiaRAM) 14 | } 15 | func (i *NvidiaFactory) CreateCPU() CPU { 16 | return new(NvidiaCPU) 17 | } 18 | 19 | type NvidiaGPU struct{} 20 | 21 | func (i *NvidiaGPU) Display() { 22 | fmt.Println("我是nvidia显卡,有显示功能") 23 | } 24 | 25 | type NvidiaRAM struct{} 26 | 27 | func (i *NvidiaRAM) Storage() { 28 | fmt.Println("我是nvidia内存,有存储功能") 29 | } 30 | 31 | type NvidiaCPU struct{} 32 | 33 | func (i *NvidiaCPU) Calculate() { 34 | fmt.Println("我是nvidia的cpu,有计算功能") 35 | } 36 | -------------------------------------------------------------------------------- /design-pattern/observer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | 设计模式-观察者模式 5 | */ 6 | 7 | import "fmt" 8 | 9 | /* 10 | 百晓生 11 | [丐帮] [明教] 12 | 洪七公 张无忌 13 | 黄蓉 韦一笑 14 | 乔峰 金毛狮王 15 | */ 16 | 17 | const ( 18 | PGaiBang string = "丐帮" 19 | PMingJiao string = "明教" 20 | ) 21 | 22 | // Listener -------- 抽象层 ------- 23 | type Listener interface { 24 | // OnFriendBeFight 当同伴被揍了该怎么办 25 | OnFriendBeFight(event *Event) 26 | GetName() string 27 | GetParty() string 28 | Title() string 29 | } 30 | 31 | type Notifier interface { 32 | // AddListener 添加观察者 33 | AddListener(listener Listener) 34 | // RemoveListener 删除观察者 35 | RemoveListener(listener Listener) 36 | // Notify 通知广播 37 | Notify(event *Event) 38 | } 39 | 40 | type Event struct { 41 | Noti Notifier // 被知晓的通知者 42 | One Listener // 事件主动发出者 43 | Another Listener // 时间被动接收者 44 | Msg string // 具体消息 45 | } 46 | 47 | // Hero -------- 实现层 ------- 48 | // 英雄(Listener) 49 | type Hero struct { 50 | Name string 51 | Party string 52 | } 53 | 54 | func (hero *Hero) Fight(another Listener, baixiao Notifier) { 55 | msg := fmt.Sprintf("%s 将 %s 揍了...", hero.Title(), another.Title()) 56 | 57 | // 生成事件 58 | event := new(Event) 59 | event.Noti = baixiao 60 | event.One = hero 61 | event.Another = another 62 | event.Msg = msg 63 | 64 | baixiao.Notify(event) 65 | } 66 | 67 | func (hero *Hero) Title() string { 68 | return fmt.Sprintf("[%s]:%s", hero.Party, hero.Name) 69 | } 70 | 71 | func (hero *Hero) OnFriendBeFight(event *Event) { 72 | // 判断是否为当事人 73 | if hero.Name == event.One.GetName() || hero.Name == event.Another.GetName() { 74 | return 75 | } 76 | 77 | // 本帮派同伴将其他门派揍了,要拍手叫好! 78 | if hero.Party == event.One.GetParty() { 79 | fmt.Println(hero.Title(), "得知消息,拍手叫好!!!") 80 | return 81 | } 82 | 83 | // 本帮派同伴被其他门派揍了,要主动报仇反击! 84 | if hero.Party == event.Another.GetParty() { 85 | fmt.Println(hero.Title(), "得知消息,发起报仇反击!!!") 86 | // todo 观察者主动触发事件,会导致死循环 87 | hero.Fight(event.One, event.Noti) 88 | return 89 | } 90 | } 91 | 92 | func (hero *Hero) GetName() string { 93 | return hero.Name 94 | } 95 | 96 | func (hero *Hero) GetParty() string { 97 | return hero.Party 98 | } 99 | 100 | // BaiXiao 百晓生(Nofifier) 101 | type BaiXiao struct { 102 | heroList []Listener 103 | } 104 | 105 | // AddListener 添加观察者 106 | func (b *BaiXiao) AddListener(listener Listener) { 107 | b.heroList = append(b.heroList, listener) 108 | } 109 | 110 | // RemoveListener 删除观察者 111 | func (b *BaiXiao) RemoveListener(listener Listener) { 112 | for index, l := range b.heroList { 113 | // 找到要删除的元素位置 114 | if listener == l { 115 | // 将删除的点前后的元素链接起来 116 | b.heroList = append(b.heroList[:index], b.heroList[index+1:]...) 117 | break 118 | } 119 | } 120 | } 121 | 122 | // Notify 通知广播 123 | func (b *BaiXiao) Notify(event *Event) { 124 | fmt.Println("【世界消息】 百晓生广播消息: ", event.Msg) 125 | for _, listener := range b.heroList { 126 | // 依次调用全部观察的具体动作 127 | listener.OnFriendBeFight(event) 128 | } 129 | } 130 | 131 | func main() { 132 | hero1 := Hero{ 133 | "黄蓉", 134 | PGaiBang, 135 | } 136 | 137 | hero2 := Hero{ 138 | "洪七公", 139 | PGaiBang, 140 | } 141 | 142 | hero3 := Hero{ 143 | "乔峰", 144 | PGaiBang, 145 | } 146 | 147 | hero4 := Hero{ 148 | "张无忌", 149 | PMingJiao, 150 | } 151 | 152 | hero5 := Hero{ 153 | "韦一笑", 154 | PMingJiao, 155 | } 156 | 157 | hero6 := Hero{ 158 | "金毛狮王", 159 | PMingJiao, 160 | } 161 | 162 | baixiao := BaiXiao{} 163 | 164 | baixiao.AddListener(&hero1) 165 | baixiao.AddListener(&hero2) 166 | baixiao.AddListener(&hero3) 167 | baixiao.AddListener(&hero4) 168 | baixiao.AddListener(&hero5) 169 | baixiao.AddListener(&hero6) 170 | 171 | fmt.Println("武林一片平静.....") 172 | hero1.Fight(&hero5, &baixiao) 173 | } 174 | -------------------------------------------------------------------------------- /design-pattern/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | 设计模式:代理模式 7 | 此案例来源某大佬,觉得恰到好处,拿来仅做理解笔记 8 | */ 9 | 10 | // BeautyWoman 抽象主题 11 | type BeautyWoman interface { 12 | // MakeEyesWithMan 对男人抛媚眼 13 | MakeEyesWithMan() 14 | // HappyWithMan 和男人共度美好的时光 15 | HappyWithMan() 16 | } 17 | 18 | // Panjinlian 具体主题 19 | type Panjinlian struct{} 20 | 21 | func (p *Panjinlian) MakeEyesWithMan() { 22 | fmt.Println("潘金莲对本馆抛了个眉眼") 23 | } 24 | 25 | func (p *Panjinlian) HappyWithMan() { 26 | fmt.Println("潘金莲和本馆共度了浪漫的约会。。。") 27 | } 28 | 29 | // WangPo 中间的代理人,王婆 30 | type WangPo struct { 31 | woman BeautyWoman 32 | } 33 | 34 | func NewProxyWangPo(woman BeautyWoman) BeautyWoman { 35 | return &WangPo{woman: woman} 36 | } 37 | 38 | func (w WangPo) MakeEyesWithMan() { 39 | w.woman.MakeEyesWithMan() 40 | } 41 | 42 | func (w WangPo) HappyWithMan() { 43 | w.woman.HappyWithMan() 44 | } 45 | 46 | // 业务逻辑 47 | func main() { 48 | // 西门想找金莲,让王婆来安排 49 | wangpo := NewProxyWangPo(new(Panjinlian)) 50 | // 王婆命令金莲抛媚眼 51 | wangpo.MakeEyesWithMan() 52 | // 王婆命令金莲和西门约会 53 | wangpo.HappyWithMan() 54 | } 55 | -------------------------------------------------------------------------------- /design-pattern/singleton/singleton.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | /** 4 | 设计模式:单例模式 ======= 保证一个类全局永远只能有一个对象,这个对象还能被系统的其他模块使用 5 | */ 6 | 7 | import ( 8 | "sync" 9 | "sync/atomic" 10 | ) 11 | 12 | type singleton struct{} 13 | 14 | // 饿汉式单例,在编译期就会创建对象分配内存 15 | // var instance *singleton = new(singleton) 16 | 17 | // 懒汉式单例,在没有创建时才创建对象分配内存 18 | var instance *singleton 19 | 20 | // 原子操作 21 | var atomicInt32 *int32 22 | 23 | // 定义一个互斥锁 24 | var lock sync.Mutex 25 | 26 | // GetInstance 全局方法获取实例对象 27 | func GetInstance() *singleton { 28 | if atomic.LoadInt32(atomicInt32) == 1 { 29 | return instance 30 | } 31 | lock.Lock() 32 | defer lock.Unlock() 33 | if instance == nil { 34 | instance = new(singleton) 35 | atomic.StoreInt32(atomicInt32, 1) 36 | } 37 | return instance 38 | } 39 | 40 | // ======== go sync库提供的Once实现方式 41 | var once sync.Once 42 | 43 | func GetInstance2() *singleton { 44 | once.Do(func() { 45 | if instance == nil { 46 | instance = new(singleton) 47 | } 48 | }) 49 | return instance 50 | } 51 | -------------------------------------------------------------------------------- /design-pattern/strategy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /* 6 | 设计模式:策略模式 7 | 商场促销有策略A(0.8折)策略B(消费满200,返现100),用策略模式模拟场景 8 | */ 9 | 10 | // SellStrategy 销售策略 11 | type SellStrategy interface { 12 | // GetPrice 根据原价得到售卖价 13 | GetPrice(price float64) float64 14 | } 15 | 16 | type StrategyA struct{} 17 | 18 | func (sa *StrategyA) GetPrice(price float64) float64 { 19 | fmt.Println("执行策略A, 所有商品打八折") 20 | return price * 0.8 21 | } 22 | 23 | type StrategyB struct{} 24 | 25 | func (sb *StrategyB) GetPrice(price float64) float64 { 26 | fmt.Println("执行策略B, 所有商品满200 减100") 27 | 28 | if price >= 200 { 29 | price -= 100 30 | } 31 | 32 | return price 33 | } 34 | 35 | // Goods 环境类 36 | type Goods struct { 37 | Price float64 38 | Strategy SellStrategy 39 | } 40 | 41 | func (g *Goods) SetStrategy(s SellStrategy) { 42 | g.Strategy = s 43 | } 44 | 45 | func (g *Goods) SellPrice() float64 { 46 | fmt.Println("原价值 ", g.Price, " .") 47 | return g.Strategy.GetPrice(g.Price) 48 | } 49 | 50 | func main() { 51 | nike := Goods{ 52 | Price: 200.0, 53 | } 54 | // 上午 ,商场执行策略A 55 | nike.SetStrategy(new(StrategyA)) 56 | fmt.Println("上午nike鞋卖", nike.SellPrice()) 57 | 58 | // 下午, 商场执行策略B 59 | nike.SetStrategy(new(StrategyB)) 60 | fmt.Println("下午nike鞋卖", nike.SellPrice()) 61 | } 62 | -------------------------------------------------------------------------------- /geth/balance/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math" 8 | "math/big" 9 | 10 | "github.com/ethereum/go-ethereum/common" 11 | 12 | "github.com/ethereum/go-ethereum/ethclient" 13 | ) 14 | 15 | func main() { 16 | // infura网关,初始化客户端 17 | client, err := ethclient.Dial("https://cloudflare-eth.com") 18 | if err != nil { 19 | log.Fatalf("infura网关%v", err) 20 | } 21 | fmt.Println(client) 22 | 23 | // 连接本地ganache RPC主机 24 | ganacheClient, err := ethclient.Dial("http://127.0.0.1:7545") 25 | if err != nil { 26 | log.Fatalf("本地ganache%v", err) 27 | } 28 | fmt.Println(ganacheClient) 29 | 30 | // 账户地址转换为common.Address类型 以太坊的账户要么是钱包地址 要么是智能合约地址 31 | address := common.HexToAddress("0x388C818CA8B9251b393131C08a736A67ccB19297") 32 | fmt.Printf("%s\n", address.Hex()) 33 | 34 | // 查询账户余额 35 | // @区块要用bigint, nil则对应最新余额 36 | // @以太坊的数字是使用尽可能小的单位处理的,是定点精度。ETH值 = wei/10^18 37 | balance, err := client.BalanceAt(context.Background(), address, nil) 38 | if err != nil { 39 | log.Fatalf("query balance err %v", err) 40 | } 41 | fmt.Println(balance) 42 | 43 | fBalance := new(big.Float) 44 | fBalance.SetString(balance.String()) 45 | ethVal := new(big.Float).Quo(fBalance, big.NewFloat(math.Pow10(18))) 46 | fmt.Println(ethVal) 47 | 48 | pendingBalance, err := client.PendingBalanceAt(context.Background(), address) 49 | if err != nil { 50 | log.Fatalf("query pending balance err %v", err) 51 | } 52 | fPendingBalance := new(big.Float) 53 | fPendingBalance.SetString(pendingBalance.String()) 54 | pendingEthVal := new(big.Float).Quo(fPendingBalance, big.NewFloat(math.Pow10(18))) 55 | fmt.Println(pendingEthVal) 56 | } 57 | -------------------------------------------------------------------------------- /geth/check-address/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "regexp" 8 | 9 | "github.com/ethereum/go-ethereum/common" 10 | 11 | "github.com/ethereum/go-ethereum/ethclient" 12 | ) 13 | 14 | func main() { 15 | // 校验地址格式 16 | re := regexp.MustCompile("^0x[0-9a-fA-F]{40}$") 17 | fmt.Println(re.MatchString("0x323b5d4c32345ced77393b3530b1eed0f346429d")) 18 | 19 | // 验证是一个智能合约 还是一个标准的以太坊账户 20 | client, err := ethclient.Dial("https://cloudflare-eth.com") 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | codeByte, err := client.CodeAt(context.Background(), common.HexToAddress("0x323b5d4c32345ced77393b3530b1eed0f346429d"), nil) 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | // 大于0存在字节码,则为智能合约;当地址上没有字节码时,则为一个标准的以太坊账户 29 | isContract := len(codeByte) > 0 30 | fmt.Println(isContract) 31 | } 32 | -------------------------------------------------------------------------------- /geth/create-wallet/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/ethereum/go-ethereum/common/hexutil" 9 | "github.com/ethereum/go-ethereum/crypto" 10 | ) 11 | 12 | func main() { 13 | // 生成私钥 14 | privateKey, err := crypto.GenerateKey() 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | privateKeyBytes := crypto.FromECDSA(privateKey) 19 | // 16进制编码 剥离0x = 用于签署交易的私钥,将被视为密码,谁拥有它就可以访问你的所有资产 20 | fmt.Println(hexutil.Encode(privateKeyBytes)[2:]) 21 | 22 | // 派生公钥 23 | publicKey := privateKey.Public() 24 | publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) 25 | if !ok { 26 | log.Fatal("cannot assert type") 27 | } 28 | publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA) 29 | // 16进制编码 剥离0x和前2个字符 = 公钥 30 | fmt.Println(hexutil.Encode(publicKeyBytes)[4:]) 31 | 32 | // 生成公共地址 33 | address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() 34 | println(address) 35 | } 36 | -------------------------------------------------------------------------------- /geth/keystore/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/ethereum/go-ethereum/accounts/keystore" 9 | ) 10 | 11 | func main() { 12 | // keystore 是一个包含了经过加密了的钱包私钥,每个文件只能包含一个钱包私钥对 13 | createKs() 14 | //importKs() 15 | } 16 | 17 | func createKs() { 18 | ks := keystore.NewKeyStore("./geth/keystore/tmp", keystore.StandardScryptN, keystore.StandardScryptP) 19 | password := "123456" 20 | // 创建新的钱包 21 | account, err := ks.NewAccount(password) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Println(account.Address.Hex()) 26 | } 27 | 28 | func importKs() { 29 | file := "./geth/keystore/tmp/UTC--2024-04-17T09-37-33.337636000Z--91884f85b457a89e18e5fd38a1cadffaeaa430e2" 30 | ks := keystore.NewKeyStore("./geth/keystore/tmp/import", keystore.StandardScryptN, keystore.StandardScryptP) 31 | jsonBytes, err := os.ReadFile(file) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | password := "123456" 36 | newPassword := "123123" 37 | account, err := ks.Import(jsonBytes, password, newPassword) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | fmt.Println(account.Address.Hex()) 42 | if err := os.Remove(file); err != nil { 43 | log.Fatal(err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /geth/keystore/tmp/UTC--2024-04-17T09-49-17.022953000Z--ac31ced7ffc7be4d92ec494b0ec3966848e791b3: -------------------------------------------------------------------------------- 1 | {"address":"ac31ced7ffc7be4d92ec494b0ec3966848e791b3","crypto":{"cipher":"aes-128-ctr","ciphertext":"fcfa5650bfe40e12d04073b658ca57d17aeee6b82d071d3de70b4b524f84c74b","cipherparams":{"iv":"122040323605bc2e3af305a94f87d78c"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"46e46807cb1b5187fbf9cd1f2941877231af000250bfd1d24407e63f33e6c473"},"mac":"7fe83c39a7f11bc4908faa4edf7e686fd31e281cf8d0a329b45852e61e250f4c"},"id":"48ead0ea-5b19-4f02-890f-6a7106e33d6b","version":3} -------------------------------------------------------------------------------- /geth/listen-block/event_listener.go: -------------------------------------------------------------------------------- 1 | package listen_block 2 | 3 | import ( 4 | "context" 5 | "math/big" 6 | "time" 7 | 8 | "go.uber.org/zap" 9 | 10 | "github.com/ethereum/go-ethereum/accounts/abi" 11 | "github.com/ethereum/go-ethereum/common" 12 | "github.com/ethereum/go-ethereum/ethclient" 13 | ) 14 | 15 | type ContractListener interface { 16 | Listen() 17 | } 18 | 19 | func NewContract(client *ethclient.Client, address common.Address, abi abi.ABI) ContractListener { 20 | return &contract{ 21 | ETHClient: client, 22 | Address: address, 23 | ABI: abi, 24 | } 25 | } 26 | 27 | type contract struct { 28 | ETHClient *ethclient.Client 29 | Address common.Address // 合约地址 30 | ABI abi.ABI // 合约对应的abi 31 | lastScannedBlock uint64 // 上次扫描的最新区块号 32 | } 33 | 34 | func (c *contract) Listen() { 35 | // 获取当前最新的区块号 36 | latestBlockNum, err := c.ETHClient.BlockNumber(context.Background()) 37 | if err != nil { 38 | zap.L().Fatal("获取最新区块号失败", zap.Error(err)) 39 | } 40 | c.lastScannedBlock = latestBlockNum 41 | // 定时扫描新的区块 42 | ticker := time.NewTicker(10 * time.Second) 43 | defer ticker.Stop() 44 | for { 45 | select { 46 | case <-ticker.C: 47 | zap.L().Info("开始扫描区块") 48 | ctx := context.Background() 49 | // 获取当前最新的区块号 50 | latestBlock, err := c.ETHClient.BlockNumber(ctx) 51 | if err != nil { 52 | zap.L().Error("获取最新区块号失败", zap.Error(err)) 53 | continue 54 | } 55 | // 扫描从上次扫描以来的新区块 56 | for blockNumber := c.lastScannedBlock + 1; blockNumber <= latestBlock; blockNumber++ { 57 | // 根据区块号获取区块数据 58 | block, err := c.ETHClient.BlockByNumber(ctx, new(big.Int).SetUint64(blockNumber)) 59 | if err != nil { 60 | zap.L().Error("获取区块失败", zap.Error(err), zap.Uint64("blockNumber", blockNumber)) 61 | continue 62 | } 63 | // 处理区块中的transaction 64 | for _, transaction := range block.Transactions() { 65 | // 获取transaction收据 66 | receipt, err := c.ETHClient.TransactionReceipt(ctx, transaction.Hash()) 67 | if err != nil { 68 | zap.L().Error("获取transaction收据失败", zap.Error(err), 69 | zap.Uint64("blockNumber", blockNumber), zap.Any("transaction", transaction)) 70 | continue 71 | } 72 | // 解析log 73 | for _, log := range receipt.Logs { 74 | event := struct{}{} // 对应事件的结构体 75 | if err := c.ABI.UnpackIntoInterface(&event, "ERC20Recovered", log.Data); err != nil { 76 | e, _ := log.MarshalJSON() 77 | zap.L().Error("事件解析失败", zap.Error(err), zap.String("e", string(e))) 78 | continue 79 | } 80 | } 81 | } 82 | } 83 | zap.L().Info("等待下一次扫描") 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /go-reflect/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | /** 9 | 基础知识: 10 | 1.获取值使用 ValueOf 11 | 2.获取元素使用 Elem 12 | 3.判断类型使用 Kind 或者 Type,如果类型使用 type 定义,Kind 返回的是底层类型 13 | 4.设置值使用 Set 14 | 5.如果想修改值,必须为指针类型 15 | 6.如果想要获取结构体信息或者类型,可以使用 TypeOf 16 | */ 17 | 18 | type People interface { 19 | Add(a, b int) int 20 | } 21 | 22 | type CustomInt int 23 | 24 | type User struct { 25 | Name interface{} `json:"name"` 26 | Age CustomInt `json:"age"` 27 | } 28 | 29 | func (u User) GetName() string { 30 | return u.Name.(string) 31 | } 32 | 33 | func (u *User) GetAge() int { 34 | return int(u.Age) 35 | } 36 | 37 | func (u *User) Add(a, b int) int { 38 | return a + b 39 | } 40 | 41 | type Dog struct{} 42 | 43 | func main() { 44 | user := User{Name: "张三", Age: 20} 45 | // 获取的是指针地址的反射对象 46 | uPtr := reflect.ValueOf(&user) 47 | fmt.Println(uPtr.Type()) 48 | // Elem 获取地址(指针)所指向的元素 49 | u := uPtr.Elem() 50 | fmt.Println(u.Type()) 51 | // 获取字段数量 52 | numField := u.NumField() 53 | fmt.Println(numField) 54 | // 获取字段 55 | name := u.FieldByName("Name") 56 | age := u.FieldByName("Age") 57 | fmt.Println(name, age) 58 | // 判断字段类型 59 | if name.Kind() == reflect.Interface { 60 | // name是interface类型,因此还需要 Elem 才能获取值的反射对象 61 | if name.Elem().Kind() == reflect.String { 62 | name.Set(reflect.ValueOf("李四")) 63 | } 64 | if name.Elem().Kind() == reflect.Int { 65 | name.Set(reflect.ValueOf(88)) 66 | } 67 | } 68 | fmt.Println(user) 69 | fmt.Println("--------获取字段相关的信息:名字、类型、tag----------") 70 | uType := reflect.TypeOf(user) 71 | for i := 0; i < uType.NumField(); i++ { 72 | field := uType.Field(i) 73 | fmt.Println("名字", field.Name) 74 | fmt.Println("类型", field.Type) 75 | fmt.Println("底层类型", field.Type.Kind()) 76 | fmt.Println("tag值", field.Tag) 77 | fmt.Println("json-tag的值", field.Tag.Get("json")) 78 | fmt.Println("----------------") 79 | } 80 | fmt.Println("--------- 获取方法与函数信息-------") 81 | userPtr := &User{ 82 | Name: "测试", 83 | Age: 10, 84 | } 85 | methodTypeOf := reflect.TypeOf(userPtr.Add) 86 | for i := 0; i < methodTypeOf.NumIn(); i++ { 87 | fmt.Printf("入参%d 类型:%s\n", i+1, methodTypeOf.In(i).Kind()) 88 | } 89 | for i := 0; i < methodTypeOf.NumOut(); i++ { 90 | fmt.Printf("出参%d 类型:%s\n", i+1, methodTypeOf.Out(i).Kind()) 91 | } 92 | fmt.Println("---------判断是否实现了接口-------") 93 | // 首先我们需要获取到接口的类型 94 | peopleType := reflect.TypeOf((*People)(nil)).Elem() 95 | fmt.Println("People是否是一个接口:", peopleType.Kind() == reflect.Interface) 96 | // 判断User和Dog是否实现了People接口 97 | noPtrUser := reflect.TypeOf(User{}) 98 | ptrUser := reflect.TypeOf(&User{}) 99 | noPtrDog := reflect.TypeOf(Dog{}) 100 | ptrDog := reflect.TypeOf(&Dog{}) 101 | fmt.Println("noPtrUser是否实现了接口", noPtrUser.Implements(peopleType)) 102 | fmt.Println("ptrUser是否实现了接口", ptrUser.Implements(peopleType)) 103 | fmt.Println("noPtrDog是否实现了接口", noPtrDog.Implements(peopleType)) 104 | fmt.Println("ptrDog是否实现了接口", ptrDog.Implements(peopleType)) 105 | fmt.Println("---------指针对象与非指针对象互转-------") 106 | uu := User{Name: "小名"} 107 | // 指针value 转 非指针value 108 | point := reflect.ValueOf(&uu) 109 | noPoint := point.Elem() 110 | fmt.Printf("point type: %s kind:%s\n", point.Type(), point.Kind()) 111 | fmt.Printf("noPoint type: %s kind:%s\n", noPoint.Type(), noPoint.Kind()) 112 | // 把 reflect.value 转为 User 113 | uu = noPoint.Interface().(User) 114 | // ------------接口没有指向具体的值 115 | var i interface{} 116 | v := reflect.ValueOf(i) 117 | fmt.Println(v.Kind() == reflect.Invalid) // 输出invalid true 118 | fmt.Println(v.IsValid()) // false 119 | // -----------value指向一个nil 120 | var uuu *User = nil 121 | vv := reflect.ValueOf(uuu) 122 | if vv.IsValid() { 123 | // 调用IsNil()前先确保IsValid(),否则panic,因为nil.call这种是不允许的,会空指针异常 124 | fmt.Printf("vv持有的值是nil %t\n", vv.IsNil()) 125 | } 126 | // ------------只声明,里面值都是0值 127 | var uuuu User 128 | vvv := reflect.ValueOf(uuuu) 129 | if vvv.IsValid() { 130 | // 调用IsNil()前先确保IsValid(),否则panic,因为nil.call这种是不允许的,会空指针异常 131 | fmt.Printf("vvv持有的值是对应类型的0值 %t\n", vv.IsZero()) 132 | } 133 | // CanSet 是否可以设置值 134 | cs := User{} 135 | csPtr := reflect.ValueOf(&cs).Elem() 136 | if csPtr.CanSet() { 137 | csPtr.FieldByName("Age").SetInt(10) 138 | fmt.Println(cs) 139 | } else { 140 | fmt.Println("不允许设置值") 141 | } 142 | // 反射创建结构体 143 | ut := reflect.TypeOf(User{}) 144 | uv := reflect.New(ut) // valueOf(&User{}) 145 | uv.Elem().FieldByName("Age").SetInt(20) 146 | // 反射创建切片 147 | sliceType := reflect.TypeOf([]User{}) 148 | sliceValue := reflect.MakeSlice(sliceType, 1, 3) 149 | sliceValue.Index(0).FieldByName("Age").SetInt(30) 150 | fmt.Println(sliceValue) 151 | // 反射修改切片 152 | users := make([]*User, 1, 3) 153 | users[0] = &User{Age: 11} 154 | usersValue := reflect.ValueOf(&users) 155 | usersValue.Elem().Index(0).Set(reflect.ValueOf(&User{Age: 111})) 156 | // 修改切片长度 157 | usersValue.Elem().SetLen(2) 158 | // 反射切片append 159 | usersValue = reflect.Append(usersValue.Elem(), reflect.ValueOf(&User{Age: 222})) 160 | fmt.Println(usersValue.Interface().([]*User)) 161 | } 162 | -------------------------------------------------------------------------------- /go-rpc-custom/client.go: -------------------------------------------------------------------------------- 1 | package go_rpc_custom 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "reflect" 7 | ) 8 | 9 | type Client struct { 10 | conn net.Conn 11 | } 12 | 13 | func NewClient(conn net.Conn) *Client { 14 | return &Client{conn: conn} 15 | } 16 | 17 | // Call 实现通用的RPC客户端 18 | // 函数具体实现在server端,client只有函数原型 19 | // @param name 函数名 20 | // @param fPtr 指向函数原型 21 | // xxx.Call("query", &query) 22 | func (c *Client) Call(name string, fPtr interface{}) { 23 | // 通过反射获取fPtr未初始化的函数原型,获取fPtr指向的值的反射对象 24 | fn := reflect.ValueOf(fPtr).Elem() 25 | // 定义一个函数 26 | f := func(args []reflect.Value) []reflect.Value { 27 | // 处理请求参数 28 | reqArgs := make([]interface{}, 0, len(args)) 29 | for _, item := range args { 30 | reqArgs = append(reqArgs, item.Interface()) 31 | } 32 | // 编码 33 | reqData, err := GobEncode(Param{Name: name, Args: reqArgs}) 34 | if err != nil { 35 | panic(err) 36 | } 37 | // 写入请求数据 38 | connSession := NewSession(c.conn) 39 | if err := connSession.Write(reqData); err != nil { 40 | panic(err) 41 | } 42 | // 读取响应数据 43 | respBytes, err := connSession.Read() 44 | if err != nil { 45 | panic(err) 46 | } 47 | // 解码 48 | respData, err := GobDecode(respBytes) 49 | if err != nil { 50 | panic(err) 51 | } 52 | respArgs := make([]reflect.Value, 0, len(respData.Args)) 53 | for i, arg := range respData.Args { 54 | // 必须进行nil转换,当值为nil,反射对象的类型是reflect.Value,但其有效性是 Invalid ,在使用某些时可能会产生panic 55 | if arg == nil { 56 | respArgs = append(respArgs, reflect.Zero(fn.Type().Out(i))) 57 | continue 58 | } 59 | respArgs = append(respArgs, reflect.ValueOf(arg)) 60 | } 61 | return respArgs 62 | } 63 | // 输出函数类型信息 64 | fmt.Println("Function Name:", fn) 65 | fmt.Println("Function Type:", fn.Type()) 66 | // 输出函数参数和返回值类型信息 67 | fmt.Println("Number of Input Parameters:", fn.Type().NumIn()) 68 | for i := 0; i < fn.Type().NumIn(); i++ { 69 | fmt.Printf("Parameter %d Type: %v\n", i+1, fn.Type().In(i)) 70 | } 71 | fmt.Println("Number of Output Parameters:", fn.Type().NumOut()) 72 | for i := 0; i < fn.Type().NumOut(); i++ { 73 | fmt.Printf("Return Value %d Type: %v\n", i+1, fn.Type().Out(i)) 74 | } 75 | // 参数1:一个函数类型,类型是reflect.Type 76 | // 参数2:一个函数,作用是对第一个函数参数操作,并返回结果,为reflect.value类型 77 | // MakeFunc 根据指定的函数类型创建一个新的函数,该函数的行为由参数2指定 78 | v := reflect.MakeFunc(fn.Type(), f) 79 | // 为函数fPtr赋值 80 | fn.Set(v) 81 | } 82 | -------------------------------------------------------------------------------- /go-rpc-custom/param.go: -------------------------------------------------------------------------------- 1 | package go_rpc_custom 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | ) 7 | 8 | // Param 数据结构体 9 | type Param struct { 10 | Name string // 方法名 11 | Args []interface{} // 参数 12 | } 13 | 14 | // GobEncode gob序列化 15 | func GobEncode(data Param) ([]byte, error) { 16 | // 创建一个字节缓冲区 17 | buf := bytes.Buffer{} 18 | // 创建一个Gob编码器,并将数据序列化到字节缓冲区中 19 | enc := gob.NewEncoder(&buf) 20 | if err := enc.Encode(data); err != nil { 21 | return nil, err 22 | } 23 | return buf.Bytes(), nil 24 | } 25 | 26 | // GobDecode gob反序列化 27 | func GobDecode(data []byte) (Param, error) { 28 | result := Param{} 29 | buf := bytes.NewBuffer(data) 30 | // 创建一个Gob解码器,并从字节缓冲区中反序列化一个 Param 类型的值 31 | dec := gob.NewDecoder(buf) 32 | if err := dec.Decode(&result); err != nil { 33 | return Param{}, err 34 | } 35 | return result, nil 36 | } 37 | -------------------------------------------------------------------------------- /go-rpc-custom/rpc_test.go: -------------------------------------------------------------------------------- 1 | package go_rpc_custom 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "net" 7 | "testing" 8 | ) 9 | 10 | type User struct { 11 | Name string 12 | Age int 13 | } 14 | 15 | type MyErr struct { 16 | Msg string 17 | } 18 | 19 | func (m MyErr) Error() string { 20 | return m.Msg 21 | } 22 | 23 | func queryUser(uid int) (User, error) { 24 | users := make(map[int]User) 25 | users[0] = User{Name: "ls", Age: 20} 26 | users[1] = User{Name: "zs", Age: 21} 27 | if val, ok := users[uid]; ok { 28 | return val, nil 29 | } 30 | return User{}, MyErr{Msg: "用户不存在"} 31 | } 32 | 33 | func TestRPC(t *testing.T) { 34 | // 需要对interface{}可能产生的类型进行注册 35 | gob.Register(User{}) 36 | gob.Register(MyErr{}) 37 | addr := "127.0.0.1:8080" 38 | // 创建服务器 39 | svr := NewServer(addr) 40 | svr.Register("queryUser", queryUser) 41 | go svr.Run() 42 | // 客户端获取连接 43 | conn, err := net.Dial("tcp", addr) 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | // 创建客户端 48 | cli := NewClient(conn) 49 | // 声明函数原型 50 | var query func(int) (User, error) 51 | cli.Call("queryUser", &query) 52 | // 得到查询结果 53 | u, err := query(3) 54 | if err != nil { 55 | t.Fatal(err) 56 | } 57 | fmt.Println(u) 58 | } 59 | -------------------------------------------------------------------------------- /go-rpc-custom/server.go: -------------------------------------------------------------------------------- 1 | package go_rpc_custom 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "reflect" 7 | ) 8 | 9 | type Server struct { 10 | // 地址 11 | addr string 12 | // 服务端维护的函数名与函数反射对象的map 13 | funcs map[string]reflect.Value 14 | } 15 | 16 | // NewServer 创建服务端对象 17 | func NewServer(addr string) *Server { 18 | return &Server{ 19 | addr: addr, 20 | funcs: make(map[string]reflect.Value), 21 | } 22 | } 23 | 24 | // Register 服务端绑定注册方法 25 | // @param fName 函数名, 26 | // @param f 真正的函数 27 | func (s *Server) Register(fName string, f interface{}) { 28 | if _, ok := s.funcs[fName]; ok { 29 | return 30 | } 31 | // map中没有值,则将函数的反射对象添加到map 32 | fVal := reflect.ValueOf(f) 33 | s.funcs[fName] = fVal 34 | } 35 | 36 | // Run 服务端等待调用 37 | func (s *Server) Run() { 38 | // 监听 39 | listener, err := net.Listen("tcp", s.addr) 40 | if err != nil { 41 | fmt.Printf("监听 %s err:%v", s.addr, err) 42 | return 43 | } 44 | for { 45 | // 接收连接请求 46 | conn, err := listener.Accept() 47 | if err != nil { 48 | fmt.Printf("accept err:%v", err) 49 | return 50 | } 51 | // 创建会话 52 | connSession := NewSession(conn) 53 | // 读取连接请求数据 54 | reqByteData, err := connSession.Read() 55 | if err != nil { 56 | fmt.Printf("read err:%v", err) 57 | return 58 | } 59 | // 对数据解码 60 | reqData, err := GobDecode(reqByteData) 61 | if err != nil { 62 | fmt.Printf("decode err:%v", err) 63 | return 64 | } 65 | // 根据函数名Name获取函数反射值 66 | f, ok := s.funcs[reqData.Name] 67 | if !ok { 68 | fmt.Printf("函数 %s 不存在", reqData.Name) 69 | return 70 | } 71 | // 解析遍历客户端的参数,放到一个数组中 72 | reqArgs := make([]reflect.Value, 0, len(reqData.Args)) 73 | for _, arg := range reqData.Args { 74 | reqArgs = append(reqArgs, reflect.ValueOf(arg)) 75 | } 76 | // 反射调用方法,传入参数 77 | result := f.Call(reqArgs) 78 | // 解析遍历执行结果,放到一个数组中 79 | respArgs := make([]interface{}, 0, len(result)) 80 | for _, item := range result { 81 | respArgs = append(respArgs, item.Interface()) 82 | } 83 | // 包装数据,返回给客户端 84 | respData := Param{reqData.Name, respArgs} 85 | // 编码 86 | respBytes, err := GobEncode(respData) 87 | if err != nil { 88 | fmt.Printf("encode err:%v", err) 89 | return 90 | } 91 | // 返回连接响应数据 92 | err = connSession.Write(respBytes) 93 | if err != nil { 94 | fmt.Printf("session write err:%v", err) 95 | return 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /go-rpc-custom/session.go: -------------------------------------------------------------------------------- 1 | package go_rpc_custom 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | "net" 7 | ) 8 | 9 | // 自定义协议 header 4字节 + data []byte 10 | 11 | // Session 会话连接的结构体 12 | type Session struct { 13 | conn net.Conn 14 | } 15 | 16 | // NewSession 创建连接 17 | func NewSession(conn net.Conn) *Session { 18 | return &Session{conn: conn} 19 | } 20 | 21 | // 向连接中写数据 22 | func (s *Session) Write(data []byte) error { 23 | // header 4字节表示数据长度 + 数据长度的切片 24 | buf := make([]byte, 4+len(data)) 25 | // 写入头部数据,记录数据长度 26 | binary.BigEndian.PutUint32(buf[:4], uint32(len(data))) 27 | // 写入数据 28 | copy(buf[4:], data) 29 | _, err := s.conn.Write(buf) 30 | if err != nil { 31 | return err 32 | } 33 | return nil 34 | } 35 | 36 | // 从连接中读取数据 37 | func (s *Session) Read() ([]byte, error) { 38 | // 读取头部长度 39 | header := make([]byte, 4) 40 | // 按头部长度 读取头部数据 41 | _, err := io.ReadFull(s.conn, header) 42 | if err != nil { 43 | return nil, err 44 | } 45 | // 读取数据长度,利用了ReadFull不会读取流已经被读取的部分,是从当前位置开始读取指定长度数据 46 | dataLen := binary.BigEndian.Uint32(header) 47 | // 按照数据长度去读取数据 48 | data := make([]byte, dataLen) 49 | _, err = io.ReadFull(s.conn, data) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return data, nil 54 | } 55 | -------------------------------------------------------------------------------- /go-rpc-custom/session_test.go: -------------------------------------------------------------------------------- 1 | package go_rpc_custom 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | "testing" 8 | ) 9 | 10 | func TestSession_ReadWrite(t *testing.T) { 11 | // 定义监听IP和端口 12 | addr := "127.0.0.1:8080" 13 | // 定义传输的数据 14 | data := "hello word" 15 | notice := make(chan struct{}) 16 | wg := sync.WaitGroup{} 17 | wg.Add(2) 18 | // 一个协程写入数据 19 | go func() { 20 | defer wg.Done() 21 | defer close(notice) 22 | // 创建tcp连接 23 | listener, err := net.Listen("tcp", addr) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | // 通知 读取方 连接已开始监听(避免还未完全启动并监听端口,连接操作就进来了) 28 | notice <- struct{}{} 29 | // 如果没有连接到达,Accept()方法会一直阻塞直到有新的连接到达或者发生错误 30 | conn, err := listener.Accept() 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | // 写数据 35 | s := NewSession(conn) 36 | err = s.Write([]byte(data)) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | }() 41 | // 一个协程读取数据 42 | go func() { 43 | defer wg.Done() 44 | <-notice 45 | // 与指定服务建立连接,并进行通信 46 | conn, err := net.Dial("tcp", addr) 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | // 读取数据 51 | s := NewSession(conn) 52 | result, err := s.Read() 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | fmt.Println(string(result)) 57 | }() 58 | wg.Wait() 59 | } 60 | -------------------------------------------------------------------------------- /go-rpc/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/rpc" 7 | ) 8 | 9 | type Params struct { 10 | Length, Width int 11 | } 12 | 13 | func main() { 14 | result := 0 15 | cl, err := rpc.DialHTTP("tcp", "127.0.0.1:8080") 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | if err = cl.Call("Rectangle.Area", Params{ 20 | Length: 100, 21 | Width: 20, 22 | }, &result); err != nil { 23 | log.Fatal(err) 24 | } 25 | fmt.Println("面积是", result) 26 | if err = cl.Call("Rectangle.Perimeter", Params{ 27 | Length: 10, 28 | Width: 20, 29 | }, &result); err != nil { 30 | log.Fatal(err) 31 | } 32 | fmt.Println("周长是", result) 33 | } 34 | -------------------------------------------------------------------------------- /go-rpc/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "net/rpc" 7 | ) 8 | 9 | type Rectangle struct { 10 | } 11 | 12 | type Params struct { 13 | Length, Width int 14 | } 15 | 16 | func (r Rectangle) Area(params Params, result *int) error { 17 | *result = params.Width * params.Length 18 | return nil 19 | } 20 | 21 | func (r Rectangle) Perimeter(params Params, result *int) error { 22 | *result = (params.Width + params.Length) * 2 23 | return nil 24 | } 25 | 26 | func main() { 27 | rectangle := new(Rectangle) 28 | if err := rpc.Register(rectangle); err != nil { 29 | log.Fatal(err) 30 | } 31 | rpc.HandleHTTP() 32 | http.ListenAndServe(":8080", nil) 33 | } 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-interview 2 | 3 | go 1.22.2 4 | 5 | require github.com/ethereum/go-ethereum v1.13.14 6 | 7 | require ( 8 | github.com/Microsoft/go-winio v0.6.1 // indirect 9 | github.com/StackExchange/wmi v1.2.1 // indirect 10 | github.com/bits-and-blooms/bitset v1.10.0 // indirect 11 | github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect 12 | github.com/consensys/bavard v0.1.13 // indirect 13 | github.com/consensys/gnark-crypto v0.12.1 // indirect 14 | github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect 15 | github.com/deckarep/golang-set/v2 v2.1.0 // indirect 16 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect 17 | github.com/ethereum/c-kzg-4844 v0.4.0 // indirect 18 | github.com/fsnotify/fsnotify v1.6.0 // indirect 19 | github.com/go-ole/go-ole v1.3.0 // indirect 20 | github.com/google/uuid v1.3.0 // indirect 21 | github.com/gorilla/websocket v1.4.2 // indirect 22 | github.com/holiman/uint256 v1.2.4 // indirect 23 | github.com/mmcloughlin/addchain v0.4.0 // indirect 24 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect 25 | github.com/supranational/blst v0.3.11 // indirect 26 | github.com/tklauser/go-sysconf v0.3.12 // indirect 27 | github.com/tklauser/numcpus v0.6.1 // indirect 28 | go.uber.org/multierr v1.10.0 // indirect 29 | go.uber.org/zap v1.27.0 // indirect 30 | golang.org/x/crypto v0.17.0 // indirect 31 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect 32 | golang.org/x/mod v0.14.0 // indirect 33 | golang.org/x/sync v0.5.0 // indirect 34 | golang.org/x/sys v0.16.0 // indirect 35 | golang.org/x/tools v0.15.0 // indirect 36 | rsc.io/tmplfunc v0.0.3 // indirect 37 | ) 38 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= 2 | github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= 3 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 4 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 5 | github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= 6 | github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= 7 | github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= 8 | github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= 9 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 | github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= 12 | github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 13 | github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= 14 | github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= 15 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= 16 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 17 | github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= 18 | github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= 19 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 20 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 21 | github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= 22 | github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= 23 | github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= 24 | github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= 25 | github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= 26 | github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= 27 | github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= 28 | github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= 29 | github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= 30 | github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= 31 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= 32 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= 33 | github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= 34 | github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= 35 | github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= 36 | github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= 37 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 38 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 39 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= 40 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= 41 | github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= 42 | github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= 43 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 44 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 45 | github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= 46 | github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= 47 | github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= 48 | github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= 49 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= 50 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= 51 | github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= 52 | github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= 53 | github.com/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ= 54 | github.com/ethereum/go-ethereum v1.13.14/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= 55 | github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= 56 | github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= 57 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 58 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 59 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= 60 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= 61 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= 62 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= 63 | github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 64 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 65 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 66 | github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= 67 | github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 68 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 69 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 70 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 71 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 72 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 73 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 74 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= 75 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 76 | github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 77 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 78 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 79 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 80 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 81 | github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= 82 | github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= 83 | github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= 84 | github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= 85 | github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= 86 | github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= 87 | github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= 88 | github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= 89 | github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= 90 | github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= 91 | github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= 92 | github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= 93 | github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= 94 | github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= 95 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 96 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 97 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 98 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 99 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 100 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 101 | github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= 102 | github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= 103 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 104 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 105 | github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= 106 | github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 107 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 108 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 109 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= 110 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 111 | github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= 112 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 113 | github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= 114 | github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= 115 | github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= 116 | github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= 117 | github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= 118 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 119 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 120 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 121 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 122 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 123 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 124 | github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= 125 | github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 126 | github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= 127 | github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 128 | github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= 129 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 130 | github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= 131 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 132 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 133 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 134 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 135 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 136 | github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= 137 | github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 138 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 139 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 140 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= 141 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 142 | github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= 143 | github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= 144 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 145 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 146 | github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= 147 | github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 148 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= 149 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= 150 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 151 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 152 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 153 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 154 | github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= 155 | github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= 156 | github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= 157 | github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= 158 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 159 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 160 | go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= 161 | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 162 | go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= 163 | go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= 164 | golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= 165 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 166 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= 167 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= 168 | golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= 169 | golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 170 | golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= 171 | golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 172 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 173 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 174 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 175 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 176 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 177 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 178 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 179 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 180 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 181 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 182 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 183 | golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= 184 | golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= 185 | google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= 186 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 187 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 188 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 189 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 190 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 191 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 192 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 193 | rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= 194 | rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= 195 | -------------------------------------------------------------------------------- /state-machine/machine.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /state-machine/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | --------------------------------------------------------------------------------