Go服务开辟高手课(极客讲堂)

[复制链接]
发表于 2025-10-5 19:59:56 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
一、工具利用

1. pprof 工具。当 CPU 大概内存占用率过高时,利用 pprof 工具可以或许精准定位到斲丧资源的热门代码。
2. benchmark 工具。要对差别方案举行性能对比时,利用 benchmark 工具,可以获取差别方案在耗时 和 内存斲丧 方面的对比环境。
二、单机吞吐优化

1. 当用 map 构造集适时,可以将 value 范例设置为空结构体范例(struct{}),不占用内存空间,低落内存斲丧。
2. 当创建 map 和切片对象时,如果可以提前确定容器容量,那就把容量传入 make 函数中,从而制止往聚会合添加数据时触发扩容迁移,低落内存和 CPU 资源斲丧。
3. 有大量字符串拼接利用时,可以利用 strings.Builder 范例,并利用它的内存预分配功能做字符串拼接。
4. 有大量整型转字符串利用时,可以用 strconv 库做转换,制止利用 fmt.Sprint 函数的反射和格式化。
5. 有大量字符串转字节切片利用时,可以用 unsafe 包,通过字符串和字节切片底层数组空间共用,实现高性能转换
6. 须要频仍创建类似范例的临时对象时,可以利用 sync.Pool 对象池,实现临时对象复用,从而镌汰 Go 中的内存分配和 GC 开销。
7. 须要频仍地创建协程,可以利用协程池。可以低落协程创建的开销。同时,协程池能限定同时运行的协程的最大数量,从而制止同时有太多协程,导致频仍举行协程调治。
六、并发等候:怎样低落实时体系的相应延时?

1. WaitGroup 范例。一个规模较大的任务,为了进步实行服从,可以将其拆分成多个子任务,然后让这些子任务并发运行。而此时,如果还须要等候全部子任务都顺遂实行完毕,那么 WaitGroup 范例可以或许精准地满意需求。
2. errgroup 包。errgroup 包可以看作是对 WaitGroup 范例的升级与封装。在实际开辟中,须要全面地对大概出现的错误举行处置惩罚、机动地取消任务以及精准地控制最大协程数等需求, errgroup 包就是最佳选择。
七、并发安全:怎样为差别并发场景选择符合的锁?

1. 对数据的写利用较多大概读利用不频仍,可以利用互斥锁Mutex。
2. 读利用远多于写利用,可以利用读写锁,允很多个协程同时举行读利用,进步并发读取的性能。
3. 当大量数据存储在 map 中,而且协程对 map 的访问相对匀称地分布在差别的键上时,可以思量利用分段锁。具体是将 map 分成多段,每段有自己的锁,低落锁粒度,从而提升并发性能。
4. 须要对共享对象举行原子利用,可以利用 atomic 包无锁编程,制止加锁利用,从而提升性能。
八、并发map:百万数据本地缓存,怎样降延时减毛刺?

1. sync.Map 借助两个 map 来告竣读写分离的计划,提升读取利用的性能。
2. 然而,这种计划固然服从高,但存在着不容忽视的标题:
(1) 须要两个map,内存占用相对较高。
(2) 数据修改频仍时,读表掷中率低。不掷中的时间,既要读一次读表,又要读一次写表,低落性能。
(3) 读掷中率低时,还会产生两个 map 之间的数据拷贝开销。数据量大时,会导致较大的性能开销。
3. sync.Map 平静凡 map 的对比:
    sync.Map通过‌读写分离、延伸更新、细粒度锁控制‌三项核心机制,在并发场景下实现比平凡map更高的吞吐量‌。
a. 读写分离:读利用优先访问无锁的读map。
b. 延伸更新与删除优化:删除利用仅标志为逻辑删除,实际整理延伸到写map提升为读map时再批量处置惩罚,制止高频删除。
c. 细粒度锁控制:仅在读表未掷中时,以及数据拷贝(读map拷贝为写map)时利用才互斥锁
4. 当 map 中缓存的数据比力多时,为了制止 GC 开销,可以将 map 中的 key-value 范例计划成非指针范例且巨细不高出 128 字节,从而制止 GC 扫描。
九、网络编程:怎样举行网络IO编程降斲丧,提吞吐?

