我所理解的微服务

2017-06-27 15:18:11 +08:00
 gouchaoer

自从去年开始工作以后就被微服务刷屏了,各大厂争先进行微服务改造,放出的各种 PPT 文档也是眼花缭乱,由于概念太难了我一直没搞懂。最近终于搞懂了啥是微服务,其实很简单。

首先需要确认的是一般的 web 后端不适合微服务,什么是一般的 web 后端呢?就是一些比较简单的对数据库增删查改或者对缓存进行操作、用来生成网页 html 的、写成一个项目也没啥问题的后端,每一次 http 请求都很快很简单并且没有特别消耗 io 或者 cpu 的调用,比如没有去查 elastic search、没有去 rpc/http 访问别的接口。

而哪些任务适合写成微服务呢?就是那些可以从 web 后端中拆分出来的比较复杂、比较消耗 io、比较消耗 cpu 的一些 api,比如专门负责搜索的 api、比如专门返回用户订单列表的 api、比如需要在后端访问友商接口的 api,我们把这些 api 分离出来做成单独的 web 服务,而这些 web 服务有的可能只需要查询一下 es、数据库和缓存啥的,有的还可能需要在本次请求中再去 rpc/http 调用另一些内网外网的 api 服务。这么做好处就是开发+更新方便,服务之间不容易受到影响。那带来的坏处也有,首先就是前端服务器对 api 的调用需要走 rpc( http rest、thrift 等等),这种 io 消耗是需要避免的;此外还有就是微服务多了调用+上线更新变复杂了,需要自动化这个过程;因此微服务的很多工作就是来弥补这些坏处的。

我认为一个完整的微服务架构最需要满足的几点:

1、凡是涉及到 io 的部分必须有 tcp 池,包括 es、mysql、redis、rpc 甚至 http,我们知道 tcp 建立连接和断开对任何一方都是有消耗的,qps 大了这个消耗都必须避免

2、凡是涉及到 io 的部分要么用异步要么用协程,不允许阻塞,因为 io 部分意味着这部分消耗时间很长,如果阻塞在这里 qps 一大就会同时有长千上万的线程卡在 io 的地方,cpu 调度这些线程消耗很大。很多框架为了证明自己性能喜欢用 hello world 的 qps 来压测,其实这个只能反映一部分性能,因为 hello world 没有任何 io 开销。综合第 1 点和第 2 点,php-fpm 运行方式不适合微服务,应该尽量避免在 php-fpm 中开 httpclient 去访问别的 api。

3、所有内网的 api 服务应该实现自动注册发现( etcd、consul 和 redis 比较多),而且尽可能的使用 ip+端口号来访问,不要用内网域名也不要用 https

4、所有微服务 api 应该实现自动更新+自动上线,假设某个 api 后面后 AB 两台服务器,那么我一旦代码库里发布了一个正式版的代码,那么需要有一个机制保证首先 A 的流量被切掉并且所有 http 都返回后,A 应用 stop 然后更新最后 start,然后轮到 B。当然了这一套可以用 nginx 和一波 openresty 的扩展解决,也可以扩展成一个灰度更新机制(比如根据 cookie 来选择 upstream ),或者 AB test 机制。自动上线不一定非要弄个啥 docker 集群方案,因为我相信大多数业务都比较小可以越简单越好,比如写个 shell 脚本自动配置环境啥的,都行。

5、所有微服务 api 应该有 trace 机制和 log 收集机制,也就是说每次 api 调用链都打进 log 了并且被收集到 log 服务器(通常是 elk ),然后我可以看到本次调用时间或者哪个环境出了问题

6、关于异地部署和灾备,首先我反对异地部署同时提供服务(除非你是腾讯 google 那种大厂,因为太难了),异地可以做个灾备平时不用,一旦发生了严重的状况才把域名解析切到灾备平台(虽然灾备平台很难提供正常服务,不过聊胜于无)。为了避免发生灾难,我认为我们需要尽可能的把主要服务的依赖减到最低,比如我已经依赖了 redis,那么当我需要服务注册发现的时候如果可以用 redis 就最好别去搭建一个 etcd。

7、关于报警和熔断,实际上对机器的性能监控以及对 api 错误的监控大家都在做没啥好说的,然而我认为监控需要能看到哪些 api 请求异常(所以不同的 api 最好独立部署在不同机器上),此时咱们首先肯定加机器(这个加机器能自动自然最好),实在不行肯定就只有把异常的 api 调用掐掉。而异常的 api 调用来自 cc 攻击、ddos 攻击以及爬虫等乱七八糟的,所以说有个很重要的一点是我们可以在 load balancer 的地方清洗一下流量,比如我专门有个服务去读 log 服务器中的异常调用 ip/用户,甚至我的后台逻辑里就主动上报有问题的 ip/用户,然后在 load balancer 的地方主动拦截掉。

以上几点就是我认为一个微服务需要具备的特点。

另外我想谈谈一种长时间 io 服务的情况,比如我有个 api 把一个关键词 http 传给友商,友商需要花上几十秒甚至几分钟才能完成任务,这种情况如果友商完成任务后直接回调通知我还好,如果友商可能没法回调我又或者不太稳定的话,我估计就得每隔几秒去轮询一次直到超时,那这样一个请求占了一个线程几分钟 qps 岂不是狂降?这个时候就用带有 tcp 池的协程就方便很好了,当然了你说可以用延时队列,然而我们到底应该开多少线程去盯着延时队列呢(一个线程同时只能处理一个请求)?

