接口型函数
最近在写极客兔兔的GeeCache这个小项目,在里面遇到了一段不太理解的代码, 1234567891011type Getter interface { Get(key string) ([]byte, error)}// A GetterFunc implements Getter with a function.type GetterFunc func(key string) ([]byte, error)// Get implements Getter interface functionfunc (f GetterFunc) Get(key string) ([]byte, error) { return f(key)} 结合兔兔的讲解,写一篇博客 本质是实现多态 接口只有一个方法,为什么不直接使用GetterFunc作为参数传进参数? 12func GetFromSource(fn GetterFunc, key string)...
延迟双删
sbCSDN,教的东西是错的,我对着绕了好一会儿 旁路缓存 流程: 写策略:先更新数据库里的数据,再删除缓存 读策略:如果命中缓存,直接返回数据,没有命中,则从数据库中读取,然后将数据写入缓存,再返回数据 缺点:存在极低概率的并发问题(读操作比写操作慢得多时,可能导致旧数据覆盖),且严重依赖“删除缓存”这一步的成功 延迟双删 流程:删除缓存->更新数据库->休眠一会儿->再次删除数据 第二次删是解决在数据库更新中,其他线程读取到旧数据并写入缓存的问题,中间需要延迟的原因是等待读取了旧数据的线程把缓存写入 既然数据库在更新过程中,其他线程读取的是旧数据,最后都需要第二次删除,那我为什么不留着缓存,这样还能减少数据库的压力 第一次删除实际是把读旧缓存变成了 读旧数据库 +...
Go语言圣经-第七章-接口
动态类型与动态值 动态类型是接口值里的具体类型信息,动态值是具体类型的值 接口值的比较 当且仅当两个接口都是nil,或者他们的动态类型相同并且动态值也根据这个动态类型的==操作符相等。 因为接口值是可以比较的,所以它可以作为map键或做为switch语句的操作数 如果两个接口值动态类型相同,但是这个动态类型是不可比较的(Slice)那么就会比较失败并panic 一个包含nil指针的接口不等于nil接口 包含nil指针的接口,里面的nil指针有具体的类型,只是值是nil,所以这个接口实际不是nil,而nil接口里面什么都没有 类型断言 x.(T):判断这个变量x是不是T类型 断言的是具体类型(int, string …) 如果成功,可以调用这个具体类型的所有方法和属性 失败会panic 12345var w io.Writerw = os.Stdoutf := w.(*os.File)//断言成功,里面确实是*os.File类型,可以调用Close(), Name()等io.Writer接口没有的方法c :=...
Go语言圣经第六章-方法
基于指针对象的方法 不管你的method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型进行调用的,编译器会帮你做类型转换。 在声明一个method的receiver该是指针还是非指针类型时,你需要考虑两方面的因素,第一方面是这个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝;第二方面是如果你用指针类型作为receiver,那么你一定要注意,这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝
Go语言圣经第五章-函数
strings.Map 定义:func Map(mapping func(rune) rune, s string) string 作用: 对字符串的每一个字符进行修改,替换或删除,生成一个新的字符串 主要作用取决于mapping这个函数的返回值,如果返回一个新的合法字符,那么就会替换,如果返回原来的字符就不变,如果返回-1就删除这个字符 为什么要使用strings.Map?自己使用for循环不也能做到这些吗 可以处理UTF-8字符,go的字符串的底层是[]byte,如果直接使用循环遍历,处理中文会乱码。strings.Map会自动把字符串解码为Runes,适合处理中文等 更高效,strings.Map内部使用了strings.Builder,相比于循环拼接时更高效 使用函数值,我们可以将遍历结点的逻辑和操作结点的逻辑分离,使得我们可以复用遍历的逻辑,从而对结点进行不同的操作。 匿名函数 函数squares返回另一个类型为 func() int...
Go语言圣经第四章-符合数据类型
4.1 数组 当调用一个函数的时候,函数的每个调用参数将会被赋值给函数内部的参数变量,所以函数参数变量接收的是一个复制的副本,并不是原始调用的变量。 因为函数参数传递的机制导致传递大的数组类型将是低效的,并且对数组参数的任何的修改都是发生在复制的数组上,并不能直接修改调用时原始的数组变量。在这个方面,Go语言对待数组的方式和其它很多编程语言不同,其它编程语言可能会隐式地将数组作为引用或指针对象传入被调用的函数 4.2 Slice 数组与Slice的差异 外观:方括号里面有没有东西 数组必须定长,必须告诉编译器有多大,或者让编译器自己去数 比如:[3]int{1,2,3},[...]int{1,2,3,4} 只要方括号里面有‘东西’,就是数组 方括号里面是空的,就是切片 切片:隐式创建数组 当创建一个切片时,s := []int{1, 2,...
Go语言圣经第三章-基础数据类型
3.1 整型 ^操作符在go中有两个作用: 作为两元操作符时:x ^ y,按位异或,不同为1,相同为0 作为前置应让运算符:^y,按位取反 &^: z= x &^ y, 如果y在某个位置是1,就把x对应位置的数归零,如果y在某个位置是0,x对应的数字不变 1fmt.Printf("%d %[1]o %#[1]o\n", o) %[1]o: 以八进制打印第一个参数,[1]的意思是读取第一个参数,好处是不用多次传同一个参数 #: 对于对应进制会在前面强制补零 字符使用%c打印,带单引号的字符使用%q打印 1234567ascii := 'a'unicode := '国'newline := '\n'fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'"fmt.Printf("%d %[1]c %[1]q\n", unicode) //...
缓存
缓存雪崩: 缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。缓存雪崩通常因为缓存服务器宕机、缓存的 key 设置了相同的过期时间等引起。 缓存击穿: 一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到 DB ,造成瞬时DB请求量大、压力骤增。 缓存穿透: 查询一个不存在的数据,因为不存在则不会写到缓存中,所以每次都会去请求 DB,如果瞬间流量过大,穿透到 DB,导致宕机。 解决方案:SingleFlight(单次飞行) 无论有多少个并发请求问同一个 Key,在“缓存失效”的那个瞬间,我只放一个请求去查数据库。其他请求全部阻塞等待,等第一个请求查回来,大家共享这个结果。
JWT
JWT(JSON Web Token)是一个自包含,可签名额字符串,常用于鉴权 结构: 1HEADER.PAYLOAD.SIGNATURE HEADER:描述算法如HS256 Patload(Claims): 放自定义字段和标准字段,比如用户ID,过期时间,这个是外部可读的 Signature:对 header+payload 用密钥/私钥签名,保证不可篡改 AccessToken: 短期,用于日常访问,包含用户信息 RefreshToken: 长期,用于换新的accessToken,包含少量信息 JWT工作流程: 用户登录服务器,后端签发accessToken和refreshToken,前端保存token,每次访问接口时带上token,后端验证accessToken,通过则返回数据,如果accessToken过期了,前端自动使用RefreshToken获取新的accessToken,如果refreshToken也过期了,用户要重新登陆
go
思来想去,最后还是决定放弃java去写go了,一方面是java确实比较卷,卷王扎堆,但是更主要的还是自己不喜欢写java。 自己最早其实从7月的时候就有这个想法了,但是当时没有下定决心,而且当时在学cs61b,这门课使用java,所以当时的决定还是继续java,后面开学了开始学框架,看黑马写项目,结果越来越焦虑,经常写着写着就破防了,当时对java就很讨厌了,但是考虑到自己已经学了不少java的技术栈,有一个沉没成本的问题,还是硬着头皮写了一个月把苍穹外卖做了,最后痛苦越来越多,我对学习java已经没有任何兴趣,完全是焦虑驱动学习,在最后一次破防摆烂了几天后,决定转go,那天是11月8号,到现在9天,用了2天学了基础的语法,然后简单的了解了一些go的爬虫,就开始学gin和gorm,之后开始用框架错了一个简单的备忘录后端,这几天的感觉前所未有的好,只能说为什么不早点去学go