1. 网络 IO 模子有壅闭 IO、非壅闭 IO、IO 多路复用和异步 IO 多种范例,实践中比力常用的是 IO 多路复用模子。
2. 壅闭 IO: socket 缓冲区没有预备好,让线程壅闭在 IO 调用不返回。
    每个毗连我们都须要创建一个专门的线程来处置惩罚。
3. 非壅闭 IO:socket 缓冲区还没预备好,网络 IO 体系调用立即返回,不壅闭线程,线程可以处置惩罚另一个毗连的哀求。
    须要利用轮询不停做体系调用,浪费大量 CPU 资源。
4. IO 多路复用:一个线程壅闭监听多个毗连的网络 IO 变乱,当有毗连的 socket 缓冲区预备好,IO 调用就会返回。
    既不消创建大量线程,也制止不停轮询。
5. 异步 IO:线程举行IO 调用会立即返回,由内核负责将 socket 缓冲区数据复制到用户空间,然后关照线程完成,整个过程完全没壅闭。
    各个平台对异步 I/O 的支持水平不一,这种方式利用起来复杂度较高,因此并不是很广泛。
6. epoll 技能分析
(1) epoll_create 函数,在 Linux 内核创建一个内核须要监听的网络毗连池子。
(2) epoll_ctl 函数,增编削池子里须要监听的毗连和变乱。
(3) epoll_wait 函数,壅闭等候池子里毗连的网络 IO 变乱。
7. 线程调用 epoll_wait 方法时,吸收来自利用体系的关于 网络 IO 变乱停当的关照 的触发模式
(1) 水平触发:
a. 只要socket 缓冲区有未处置惩罚数据,一连触发变乱
b. 应用层未处置惩罚完数据时,内核保持fd在停当队列
c. 下次epoll_wait()会立即返回该fd
d. 不须要包管缓冲区充足大。
(2) 边沿触发:
a. 仅在fd状态厘革时触发一次关照
b. 内核不会保存fd在停当队列(除非新变乱到达)
c. 应用层必须一次性处置惩罚完全部数据。
d. 须要包管缓冲区充足大。
8. Golang 运行时是怎样感知网络 IO 停当变乱:
(1) 在步伐启动时,Golang 底层会单独启动一个线程,该线程每隔 10ms 轮询调用 netpoll 函数,实验取出网络 IO 变乱停当的协程列表,举行叫醒利用。(非壅闭的轮询读取)
(2) 举行协程调治时,Golang 底层会为当前处置惩罚器探求下一个可实行的协程。如果没有可调治的协程,就会实验获取网络 IO 停当的协程用于调治。
(3) GC 过程中调用完 STW(stop the world)后,都会调用 start the world,此时也会对网络 IO 变乱停当的协程举行叫醒利用。
十、网络通讯:不改业务代码,怎样低落延时?

1. 只管不跨地区、不跨机房调用。
2. 相应速率要求高的上卑鄙服务,摆设到一台物理机里。
3. 将RPC 调用,归并编译成本地 SDK 的函数调用。
十一、数据库:怎么提升服从?

1. 读写分离。当数据库读 QPS 过高时,可以通过读写分离架构,增长从库来提升读 QPS。
2. 分表。当单表数据行数太多,导致读性能降落时,可以用分表架构,将一张表拆成多张小表,从而提升读性能。
3. 分库。当数据库写入 TPS 过高时,可以用分库架构,通过增长多个主库,分散单库的写压力,从而进步写 TPS 上限。
十二、分布式缓存:大促抢购,不知热门咋防热Key?

