简介:杭州开课啦教育科技有限公司是一家致力于为中小学生提供学习辅导的在线教育公司,目前公司后端服务基础设施次要依靠于阿里云原生,其中蕴含计算、网络、存储以及 Kubernetes 服务。
技术选型背景
2020 年是开课啦公司发展壮大的一年,整个公司团队由原来的几百人裁减至当初的几千人,在集中应用的时候基本上会有几千人同时在经营后盾进行操作,公司原有的外部后盾经营零碎是用 PHP 搭建起来的,性能跟业务上已逐步不能满足公司的需要布局,加上目前开课啦公司开发部曾经做了微服务拆分,主体对外服务是 java 语言的 Dubbo 集群,后盾零碎须要无缝对接 java 的 Dubbo 服务,所以 PHP 曾经逐步不能满足开课啦公司的需要。
过后本人也调研过 PHP 的 Dubbo 我的项目,因为我的项目已根本无人更新保护所以 pass 掉,前面本人对简洁高性能的 go 语言感兴趣,而后就关注到了 Dubbo-go 我的项目,通过一段时间的调研之后发现 Dubbo Go 合乎咱们的业务须要,并且社区十分的沉闷,前面便决定选用 Dubbo-go 作为后盾的 pc 业务框架。
可能也有同学会问为什么不应用跨言反对水平更好的 gRPC 呢,因为很多公司最开始的 RPC 服务集群都是基于 Dubbo 生态构建的,如果换框架老本太大,所以根本不会思考,gRPC 尽管跨语言反对水平更好然而很多货色都须要本人造轮子,比方服务注册,服务发现,日志监控等。
过后在决定选用 Dubbo-go 的时候开发外部也有一些拥护的声音的,为什么不间接转 java,转 java 的话就没有跨语言通信的问题了,转 java 的问题在于入门老本高,而且对于整个公司的技术栈来说,放弃语言的多样性,能力更加从容的应答将来的业务变动,Go 自身是一个不弱于 Java 的高性能语言,非常适合微服务架构。
面临的挑战
确定了框架选型后,我接到的首要任务便是要搭建出一套可疾速创立业务我的项目的脚手架,开发出基于 HTTP 协定的 RPC 代理服务,部署须要接入公司的容器化部署平台,一切都是从零开始,在网上基本上找不到能够借鉴的材料。
首先是要进行 Dubbo-go 我的项目的架构的布局,确定我的项目目录构造,通过参考 Dubbo-go Demo 以及其它的 Go 我的项目最终确定了我的项目的目录构造,以下目录构造可作为参考。
为了与 Java 服务注册核心保持一致,Dubbo-go 在我的项目选型上选用如下组件:
- 应用 zookeeper 作为注册核心
- nacos 作为配置核心
- 数据库 orm 采纳 gorm
- 音讯队列应用 RocketMQ
为了减少开发的效率咱们在 provider 服务初始化前能够对配置进行精简只保留最根底的配置就能够相似上面这种,provider 服务的编码参考 Dubbo-go demo 就能够了。
上面是服务启动的 main 办法代码:
Dubbo-go RPC 服务网关设计
个别应用 Dubbo,provider 端须要暴露出接口和办法,consumer 端要十分明确服务应用的接口定义和办法定义,还有入参返参类型等等信息,还须要基于 provider 端提供的 API,两端能力失常通信调用。
然而网关的应用场景是并不关怀要调用的接口的具体定义,网关只关注要调用的办法、传递的参数、能接管返回后果就能够了,实现网关代理的根底是 Dubbo/Dubbo-go 的泛化调用个性。
上面是 Dubbo-go 官网给的 demo,泛化服务加载后须要期待 3 秒能力实现调用,然而在理论应用的时候必定是不能实时加载服务去期待 3 秒,所以在网关利用启动时就须要加载缓存好须要泛化调的服务。
通过对 Dubbo-go 泛化调用 demo 的钻研,发现用该个性设计 dubbo-go 网关是可行的,难点在于咱们须要把每一个须要网关代理 RPC 服务办法的参数以及服务的门路等配置获取到并缓存起来,这样能力在调用前初始化好泛化调用服务,一个服务的配置如下。
因为是用 go 语言做的网关代理,所以不能通过 Java 的 jar 包来获取到 Java RPC 服务配置,如果通过人工保护的话工作量太大,而且易出错,显然是不可承受的。通过一段时间的理解,Java 服务能够通过注解来实现配置的获取,Java 端在办法上加上注解后启动服务的时候会将配置信息通过音讯发送到 MQ,网关生产这些音讯来实现获取 Java RPC 服务的配置。
Dubbo Go 的 RPC 服务因为 go 语言不反对注解,所以我通过思考本人写了一个扫描代码的小工具,在每个 RPC 服务办法前加上对应的正文,通过对正文的扫描来获取 RPC 服务的配置,获取到配置后在我的项目目录内生成 RPC 服务配置,启动利用的时候读取配置发送到 MQ。
网关代理实现之后还能够在网关的根底实现更多的性能,比方 token 验证、白名单、限流、熔断、日志监控性能,网关代理申请实现成果如下:
容器化部署
公司外部的容器化部署环境为阿里云的 K8s,部署至 K8s 平台只须要提供镜像文件,因为 Dubbo-go 编译后是一个二进制的文件,不需任何额定的第三方库,能在 Docker 环境下稳固运行。有 docker 镜像文件如下图所示,能够用 centos 等任一 linux 发行版作为 base 镜像。
LABEL maintainer="<xxx@xx.com>"
LABEL version="1.0"
LABEL description="KKL-GO-NKO-BASE"`
ARG envType=stable
#设置环境变量
ENV envType ${envType}
#编译打包好的压缩包
ADD ./target/nko-base-${envType}.tar.gz /app/
WORKDIR /app
EXPOSE 20000
镜像写好后提供给公布平台,公布平台机器启动镜像并解压打包文件,执行 Dubbo-Go 程序。
Container entrypoint set to [bash, -c, tar -zxf nko-base-stable.tar.gz && SERVER_ENV=kubernetes && sh ./nko-base/bin/load.sh start -group=stable]
因为开发测试到生产个别是有多个部署环境的,所以咱们须要改变的 dubbo-go samples demo 里的编译脚本,让其反对多环境打包。
另外,Dubbo-go 默认注册的 IP 是 K8s pod 的虚构 IP,不同 K8s 集群之间网络是不能互通的,所以如果须要跨集群调用就须要批改默认注册 IP,将默认注册的 pod IP + 端口 批改为 Kubernetes 实体机的 IP 加对应端口,Kubernetes 会在 pod 内写入实体机的 IP 加对应端口环境变量,应用程序能够通过读取环境变量获取实体机的 IP 加端口,如果须要实现此性能须要批改 Dubbo-go 的注册逻辑。例如以 zookeeper 注册核心为例,咱们能够通过扩大
registery/zookeeper/registry.go 的 registerTempZookeeperNode 办法来实现批改注册 IP 跟端口,代码如下图,Dubbo-go 官网将在前面的版本以配置的模式反对自定义注册 IP 跟端口的性能。
func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error {
...
regIp = os.Getenv(constant2.RegistryEnvIP) // 实体机的 ip
regPort = os.Getenv(constant2.RegistryEnvPort) // 实体机的端口
urlNode, _ := common.NewURL(node)
role, _ := strconv.Atoi(urlNode.GetParam(constant.ROLE_KEY, ""))
if role == common.PROVIDER && regIp != ""&& regPort !="" {
urlNode.Ip = regIp
urlNode.Port = regPort
node = url.QueryEscape(urlNode.String())
}
zkPath, err = r.client.RegisterTemp(root, node)
...
}
作者:曾凡维, 一个有 9 年服务端业务开发教训的一线程序员,曾在腾讯阅文等多家公司负责后端开发工程师,目前就任杭州开课啦教育科技有限公司,从事 go 语言服务基础架构和中间件及局部业务开发工作。
原文链接
本文为阿里云原创内容,未经容许不得转载