Golang

grpc-go源码分析之logger

从上个月开始梳理grpc-go的源码,希望从中学习到一些来自谷歌“原汁原味”的代码设计及风格。第一个开始分析的包是encoding,这个包下的代码行数不多,主要就是如下两个部分 encoding/decoding compress/decompress encoding/decoding是直接使用了第三方的包「protobuf规范的Go实现」,笔者没有继续深入梳理第三方包的代码。 compress/decompress是使用了Go SDK的compress包,笔者也并没继续往官方包的方向看代码。 所以对于grpc-go的encoding这个包,grpc-go本身并没有自己实现编码/解码,压缩/解压缩的代码,而是调用其他包。所以这个包也没什么代码值得去分析。不过值得一提的是笔者在看这个包的注释时候发现了一个错误,后来提了PR,reviewer当时跟我争辩了几句(他认为没有错)。然而,最终我的PR还是被合并啦! 接下来进入正题,开始分析第二个包grpclog。在梳理这个包的过程中,恰巧我准备从0开始为一个开源项目贡献Go-SDK。截止文章分享时,这个Go-SDK的logger部分我已经编写完成,设计的风格几乎与grpc的logger无异。毕竟,学以是为了致用 😀 通过源码可以知道loggerWrapper已经弃用,loggerT和tLogger是为了单元测试和基准测试用的,所以本文暂不讨论这三种struct(为行文流畅易读,struct在后面统称结构体)。 因此下文主要从以下三点分析: glgger结构体 componentData结构体 初始化流程 glogger结构体 梳理所有grpc-go打印日志的方法(除了测试用的实现方法)会发现:尽管一些方法所属的结构体并不是glogger(一些是下面的componentData),但调用链最终还是会到glogger结构体所属的方法。最后,glogger结构体的方法使用了第三方包“glog”输出日志。 glog是谷歌在github上开源的日志类项目 通过梳理这些结构体的实现方法,还能发现duck-typing这种设计思想在Go语言中的使用,深深体验到这种简洁的设计风格所带来的便利性。简单来说就是当一个结构体的实现了一个接口的所有方法,那么这个结构体就实现了这个接口。以grpc-go举例说明: componentData结构体 首先,可以从componentData结构体实现的方法看出:它同样了LoggerV2接口,如下图: 以componentData结构体的InfoDepth方法举例,该方法作用主要有两点: 在每行日志前拼接调用该方法的微服务名称; 将指定的日志打印栈深度+1。 实际上大部分微服务的都是通过调用Info方法(指定了日志打印栈深度为1),来调用InfoDepth: 最后internal包里的方法会判断是否初始化了变量DepthLogger去执行真正输出日志的方法。至于变量DepthLogger是如何被初始化的,会在第三部分“初始化流程”中分析。先提前说明:这里被初始化进去的正是glogger结构体,所以最终由第三方包“glog”输出日志。 初始化流程...

Continue reading...

使用map降低算法的时间复杂度

在前两周在开发新接口时,遇到了今天要介绍的两种场景,我都通过map降低了整个算法的时间复杂度。我认为有必要记录一下思考过程,也顺便分享给各位读者。虽然简单,但确实很好用。 1. 群组中的好友 背景 各位读者应该都知道在微信聊天框中输入@之后,会出现各群员的列表。新接口功能上差不多,只是在这基础上还要加上一个限制条件:“列表中的群成员必须是好友”。我可以从其他微服务已经有的“好友接口”获取某一个用户的好友列表(list),那么我只需要找出和群成员列表的交集即可。 实现 当时想到有两种方法可以实现上面说的效果(设群有N个群成员,M个好友): 采用两层遍历查询方式,遍历群成员list + 二分查找好友list(查询的时间复杂度:O(N * logM)) 采用map,将群成员id作为map的key,role作为value(查询的时间复杂度:O(1)) 哪种方式更好?通过时间复杂度来看,答案就不言而喻了。部分代码如下: // 获取群内成员(我的源码有用到role做别的业务逻辑处理) func GetMemberInGroup(groupID int64) (map[int64]int8, error) dataSet := make(map[int64]int8) strSql :=...

Continue reading...