1. 本地缓存全量数据。要是数据量不大,可以直接在服务本地内存把全部数据都缓存起来,能大幅度低落热 Key 标题导致的 Redis 访问压力。
2. 本地只缓存热 Key 数据。当服务不能缓存全部数据时,可以接入热 Key 探测框架,只把那些被频仍访问的热 Key 数据存到本地。
3. Redis 读写分离架构。要是不想让业务层变得复杂,可以采取读写分离架构,给每个 Redis Server 都加上从库,让从库去应对热 Key 的高频率读取,分担压力。
4. Redis 提供的Proxy 热 Key 承载方案。利用 Proxy 来缓存热 Key 数据,负担热 Key 访问所带来的压力。比如 阿里云 Redis 的 Proxy Query Cache
十三、分布式缓存:大Key更新,拆分大Key怎样防脏读?

1. 用 PB 序列化做数据压缩。在将数据存储到 Redis 时,不利用 JSON 格式,过用 PB 序列化,可以制止不须要的数据写入,从而有用镌汰数据体积。
2. 数据拆分,同时加上版本号作为key包管制止脏读。 将数据拆分成多个部分,并为每个部分添加版本号作为子 Key。然后重新组装时,根本版本号得到key值,再读书多个部分数据,组合成原数据。
十四、本地缓存:本地缓存全量数据的方案,须要办理的标题

1. 办理步伐启动时的服从标题。
       数据量较小的环境,可以直接从数据库轮询获取数据。然而,面临大量数据时,这种方法会导致启动时间过长。为了加快步伐启动,可以采取本地文件加载和数据库轮询加载相联合的计谋。
2. 办理缓存更新的标题。
       对于对实时性要求不高的场景,可以设定一个时间隔断,定期从数据库轮询获取更新的数据。但是,对于对实时性要求高的场景,可以采取 RocketMQ 广播消耗的方式,x更快速的数据同步。
3. 办理数据量过大的标题。
       当本地缓存的数据量超出单机内存的承载本领时,可以采取分片集群的头脑。将数据分散加载到差别的服务集群中,从而低落单机内存的负担。
十五、项目拆分:业务逻辑复杂,怎样拆分服务让协作清晰有序?

1. 确定是否有须要采取微服务架构。一方面要思量项目的复杂水平;另一方面要考量公司的微服务底子办法是否美满。比如 快速编译、自动摆设、Kubernetes容器、 服务注册和发现等。
2. 举行服务拆分时,可以从业务、技能和构造结构这三个维度入手。
十六、目次规范:几万行的大文件,怎样重构目次结构?

经典的后端三层架构:
1. Controller 层。用于吸收前端哀求,并调用 Service 层。
2. Service 层。负责具体的业务逻辑处置惩罚,调用 DAO 层,不直接读写数据库。
3. DAO 层。负责 MySQL、Redis 等数据库的增编削查。
十七、计划原则和模式:功能一连迭代,怎样镌汰改动?

1. 依赖反转原则:高层模块不应该直接依赖于低层模块的具体实现,两者都应该依赖于接口。
    作用:将高层模块和低层模块的具体实现解耦。
    实用场景:低层模块将来很大概须要更换或升级时。
2. 计谋模式:对象的某个举动,在差别的场景中该举动有差别的实现算法。
    作用:消除过长的 if else 代码。(if else 只负责计谋范例构造, 具体逻辑在各自计谋范例里实现)
    实用场景:if else 各分支的功能一样,只是实现方法不一样时。
3. 简单工厂方法模式:根据传入的参数,动态决定应该创建哪一个结构体的实例。(这些结构体实现了同一个接口)
    作用:便干对象创建的会合管理,制止ifelse创建对象的逻辑在各处重复
    实用场景:须要根据差别的条件,创建实现类似接口的差别结构体时。
4. 制作者模式和函数选项模式:构建对象时,用同一的构建接口。但具体怎么构建,在构建接口以外的代码去机动界说,比如通过一个函数去界说,并传入到构建对象接口。
作用:低落构建接口的复杂性,和修改维护的资本。
实用场景:构建函数须要有多种方式,构建函数参数也有多少方式。
5. 责任链模式:将一个复杂的处置惩罚过程,分散到多个处置惩罚对象中。 每个对象只处置惩罚自己特定的逻辑和职责,而且根据环境决定是否将哀求通报给下一个对象。
作用:低落了体系的复杂性;实现处置惩罚逻辑在多个场景中的代码复用。
十八、函数计划:重复编写相似函数,怎样实现逻辑复用?

