maksim 发布的文章

这是一个很经典的面试题,例如现在面试官让你写一个超时控制函数,你是否有注意到内存泄露?

package main

import (
    "context"
    "fmt"
    "time"
)

func job() error {
    ctx, _ := context.WithTimeout(context.Background(), time.Second)
    done := make(chan struct{})
    go func() {
        //耗时操作
        time.Sleep(time.Millisecond * 1200)
        done <- struct{}{}
    }()
    select {
    case <-ctx.Done():
        return fmt.Errorf("time out!")
    case <-done:
        return nil
    }
}

func main() {
    fmt.Println(job())
}

如果你的代码是这样写的,那么恭喜你,内存泄露了!

我们修改一下 main 函数:

func main() {
    for i := 0; i <= 50; i++ {
        go func() {
            job()
        }()
    }
    for {
        time.Sleep(time.Second * 2)
        fmt.Println(runtime.NumGoroutine())
    }

}

执行一下再看看,效果很神奇。

2023-04-10T21:31:47.png

它会一直打印 52,证明我们上面开的 50 个协程一直没有被释放。

2023-04-10T21:31:57.png

问题其实就出在我画的圈里面,因为每一个程序都超时了,那么就租到了 ←ctx.Done() 这个 case 中去,Job 就算是完成了,但是我们的 ←done 就无法取值了, done ← strcut{}{} 就会被阻塞在这里了。这个协程就永远结束不了。

解决办法就是给 done 一个长度:

done := make(chan struct{}, 1)

2023-04-10T21:32:11.png

只要协程能够往下走,job 中的协程就会被 gc。

掏出了吃灰的树莓派打算自己搭建一套 DevOPS,树莓派在 hosts 中增加了域名解析。

# pi
192.168.124.15 pi.local

搭建好 nginx 后,那个不安全的字样怎么看都难受,有点强迫症,在线申请免费的 SSQL 有点麻烦,所以就找到了 mkcert。

$ brew install mkcert

安装本地 CA 证书

# 这个步骤需要管理员权限
$ mkcert -install

生成证书:

mkcert  "pi.local"

最后传到树莓派上,设置 nginx。

2023-04-07T12:45:29.png

喜大普奔,大功告成。

参考:

https://www.cnblogs.com/sleepyocean/p/16159327.html
https://juejin.cn/post/7034902197020131364

这个系列的文章来自于,我们以前在团队内部的分享,用来做微服务体系架构设计的学习使用,一开始是放在 github 上的,近期有时间,就打算整理一下,顺便将这段时间的学习和沉淀融入进去,可能会很庞大,所以就先列出来一个目录。

Golang 基础篇

如果不喜欢看视频的话推荐看这个系列的文章 : https://www.liwenzhou.com/posts/Go/golang-menu/

作为入门还是非常不错的。

微服务概念篇

Web 框架篇

RPC 篇

  1. 网络编程
  2. RPC 的实现
  3. 注册中心
  4. etcd 实现注册中心
  5. etcd 实现配置中心

服务治理篇

  1. 可用性
  2. 可观测行

DevOPS 篇

2023-04-03T07:45:34.png

最近失业在家待业,思考了以前的架构设计以及代码设计中的问题。

一次有趣的面试经历

去年在杭州面试一家只有五个程序员的小公司,后台开发人员只有一个人,业务其实并不复杂,其后端采用了微服务架构,于是和对方“犟” 了起来,后来反思这是没有必要的,没有必要和对方犟。

因为对方的老板也在场,其实那个时候我”落“了对方的面子,所以面试结果可想而知,并没有通过,大家一定要引以为戒。

不过,直至今日,我依旧认为对方的技术选型是错误的,是典型的为了用而用,没有去考虑企业的当前场景。架构设计是为业务服务的,最主要的应该是考虑当前的应用场景、当前公司目标追求。

当我踏入那家公司的时候,我看到了白板上的各种微服务划分,DTM 分布式事务管理,我整个人是蒙的,只有一个后台开发,还找么搞,而且还是个初创企业,这样会浪费大量的时间去处理各种由于微服务带来的问题,这对初创企业的投入产出是非常低的,这个阶段的企业考虑的应该是如何活下来,而不应该将大量的事件浪费在技术选型上,以及去不断地解决由于系统复杂度过高导致的各种问题。

2023-04-03T07:51:04.png

如果有微服务开发经验的小伙伴,应该知道,微服务的 Debug 要比单体应用难上很多,而且也会提高新人入职入手工作的难度,毕竟那么多复杂的调用关系,如果文档不够齐全的话,那基本上都是想要一头撞死。

我们真的需要微服务吗?

我认为需要看阶段,在创业初期是不合适的,因为技术是为了业务服务的,如果这个时候就一头扎入到所谓的微服务中,那么将浪费大量的精力,而且微服务也不是”银弹“,它甚至会引发各种各样的问题。

首先说一下微服务的作用吧,微服务简单的理解其实就是分而治之,将复杂的单体应用拆分成一个个独立的微服务,这样就可以降低代码的复杂度,同时为了配合微服务的开发,我们还需要对组织架构进行调整,以配合我们的架构设计。

在励步时候,励步启蒙APP 的后端采用的就是微服务架构,由于当时的团队解散,这个项目到了我们团队手中,我认为当时的架构主要存在下面的问题:

  1. 技术选型混乱,有的地方采用 Gin,有的用 beego,有的地方用 Irsi,有的地方用 ZORM 有的用 GROM,这就导致学习成本激增
  2. 服务之间通信混乱 RestAPI,GRPC,HTTP 调用都有。
  3. 没有配置中心,没有服务发现,配置和服务发现都是在配置文件中写死的,好在一共就三台机器。

当时给我的感觉这要是个单体应用该多好,每一次迭代的时候涉及到多个微服务,我都想撞墙,因为每个服务采用的技术选型都不一样,还需要去看好几个文档。

再有就是我在帷幄匠心的两个月时间,当时的架构也是采用微服务设计,团队成员的技术能力其实非常强,我们有专门的基础架构部门去为业务团队开发各种底层工具,但是当时的微服务治理做的并不是很好,每一次大规模迭代的时候,等 Jenkins 排队都需要好久,同时测试环境冲突特别多,当一个服务同时有多个人员进行迭代的时候,就会发现还需要再测试环境上进行排队,因为混存在代码冲突,服务冲突等抢矿,导致研发效能降低了不少。

这其中出现的问题,我认为最根本的原因是因为组织架构没有做好配合,我们团队一共十几个人,所有人穿插进行开发,没有人固定的负责某个业务模块,这就导致为了同时并行开发多个需求,多个人都要去修改一个微服务,这也就就导致各种环境冲突的原因之一。

那什么时候用比较合适的呢?

我认为在业务已经趋于稳定,原来的代码已经成为了历史遗留系统,甚至成为团队的负担的时候就是微服务化的时候了,使用”绞杀式“的架构方案,逐步对老的功能进行替代。

单体应用的性能问题

有很多人可能会觉着,单体应用性能差,当我们达到一定的量级,一定会是会走向分布式,走向微服务,其实原来我也是这么认为的,直到我看到了下面这篇文章。

首先是微服务,我们真的需要微服务吗?

《没用微服务,Shopify的单体程序居然支撑了127万/秒的请求?》

阅读地址:https://colobu.com/2022/12/04/Shopify-monolith-served-1-27-Million-requests-per-second-during-Black-Friday/