1. 引言

在系统中,上游服务和下游服务是两个关键概念。上游服务通常指的是提供某种功能或数据的服务端,它接收来自下游服务的请求,并根据请求进行处理和响应。下游服务通常指的是发起请求并依赖上游服务的客户端,它们通过发送请求向上游服务请求数据或执行某些操作。

上游服务和下游服务之间的协作是系统中实现整体功能的关键。上游服务提供了核心的业务逻辑和数据,下游服务则依赖于上游服务来完成其特定任务。

下游服务的稳定性和可用性直接依赖于上游服务的可靠性和性能。如果上游服务不可用或出现故障,同时下游服务没有采取任何应对措施,此时将可能出现以下问题:

  1. 无法正常执行任务:下游服务可能无法执行其功能,因为它依赖于上游服务的数据或资源。这将导致下游服务无法正常工作。
  2. 延迟和性能下降:如果下游服务不对上游服务的不可用进行适当处理,而是无限期等待或不断尝试请求,系统的响应时间会增加,导致性能下降。
  3. 级联故障:如果下游服务继续发起大量的请求到不可用的上游服务,它可能会导致下游服务资源被全部占用,无法为其他请求提供服务。这可能导致级联故障,使整个系统不可用。

因此,下游服务应该采取适当的应对措施来处理上游服务不可用的情况,以确保系统的稳定性和可用性。

2. 情况分类

对于上游服务的不可用,此时可以区分为短暂不可用和长时间不可用两种情况,从而采用不同的方式来进行处理。

短暂不可用,是指上游服务在一段时间内暂时无法提供正常的服务,通常是由于网络波动或负载过高等原因导致的。这种情况下,上游服务可能会在很短的时间内自行恢复,并重新可用。短暂不可用通常持续时间较短,可以通过重试机制来处理。下游服务可以在请求失败后进行重试,直到上游服务恢复正常。

长时间不可用,是指上游服务在较长的时间内无法提供正常的服务,无法自行恢复或恢复时间较长。这种情况可能是由于严重的故障、系统崩溃,或其他长期性问题导致的。在这种情况下,简单地进行无限制的重试可能会导致系统被该请求全部占用,甚至引发级联故障。

短暂不可用和长时间不可用是上游服务不可用的两种常见情况,它们需要不同的应对策略。了解并区分这两种情况对于确保系统的稳定性和可用性至关重要。

3. 短暂不可用

3.1 处理方式

短暂的不可用可能只是临时性的,可能是网络拥塞或其他暂时性问题导致的。此时可以通过重试机制,下游服务可以尝试重新发送请求,增加请求成功的机会,避免由于系统的短暂不可用导致请求的失败。

3.2 代码示例

重试机制在许多客户端库中已经成为标准功能。这些客户端库提供了一些配置选项,以控制重试行为。下面是一个使用gRPC的示例代码,展示了如何配置和使用重试机制,然后基于此避免由于系统的短暂不可用导致请求的失败。

首先展示服务端代码,用于向外提供服务:

package mainimport ("context""log""net"pb "path/to/your/protobuf""google.golang.org/grpc"
)type server struct {pb.UnimplementedYourServiceServer
}func (s *server) YourRPCMethod(ctx context.Context, req *pb.Request) (*pb.Response, error) {// 处理请求逻辑return &pb.Response{Message: "Hello, world!"}, nil
}func main() {listener, err := net.Listen("tcp", ":50051")if err != nil {log.Fatalf("Failed to listen: %v", err)}srv := grpc.NewServer()pb.RegisterYourServiceServer(srv, &server{})log.Println("Server started")if err := srv.Serve(listener); err != nil {log.Fatalf("Failed to serve: %v", err)}
}

接下来展示客户端代码,其在服务端出现错误时,会进行重试,避免由于服务的短暂不可用,导致请求的失败,保证系统的可用性:

package mainimport ("context""log""time"pb "path/to/your/protobuf" "google.golang.org/grpc"
)func main() {conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())if err != nil {log.Fatalf("Failed to connect: %v", err)}defer conn.Close()client := pb.NewYourServiceClient(conn)// 设置重试次数和超时时间retryOptions := []grpc.CallOption{grpc.WithRetryMax(3),           // 最大重试次数grpc.WithTimeout(5 * time.Second), // 超时时间}// 发起 gRPC 请求(带重试和超时时间)ctx := context.Background()response, err := client.YourRPCMethod(ctx, &pb.Request{}, retryOptions...)if err != nil {log.Fatalf("Failed to make request: %v", err)}log.Println("Response:", response.Message)
}

在客户端代码中,我们通过 grpc.WithRetryMax 设置最大重试次数,通过 grpc.WithTimeout 设置超时时间。

这样,当遇到服务端短暂不可用时,客户端将进行最多 3 次的重试,并在每次重试时设置 5 秒的超时时间。能够让上游服务在短暂不可用情况下,仍然能够最大限度得保证下游服务请求的成功。

3.3 最佳实践

但是并非当请求失败时,便不断进行重试,直到请求成功,需要设置适当的重试策略和超时机制。避免由于大量的重试导致下游服务压力变大,从而导致服务从短暂不可用转变成了长时间不可用。

首先确定最大重试次数,根据系统需求和上游服务的特性,确定最大重试次数。避免无限制地进行重试,因为过多的重试可能会给上游服务造成过大的负担。通常,3-5次重试是一个常见的值,但具体数字应根据实际情况进行调整。

其次设置合理的超时时间,为了避免下游服务长时间等待不可用的上游服务,设置合理的请求超时时间是很重要的。超时时间应根据上游服务的预期响应时间和网络延迟进行调整。一般来说,超时时间应该在几秒钟的范围内,以确保及时地放弃不可用的请求并进行后续处理。

4. 长时间不可用

4.1 会导致的问题

长时间不可用的情况可能导致严重的问题,其中包括系统资源被全部占用和级联故障的风险。

首先是上游服务可能直接崩溃。当上游服务面临大量请求失败时,如果下游服务仍然持续地发起请求并无脑重试,此时随着请求数的增加,系统的资源(例如线程、内存、连接等)将被耗尽,导致系统无法处理其他的请求。结果是整个系统变得不可响应,甚至崩溃。

其次是级联故障的风险。长时间不可用的上游服务可能引发级联故障。在分布式系统中,不同的服务通常相互依赖和交互,上游服务的不可用性可能会影响到下游服务。如果下游服务在遇到上游服务不可用时不具备合适的应对机制,它们可能会继续尝试发送请求,造成资源浪费和堆积。这样的情况可能导致下游服务也不可用,甚至影响到更多的依赖系统,最终导致整个系统的级联故障。

所以,我们在设计系统时,下游服务在一些关键节点应该采取适当的应对措施来处理上游服务不可用的情况,以确保系统的稳定性和可用性。

4.2 处理方式

当上游服务长时间不可用时,下游服务需要采取一些措施来处理这个问题,以避免系统资源被全部占用和防止级联故障的发生。有两种常见的处理方式:引入熔断器和数据降级。

首先可以引入熔断器:熔断器是一种用于监控上游服务可用性并在需要时进行熔断的机制。当上游服务长时间不可用时,熔断器会直接熔断下游服务对上游服务的请求,而不是无脑地继续发送请求。这样可以避免下游服务持续占用系统资源,并且防止级联故障的发生。

熔断器通常有三个状态:关闭状态、打开状态和半开状态。在关闭状态下,请求会正常通过;当错误率达到一定阈值时,熔断器会打开,所有请求都会被熔断;在一段时间后,熔断器会进入半开状态,允许部分请求通过,以检查上游服务是否已经恢复正常。通过熔断器的状态转换,下游服务可以更好地适应上游服务的不可用情况。

其次是进行数据缓存与降级,如果上游服务提供的数据不是实时性要求很高,下游服务可以考虑在上游服务可用时缓存一部分数据。当上游服务不可用时,下游服务可以使用已经缓存的数据作为降级策略,确保系统的正常运行。这样可以减少对上游服务的依赖,避免系统资源被全部占用。

两种常见的方式,可以帮助下游服务在上游服务长时间不可用的情况下保持稳定性,并避免系统资源被全部占用和级联故障的发生。

4.3 代码示例

下面展示一个示例代码,展示下游服务向上游服务发送请求,并实现熔断机制和缓存降级操作,从而在上游服务不可用时,保证下游服务的稳定性:

package mainimport ("fmt""sync""time""github.com/sony/gobreaker"
)var (circuitBreaker *gobreaker.CircuitBreakercache          map[string]stringcacheMutex     sync.RWMutex
)func main() {// 初始化熔断器circuitBreaker = gobreaker.NewCircuitBreaker(gobreaker.Settings{Name:        "MyCircuitBreaker",MaxRequests: 3,                  // 最大请求数Interval:    5 * time.Second,    // 统计时间窗口Timeout:     1 * time.Second,    // 超时时间ReadyToTrip: customTripFunc,     // 自定义熔断判断函数})// 初始化缓存cache = make(map[string]string)// 模拟下游服务发送请求for i := 0; i < 10; i++ {response, err := makeRequest("https://baidu.com")if err != nil {fmt.Println("Request failed:", err)} else {fmt.Println("Response:", response)}time.Sleep(1 * time.Second) // 间隔一段时间再次发送请求}
}// 发送请求的函数
func makeRequest(url string) (string, error) {// 使用熔断器包装请求函数response, err := circuitBreaker.Execute(func() (interface{}, error) {// 假设向上游服务发送 HTTP 请求// ...// 返回响应数据或错误return "Response from  service", nil})if err != nil {// 请求失败,使用缓存降级cacheMutex.RLock()cachedResponse, ok := cache[url]cacheMutex.RUnlock()if ok {return cachedResponse, nil}return "", err}// 请求成功,更新缓存cacheMutex.Lock()cache[url] = response.(string)cacheMutex.Unlock()return response.(string), nil
}// 自定义熔断判断函数,根据实际情况进行判断
func customTripFunc(counts gobreaker.Counts) bool {// 根据失败次数或其他指标来判断是否触发熔断return counts.ConsecutiveFailures > 3
}

上述代码示例中,使用了gobreaker开源项目实现了熔断机制,并使用本地缓存实现了缓存降级操作。

在发送请求的函数makeRequest中,使用熔断器包装请求函数,在请求函数内部实际发送请求给上游服务。如果请求成功,则更新缓存,并返回响应。

如果请求失败,则再次尝试从缓存中获取数据,如果缓存中存在,则返回缓存的响应。如果缓存中也不存在数据,则返回错误。

这样,下游服务可以在上游服务不可用的情况下,通过缓存提供一定的降级功能,避免对上游服务进行频繁无效的请求。而熔断机制则可以在下游服务可用性不高时,直接熔断请求,减少对上游服务的负载,提高整体系统的稳定性。

5. 总结

本文主要介绍了上游服务不可用时,下游服务的应对措施。主要分为短暂不可用和长时间不可用两种情况。

短暂不可用通常由网络波动或其他暂时性问题导致。在这种情况下,可以采用重试机制来成功请求上游服务,确保资源的可用性。重试机制是一种简单而有效的方法,通过多次重试请求,以应对短暂的不可用情况,避免下游服务受到影响。

长时间不可用可能导致严重的问题,如上游服务崩溃或级联故障。为了应对这种情况,可以引入熔断器保护机制来确保下游服务的稳定性。熔断器能够快速切断对不可用的上游服务的请求,避免系统资源被全部占用。此外,根据具体服务的特性,可以考虑使用缓存降级来处理长时间不可用的情况。通过缓存上游服务的响应数据,即使上游服务不可用,下游服务仍可以从缓存中获取数据,保持服务的可用性。

综上所述,重试机制和熔断器是应对上游服务不可用的关键措施。重试机制适用于短暂不可用情况,而熔断器则用于长时间不可用的情况下保护下游服务。此外,根据具体情况,采用缓存降级等策略可以进一步提升服务的稳定性和可用性。

上游服务不可用了,下游服务如何应对?相关推荐

  1. 详细解读503服务不可用的错误以及如何解决503服务不可用

    文章目录 1. 问题引言 2. 什么是503服务不可用错误 3 尝试解决问题 3.1 重新加载页面 3.2 检查该站点是否为其他人关闭 3.3 重新启动设备 3.3 联系网站 4. 其他解决问的方法 ...

  2. 【微服务】Go进阶② 微服务可用性设计

    文章目录 微服务可用性设计 隔离 核心隔离 快慢隔离 热点隔离 线程隔离 进程隔离 集群隔离 超时控制 双峰分布 超时原因 超时控制中间件 过载保护 常见限流的缺点 过载保护策略 如何计算接近峰值时的 ...

  3. 学习笔记:带你十天轻松完成 Go 微服务系列(二)- 服务拆分

    学习笔记:带你十天轻松搞定 Go 微服务系列(二) 1.学习课程 2.服务拆分 2.1 按业务服务拆分 2.2 按调用方式拆分 3.创建项目目录 3.1 在 code 中新建项目 3.2 创建 mal ...

  4. 商城项目01 _电商系统基本模式、分布式基础概念、微服务架构图、微服务划分图

    文章目录 ①. 电商系统基本模式 ②. 分布式基础概念 ③. 微服务架构图详解 ④. 微服务划分图 ①. 电商系统基本模式 ①. B2C模式 就是我们经常看到的供应商直接把商品卖给用户,即" ...

  5. 服务不可用怎么排查?讲了100遍还是记不住?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 下面是线上机器的cpu使用率,可以看到从4月8日开始,随着 ...

  6. ChaosBlade x SkyWalking 微服务高可用实践

    来源|阿里巴巴云原生公众号 前言 在分布式系统架构下,服务组件繁多且服务间的依赖错综复杂,很难评估单个故障对整个系统的影响,而且请求链路长,如果监控告警.日志记录等基础服务不完善会造成故障响应.故障定 ...

  7. 微服务高可用之熔断器实现原理与 Golang 实践

    I.内容提要 在微服务架构中,经常会碰到服务超时或通讯失败的问题,由于服务间层层依赖,很可能由于某个服务出现问题,不合理的重试和超时设置,导致问题层层传递引发雪崩现象,而限流和熔断是解决这个问题重要的 ...

  8. “服务不可用“怎么排查?

    下面是线上机器的cpu使用率,可以看到从4月8日开始,随着时间cpu使用率在逐步增高,最终使用率达到100%导致线上服务不可用,后面重启了机器后恢复. 1.排查思路 简单分析下可能出问题的地方,分为5 ...

  9. 几个预防并发搞垮下游服务的方法

    前言 上一篇文章 我用休眠做并发控制,搞垮了下游服务 发出去后得到不少网友的回应,有人问自己平时用的方案行不行,有人建议借鉴TCP的拥塞控制策略,动态地调整发起的并发数,还有人问为啥我要管下游抗不抗得 ...

最新文章

  1. Java三大主流开源工作流引擎技术分析
  2. 切诺夫界证明(Chernoff bound)
  3. XCode 4.2(4.1)真机调试及生成IPA全攻略
  4. Sqlserver内存管理:限制最大占用内存(转载)
  5. Windows编程—获取操作系统默认浏览器路径
  6. 金立手机又推新品!翻盖手机A326发布 意图复刻经典
  7. 嵌入式 C 语言(上)
  8. Python 开发 利用SQLmap API接口进行批量的SQL注入检测.(SRC挖掘)
  9. Python-Selennium之爬虫实战--链家二手房爬虫项目
  10. HealthKit 的新增功能
  11. 项目经理面试的一些问题讨论
  12. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  13. echarts自定义主题构建
  14. Java自定义导出列_后台生成EXCEL文档,自定义列
  15. WIN10 JDK下载及安装说明
  16. GCN变体-graphSAGE
  17. WPAN(无线个域网)和WLAN(无线局域网)的区别
  18. 翁刚c语言,每小题3分,共计12分。  ——青夏教育精英家教网——
  19. 工欲善其事,必先利其器——NHibernate
  20. Flutter安卓系统把状态栏设置为透明色

热门文章

  1. 虚拟机设置了桥连模式无法上网(电脑wifi上网)
  2. iOS 高性能置灰方案
  3. 单目三维重建学习笔记2023
  4. 【疯狂世界杯】css 动画实现跳动的足球
  5. Mybatis的Executor介绍
  6. 京东承认刘强东涉案!
  7. 一加android 6.0壁纸,一加手机2即将升级至Android 6.0:氧OS 3.0截图曝光
  8. python海龟图画龙珠_阿里云天池龙珠计划——Python基础入门第2课:变量和赋值...
  9. 从零开始自制实现WebServer(十六)---- 学习新工具CMake自动编写MakeFile 分门别类整理源文件心情愉悦
  10. git 删除分支失败