1. 反射机制。 reflect 的反射功能,可以将接口范例 interface{} 的变量转换为反射范例对象 reflect.Type 和 reflect.Value,通过它们就可以访问和利用真实对象的方法和属性。
    但在实际开辟中,‌若范例已知,优先用范例断言‌;若范例需动态才气确定,则需利用反射。
2. 泛型功能。可以或许通过范例参数与范例束缚,来构建泛型范例和函数。运用范例参数,让代码得以适配多种差别范例,无需为每种范例编写特定实现。
十九、代码陷阱:最易导致步伐堕落的四类代码坑

1. 接口变量判空标题。接口变量在底层会储存范例 和值 两个元素。当值为  nil  但范例不为  nil  时,就会出现判空不符合预期的环境。返回nil必须返回字面值nil,不能返回(nil值的)接口变量。
2. 循环变量的利用。在 Go 1.22 版本之前,循环迭代变量的作用域涵盖整个循环体。如果我们在循环内部,通过闭包的方式直接利用这个变量,极有大概引发错误。可以创建一个新变量,并以参数的方式输入。
    闭包是‌函数与外部变量的组合体‌,可以或许捕捉并持有外部作用域的变量引用‌。
    循环变量在‌整个循环体‌内共享同一内存所在,闭包捕捉的是引用而非值拷贝‌。
    闭包实行时(如延伸调用或异步回调),循环已竣事,变量值为末了一次迭代的效果‌
3. 数值范例的 JSON 反序列化标题。当我们采取  map[string]interface{}  范例对 JSON 字符串举行反序列化利用时, int  范例会酿成了  float64  范例。
4. WaitGroup利用不妥。必须在启动协程之前,在外部调用 Add  方法。
   channel  利用不妥。吸收者存在超时逻辑时,利用的channel 应采取带缓冲区 (非壅闭型), 制止因吸收者超时,而导致发送者的协程永久被壅闭(channel发送信号但无吸收者)。
二十二、超时和重试:怎样提升高并发告急哀求的乐成率?

1. 上游的超时时间,应大于卑鄙超时时间 乘以 重试次数。
2. 重试阈值熔断, 当在一个时间窗口内,重试哀求到达肯定比例,就不再举行重试,制止大面积重试把卑鄙打垮。
二十三、熔断和降级:卑鄙服务大量报错,怎样快速止损?

1. 让报错服务返回兜底数据。
2. 每个服务都事先预备好兜底数据机制。
二十四、限流:不消Redis,怎样搞定高并发低延时服务限流?

保障服务的稳固性比即时性更告急,以是可以对服务做限流利用,并利用特定的限流算法。
从简单到复杂依次是:
1. 固定窗口算法。每段时间能处置惩罚的哀求数是固定的。
    缺点:当哀求处于窗口界限时,不能有用实现限流。
2. 滑动窗口算法。把每个固定窗口,切分成多个子窗口,再滑动地把子窗口组合成大窗口,再做限流。
    缺点:应对流量颠簸时,抗抖动本领差的标题依然存在。
3. 漏桶算法。处置惩罚哀求的速率,在一段时间内被被限定在一个固定值,高出固定值之后,哀求须要等候,大概直接拒绝。
    缺点:未能分发掘体系资源的潜力。
4. 令牌算法。
(1) 体系会以固定的速率天生令牌,体系空闲时,令牌一连堆积。
(2) 出现大流量哀求时,大量斲丧令牌。令牌斲丧完之后,哀求须要等候,大概直接拒绝。
(3) 继续以固定的速率天生令牌。
长处:通过令牌总数限定体系繁忙水平的上限, 当体系资源利用已达峰值时,保持较低令牌的生产速率,让体系得到喘气的机会,制止“落井下石”。 既充实发挥体系潜力,也制止直接把体系打挂。
二十五:灰度发布:新功能上线怎样有用控风险?

1. 蓝绿摆设:先更新一部分实例,确认没标题之后,再更新剩余实例。
2. 灰度摆设:先跟新个别实例,确认没标题之后,再更新全部。

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表