6976 次点击
所在节点    程序员
38 条回复
chairuosen
2017-06-27 18:00:14 +08:00
@timbotetsu 输入法出卖了你
chairuosen
2017-06-27 18:04:51 +08:00
@timbotetsu blue-green 只适合无状态的服务吧?
qieqie
2017-06-27 18:50:31 +08:00
我觉得楼主就是想的太多实践不够
helloword1995
2017-06-27 20:18:45 +08:00
我理解的微服务就是后端解耦,解耦到服务都是“微型”的样子。 解耦主体可以是一个企业,也可以是一个项目, 比如一个电商项目的购买行为,可能需要同时使用用户服务,订单服务,支付服务,物流服务。 而没有使用微服务的架构是没有这些服务区分的。
duoglas
2017-06-27 20:22:16 +08:00
楼主说的每个点本身都是具有一定正确性的。然而并不是有了这些就是微服务,也不是微服务就必须要有这些特性。
zhuf
2017-06-27 22:20:29 +08:00
我们的系统刚开始是用 django 写的,后来随着功能越加越多,导致经常出问题,就按照一个个 app 拆分掉做成单独的服务了
slixurd
2017-06-27 22:38:31 +08:00
就是需要一套服务治理框架
还要有分布式追踪服务
资源池 /报警 /灾备和是不是微服务没关系.....
只要是线上服务都需要做....
sagaxu
2017-06-27 23:01:03 +08:00
1. 准确的说要有连接池,不管是不是微服务,连接池总归要有的。
2. 单机 10000 个线程是个典型的分界线,10000 个以内 IO 密集型线程,调度吃得消。
3. 服务注册和发现,规模很小(服务不到 10 个)的时候,通过配置文件其实也可行,甚至 redis 都不用。
4. 这跟微服务没有关系啊,单体应用也要有自动发布和灰度发布,单体也是可以多机部署的。
5. 单体应用一样要足够多的 trace 去排查一些线上问题的。
6. 灾备必须有,但是跨机房很难,最近一两年支付宝不也出现过异地灾备失效?
7. 单体一样需要监控和必要的防御措施,跟是不是微服务没有太大关系。

调用外部耗时 API 占用线程,这个好解决啊,如果调用链路不深,分支不复杂,用非阻塞的 http 客户端加回调就好了。如果调用链路深逻辑复杂,最简单就是把这部分用 Go 或者 elxir 等内置协程的语言重构成一个服务。
kuno1
2017-06-28 08:04:11 +08:00
nginx 有一篇详细的文章,讲解了微服务的架构设计,值得参考.

https://www.nginx.com/blog/building-microservices-inter-process-communication/
(Building Microservices: Inter-Process Communication)
gouchaoer
2017-06-28 10:32:49 +08:00
@sagaxu 如果单机达到了 10000 个线程的话,内存占用已经很大了吧,我想问问 swoole 的 coroutine 或者 yield 那种半协程的内存占用是多少呢(比如我现在有 10000 个协程在跑所占用的内存)?我看了一下一个 go 的协程似乎只有 8K 字节,swoole 的协程占用内存比较多是不是因为 zend 虚拟机的栈比起 go 的栈要深很多呢?
everhythm
2017-06-28 12:48:51 +08:00
微服务架构特点,不是要讲 微 的特点么,初看好多都是 服务架构 的特点
sagaxu
2017-06-28 13:55:40 +08:00
@gouchaoer Java 线程栈,默认 1M 一个,一般 128KB 也够了。Go 协程,1.4 的时候从 8KB 减少到 2KB 了,将来不管怎么变,估计是不会超过两个 page 的 size 了。我需要协程就直接用 Go 了,所以 swoole 的协程并没研究过。
wangjxxx
2017-06-28 14:03:06 +08:00
这里有一系列的文章 http://blog.daocloud.io/microservices-1/
感觉回答微服务是什么,为什么要微服务,怎么弄微服务
Gathaly
2017-06-28 14:32:46 +08:00
我理解其实就是一种以多进程通讯实现、将业务进行水平拆分的解决方案
感觉这种做法之前一直都存在,最近几年是给它套了个马甲
最大特点呢,业务重用是其次,最大作用是可以在更新时,不用停止全部服务
可能待的不是互联网公司,我觉得它对于自己来说和 docker 一样,没有会用到的机会
gouchaoer
2017-06-28 23:48:02 +08:00
@sagaxu 这就很尴尬了,我刚去看了一下 swoole 的协程发现写死了每个 worker 能开的最多协程数量是 3000 个。打比 4 核 cpu 开 8 个 worker 也才 24000 个协程,而 go 的协程可是几十上百万的吧
abcbuzhiming
2017-06-28 23:52:46 +08:00
各位,微服务最大的问题在于每个服务背后都有一个独立的数据源啊,你们是怎么做这种分布式事务的,我觉得这才是核心问题,不论这个的话,微服务不过是把以前在一个项目组织内把功能分组进化到每个组进化为一个项目而已,如果解决不了分布式事务的问题,微服务压根也不能有效解决水平扩展问题。但是网上基本没有讲这块的具体实现的,都一股脑的说如何拆分业务
gouchaoer
2017-06-29 08:39:58 +08:00
@abcbuzhiming 不要事务,也有分布式带事务的比如 tidb 啥的,不同微服务用不同数据库,数据库撑不住了上集群方案
abcbuzhiming
2017-06-29 09:06:37 +08:00
@gouchaoer 你在大部分企业里实践微服务的第一个绕不过去的坎就是把他们原本基于传统关系数据库的系统改造成基于微服务的架构,这其中必然有一致性要求高的业务,要完全不要事务,纯粹 kv 存储是不可能,分布式事务的关系数据库,目前感觉都在发展中,鲜有成熟的例子

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/371405

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX