快速了解什么是:微服务
Posted 做一道光
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速了解什么是:微服务相关的知识,希望对你有一定的参考价值。
1. 微服务简介 随着互联网技术的飞速发展,目前全球超过半的人口在使用互联网,人们的生活随着互联网的发展,发生了翻天覆地的变化。各行各业都在应用互联网 国家政策也在大力支持互联网的发 展。随着越来越多的用户参与,业务场景越来越复杂,传统的单体架构己经很难满足互联网技 术的发展要求。这主要体现在两方面, 是随着业务复杂度的提高,代码的可维护性、扩展性 和可读性在降低; 是维护系统的成本、修改系统的成本在提高。所以,改变单体应用架构己 经势在必行。另外,随着云计算、大数据、人工智能的飞速发展,对系统架构也提出了越来越 高的要求。 微服务(不是一个框架 而是一种架构思想),是著名的 oo (面向对象, ObjectOriented )专家 Martin Fowler 提出来的,它是用来描述将软件应用程序设计为独立部署 的服务的种特殊方式。最近两年,微服务在各大技术会议、文章、书籍上出现的频率已经让人 意识到它对于软件领域所带来的影响力。 微服务架构的系统是个 分布式系统 ,按业务领域划分 为 独立的服务单元 ,有自动化运维、容错、快速演进的特点,它能够解决传统单体架构系统的 痛点,同时也能满足越来越复杂的业务需求 。 1.1 单体架构不足 在应用的初始阶段,单体架构无论是在开发速度、运维难度上,还是服务器的成本上都有着显著的优势。在一个产品的前景不明确的初始阶段,用单体架构是非常明智的选择。随着应 用业务的发展和业务复杂度的提高,这种架构明显存在很多的不足,主要体现在以下 3 个方面: 1. 业务越来越复杂,单体应用的代码量越来越大,代码的可读性、可维护性和可扩展性下降,新人接手代码所需的时间成倍增加,业务扩展带来的代价越来越大。 2. 随着用户越来越多,程序承受的并发越来越高,单体应用的并发能力有限。 3. 测试的难度越来越大,单体应用的业务都在同个程序中,随着业务的扩张、复杂度的增加,单体应用修改业务或者增加业务或许会给其他业务带来定的影响,导致测试难度增加。 1.2 到底什么是微服务 什么是微服务呢? 就是将一个大的应用,拆分成多个小的模块,每个模块都有自己的功能和职责,每个模块可以进行交互,这就是微服务 对于微服务,业界没有严格统一的定义,但是作为“微服务”这名词的发明人,Martin Fowler对微服务的定义似乎更具有权威性和指导意义,他的理解如下: 简而言之,微服务架构的风格,就是将单一程序开发成一个微服务,每个微服务运行在自己的进程中,并使用轻量级通信机制,通常是 HTTP RESTFUL API 。这些服务围绕业务能力来划分构建的,并通 过完全自动化部署机制来独立部署这些服务可以使用不同的编程语 言,以及不同数据存储技术,以保证最低限度的集中式管理。 1.2.1 总结出微服务的特点 1. 按业务(功能)划分为一个独立运行的程序,即服务单元。 2. 服务之间通过 HTTP 协议相互通信。 http 是一个万能的协议 (web 应用都支持的模式) 3. 自动化部署。 4. 可以用不同的编程语言。 5. 可以用不同的存储技术。 6. 服务集中化管理。 7. 微服务是一个分布式系统。 1.3 微服务特点的具体阐述 1.3.1 微服务单元按业务来划分(不是绝对的) 微服务的“微”到底需要定义到什么样的程度,这是个非常难以界定的概念,可以从以个方面来界定: 是根据代码量来定义,根据代码的多少来判断程序的大小: 是根据开发时间的长短 来判断: 是根据业务的大小来划分。根据 Martin Fowler 的定义,微服务的“微”是按照 业务来划分的 。一个大的业务可以拆分成若干小的业务, 个小的业务又可以拆分成若干更小 的业务,业务到底怎么拆分才算合适,这需要开发人员自己去决定。例如微博最常见的功能是微博内容、关注和粉丝,而其中微博内容又有点赞、评论等,如何将微博这个复杂的程序划分 为单个的服务,需要由开发团队去决定。按业务划分的微服务单元独立部署,运行在独立的 程中 这些微服务单元是高度组件化的模块,并提供了稳定的模块边界,服务与服务之间没有 任何的相合 有非常好的扩展性和复用性。传统的软件开发模式通常由 UI 团队、服务端团队、 数据库和运维团队构成,相应地将软件按照职能划分为 、服务端、数据库和运维等模块。通 常这些开发 员各司其职 很少有人跨职能去工作。 如果按照业务来划分服务,每个服务都需 要独立的 UI 、服务端、数据库和运维。也就是说, 个小的业务的微服务需要动用 个团队的 人去协作,这显然增加了团队与团队之间交流协作的成本。所以产生了跨职能团 队,这个团 队负责一个服务的所有工作,包括 UI 、服务端和数据库。当这个团队只有 个人的时候,就 对开发人员提出了更高的要求。 1.3.2 微服务通过 HTTP 来互相通信 按照业务划分的微服务单元独立部署 并运行在各自的进程中。微服务单元之间的通信方般倾向于使用 HTTP 这种简单的通信机制,更多的时候是使用 RESTfulAPI 。这种接受请求、处 理业务逻辑、返回数据的 HTTP 模式非常高效,并且这种通机制与平台和语言无关。例如用 Java 写的服务可以消费用 Go 语言写的服务。 1.3.3 微服务的数据库独立 在单体架构中,所有的业务都共用个数据库。随着业务量的增加,数据库的表的数量越来越多,难以管理和维护,并且数据量的增 会导致查询速度越来越慢。 例如个应用有这样几个业务: 用户的信息、用户的账户、用户的购物 、数据报 服务等。典型的单体架构如微服务的 个特 点就是按业务划分服务,服务与服务之间无稠合,就连数据库也是独立的个典型的微服务的架 构就是每个微服务都有自己独立的数据库,数据库之间没有何联系这样做的好处在于,随着业 务的不断扩张,服务与服务不需要提供数据库集成,而是提供 API 接口相互调用:还有 个好 处是数据库独立,单业务的数据盆少,易于维护,数据库性能有着明显的优势,数据库的迁移 也很方便。另外,随着存储技术的发展,数据库的存储方式不再仅仅是关系型数据库,非关系 数据库的应用也非常广泛,例如 MongoDB ,它们有着良好的读 性能,因此越来越受欢迎 个典型的微服务的系统。 1.3.4 微服务的自动化部署(CI /CD)(持续集成 持续交付)
你了解微服务的超时传递吗?
为什么需要超时控制?
很多连锁故障的场景下的一个常见问题是服务器正在消耗大量资源处理那些早已经超过客户端截止时间的请求,这样的结果是,服务器消耗大量资源没有做任何有价值的工作,回复已经超时的请求是没有任何意义的。
超时控制可以说是保证服务稳定性的一道重要的防线,它的本质是快速失败(fail fast),良好的超时控制策略可以尽快清空高延迟的请求,尽快释放资源避免请求的堆积。
服务间超时传递
如果一个请求有多个阶段,比如由一系列 RPC 调用组成,那么我们的服务应该在每个阶段开始前检查截止时间以避免做无用功,也就是要检查是否还有足够的剩余时间处理请求。
一个常见的错误实现方式是在每个 RPC 服务设置一个固定的超时时间,我们应该在每个服务间传递超时时间,超时时间可以在服务调用的最上层设置,由初始请求触发的整个 RPC 树会设置同样的绝对截止时间。例如,在服务请求的最上层设置超时时间为3s,服务A请求服务B,服务B执行耗时为1s,服务B再请求服务C这时超时时间剩余2s,服务C执行耗时为1s,这时服务C再请求服务D,服务D执行耗时为500ms,以此类推,理想情况下在整个调用链里都采用相同的超时传递机制。
如果不采用超时传递机制,那么就会出现如下情况:
- 服务A给服务B发送一个请求,设置的超时时间为3s
- 服务B处理请求耗时为2s,并且继续请求服务C
- 如果使用了超时传递那么服务C的超时时间应该为1s,但这里没有采用超时传递所以超时时间为在配置中写死的3s
- 服务C继续执行耗时为2s,其实这时候最上层设置的超时时间已截止,如下的请求无意义
- 继续请求服务D
如果服务B采用了超时传递机制,那么在服务C就应该立刻放弃该请求,因为已经到了截止时间,客户端可能已经报错。我们在设置超时传递的时候一般会将传递出去的截止时间减少一点,比如100毫秒,以便将网络传输时间和客户端收到回复之后的处理时间考虑在内。
进程内超时传递
不光服务间需要超时传递进程内同样需要进行超时传递,比如在一个进程内串行的调用了Mysql、Redis和服务B,设置总的请求时间为3s,请求Mysql耗时1s后再次请求Redis这时的超时时间为2s,Redis执行耗时500ms再请求服务B这时候超时时间为1.5s,因为我们的每个中间件或者服务都会在配置文件中设置一个固定的超时时间,我们需要取剩余时间和设置时间中的最小值。
context实现超时传递
context原理非常简单,但功能却非常强大,go的标准库也都已实现了对context的支持,各种开源的框架也实现了对context的支持,context已然成为了标准,超时传递也依赖context来实现。
我们一般在服务的最上层通过设置初始context进行超时控制传递,比如设置超时时间为3s
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
当进行context传递的时候,比如上图中请求Redis,那么通过如下方式获取剩余时间,然后对比Redis设置的超时时间取较小的时间
dl, ok := ctx.Deadline()
timeout := time.Now().Add(time.Second * 3)
if ok := dl.Before(timeout); ok {
timeout = dl
}
服务间超时传递主要是指 RPC 调用时候的超时传递,对于 gRPC 来说并不需要要我们做额外的处理,gRPC 本身就支持超时传递,原理和上面差不多,是通过 metadata 进行传递,最终会被转化为 grpc-timeout 的值,如下代码所示 grpc-go/internal/transport/handler_server.go:79
if v := r.Header.Get("grpc-timeout"); v != "" {
to, err := decodeTimeout(v)
if err != nil {
return nil, status.Errorf(codes.Internal, "malformed time-out: %v", err)
}
st.timeoutSet = true
st.timeout = to
}
超时传递是保证服务稳定性的一道重要防线,原理和实现都非常简单,你们的框架中实现了超时传递了吗?如果没有的话就赶紧动起手来吧。
go-zero 中的超时传递
go-zero 中可以通过配置文件中的 Timeout
配置 api gateway
和 rpc
服务的超时,并且会在服务间自动传递。
之前的 一文搞懂如何实现 Go 超时控制 里面有讲解超时控制如何使用。
参考
《SRE:Google运维解密》
项目地址
https://github.com/zeromicro/go-zero
欢迎使用 go-zero
并 star/fork 支持我们!
微信交流群
关注『微服务实践』公众号并点击 交流群 获取社区群二维码。
以上是关于快速了解什么是:微服务的主要内容,如果未能解决你的问题,请参考以下文章