现在有越来越多的公司将go语言做为后台开发的首选语言,另外在后台开发中,微服务应该算得上是最流行的架构模式了。

写这个系列的文章,主要是对此前项目中的微服务实践做个自我总结,因为个人也是个初学者,在黑暗中摸索,所以如果文章中出现低级错误也请读者勿要见笑,同时希望大神们不吝赐教。

本系列的文章,将围绕搭建一个基于微服务架构的数据统计系统的实战例子展开,我们将展示如何将服务运行在docker容器中,并将其部署在kubernetes上,相信看完这个系列的文章后,你也可以快速入门后台开发。

在开始动手之前,咱们先用一篇文章简单了解一下go语言和微服务。


1、为什么用go语言


越来越多的公司开始用go语言进行后台开发,比如腾讯就在逐步的将后台从C++向Go语言过渡。任何事情的发生背后都是有原因的,使用go语言开发服务端程序的最主要的原因应该是go具有支持大并发的基因。


1.1 天然的并发支持

了解过go语言的同学一定知道协程这个概念。相比线程而言,协程因为可以免去线程切换时用户态和内核态的切换,具有更高的性能,对并发的支持非常好,即便一个刚进入后台开发的小白,用go语言也可以写出支持几十上百万的程序。


1.2 编译速度快

编译速度对程序员来说一直是个绕不开的话题,大型的项目动不动就是几个小时的编译时间,本人至今还依稀记得编译一次android源码的酸爽。相较于其他语言,go语言的编译速度快可以说是它的另一大优势。


1.3 完备的垃圾回收机制

对于用C++开发后台的程序员来说这无疑是个福音。但凡写过C++的一定对内存泄漏深有体会,即便有智能指针也不能避免,一个稍微大点的项目,内存问题排查起来费时费力,在这一点上,go的设计者吸纳了Java GC的理念,用GC将开发者从手动管理内存分配释放的苦恼中解脱出来(当然跟Java一样,即使有垃圾回收,go也无法彻底避免内存泄漏)


1.4 简单易学

go的关键字数量非常少,简单易学,新手能够快速入门。如果学C++,里面随便抽出一块诸如STL之类的,估计都够大部分人喝上一壶。go则不然,语法简练,稍有编程基础的人可以足够快的入门。

这也是go语言设计者的初衷之一:其他的语言是在不断的向其中堆加新功能,而go语言则是做减法,取其精华去其糟粕。

了解了go,在回头来看看微服务,这是目前后台开发中比较流行的框架,对后台开发有兴趣的同学值得了解和学习。


2、初识微服务


2.1 单体架构

要想搞明白微服务,先得了解单体架构。一般小的公司在项目规模还不是很大的时候,为了快速上线新功能,往往会采用这种架构。这种架构就跟它的名字一样简单易懂。设想现在要做一个电商项目,可以创建订单,验证库存,配送等等。

因此在服务端可能有不同的模块:用户模块,订单模块,物流配送模块等等。

在单体架构模式下,将这些模块都整合成为一个整体部署进去,在加一个ngix反向代理做负载均衡就可以了,就向下面这张图一样:

一个Ngix反向代理服务器做负载均衡,一个数据库,再加上一个打包运行在容器中的业务进程,这基本上就是单体架构的全部了。

正如图中所说的,这种架构的特点就是:开发、测试、部署和扩容都非常简单,业务规模大了填机器就行了(这也是很多向我一样没有搞过后台开发的人的幼稚思维:后台不就是加机器就可以了吗?)


单体架构的优点很明显,但缺点也同样明显

(1) 代码库庞大:所有的业务代码都在一个工程中,随着产品需求和功能的不断堆加,代码会变得越来越多并且难以维护;

(2) 模块边界模糊:很可能只是每个模块仅仅用一个文件夹了事,但实际的依赖关系很难理清;

(3) 构建部署问题:每次修复一个问题,整个应用都需要重新打包部署。试想一下,因为用户模块的一个小bug,就要重新打包部署整个应用,导致订单等其他模块在一段时间内也不可用,这对用户体验及其不友好;

(4) 阻碍技术创新:在单体应用下,整个团队只能使用相同的技术栈,要么大家都用Java,要么大家都用C++,如果你想尝鲜用用go,对不起,其余人可能没那么多时间陪你玩。总结起来一句话:项目规模小的时候,单体架构完全可以应付好一阵子,但是当规模上来以后,单体架构一定无法满足需求。


2.2 微服务架构

说完了单体架构,再来说说微服务。微服务架构的哲学其实可以理解为设计模式中的单一职责原则:只做一件事并将它做到极致。仍以上面的电商那个例子来看,在微服务架构模式下,可以拆分成用户服务,订单服务,物流服务等一个一个的微服务。

这些微服务不再向单体那样铁板一块,而是单独部署。这就解决了前面提到的单体架构的一些痛点问题:

(1) 因为微服务是单独部署的,所以假如用户服务出了问题,只需要修复问题以后重新部署用户服务即可,其他服务依然可以对外提供服务;

(2) 模块清晰:每个微服务就是一个独立的功能模块,微服务之间可以通过RPC的方式调用其依赖的服务;

(3) 技术栈可以异构:负责用户服务的团队用的是基于Java的Spring,而负责订单服务的团队可以用Go。微服务架构虽然解决了单体架构的问题,但是随之而来的一个无法回避的问题就是服务与服务之间互相调用的问题。


因为服务是单独部署的,因此服务间要想相互调用只能通过RPC的方式:部署在机器A上的用户服务通过RPC的方式调用部署在机器B上的订单服务。这就引入了微服务架构中一个重要的组件:服务发现与注册中心。

你要调用订单服务的接口,首先你得知道哪些机器上部署了订单服务,注册中心就是干这个事的,所有的微服务都把自己注册到注册中心,这样注册中心就知道哪个服务在哪些机器上,当有调用请求的时候,由注册中心按一定的负载均衡策略把这个请求落地到具体的机器上,下面是最能说明服务注册中心的一张经典图:

有好事的同学可能要问了:那注册中心挂了咋办?

没错,注册中心是大脑,脑死亡了当然不行,所以注册中心在部署的时候肯定是要部署多台机器做到高可用。

注册中心作为微服务架构中的核心组件,其本身的设计实现是很复杂的,目前常见的注册中心有阿里开源的Nacos,ZooKeeper,Consful,Eureka等等,另外kubernetes也可以作为注册中心来使用。如果对注册中心的实现感兴趣,建议可以看看Nacos的代码。


3 总结


go语言的特性:并发支持好、编译快,简单易上手,特别适合新手快速入门后台开发;单体架构的应用易于开发、测试和扩容,但是随着项目规模越来越大将会越来越难维护,容易积累技术债,后期在想升级架构会付出很大的成本。

微服务架构解决了单体架构的痛点问题,足以应对大规模用户量的场景。