上游服务不可用了,下游服务如何应对?
上游服务不可用了,下游服务如何应对?
引言
- 在系统中,上游服务和下游服务是两个关键概念。上游服务通常指的是提供某种功能或数据的服务端,它接收来自下游服务的请求,并根据请求进行处理和响应。下游服务通常指的是发起请求并依赖上游服务的客户端,它们通过发送请求向上游服务请求数据或执行某些操作。
- 上游服务和下游服务之间的协作是系统中实现整体功能的关键。上游服务提供了核心的业务逻辑和数据,下游服务则依赖于上游服务来完成其特定任务。
- 下游服务的稳定性和可用性直接依赖于上游服务的可靠性和性能。如果上游服务不可用或出现故障,同时下游服务没有采取任何应对措施,此时将可能出现以下问题:
- 无法正常执行任务:下游服务可能无法执行其功能,因为它依赖于上游服务的数据或资源。这将导致下游服务无法正常工作。
- 延迟和性能下降:如果下游服务不对上游服务的不可用进行适当处理,而是无限期等待或不断尝试请求,系统的响应时间会增加,导致性能下降。
- 级联故障:如果下游服务继续发起大量的请求到不可用的上游服务,它可能会导致下游服务资源被全部占用,无法为其他请求提供服务。这可能导致级联故障,使整个系统不可用。
- 因此,下游服务应该采取适当的应对措施来处理上游服务不可用的情况,以确保系统的稳定性和可用性。
情况分类
- 对于上游服务的不可用,此时可以区分为短暂不可用和长时间不可用两种情况,从而采用不同的方式来进行处理。
- 短暂不可用,是指上游服务在一段时间内暂时无法提供正常的服务,通常是由于网络波动或负载过高等原因导致的。这种情况下,上游服务可能会在很短的时间内自行恢复,并重新可用。短暂不可用通常持续时间较短,可以通过重试机制来处理。下游服务可以在请求失败后进行重试,直到上游服务恢复正常。
- 长时间不可用,是指上游服务在较长的时间内无法提供正常的服务,无法自行恢复或恢复时间较长。这种情况可能是由于严重的故障、系统崩溃,或其他长期性问题导致的。在这种情况下,简单地进行无限制的重试可能会导致系统被该请求全部占用,甚至引发级联故障。
- 短暂不可用和长时间不可用是上游服务不可用的两种常见情况,它们需要不同的应对策略。了解并区分这两种情况对于确保系统的稳定性和可用性至关重要。
短暂不可用
处理方式
- 短暂的不可用可能只是临时性的,可能是网络拥塞或其他暂时性问题导致的。此时可以通过重试机制,下游服务可以尝试重新发送请求,增加请求成功的机会,避免由于系统的短暂不可用导致请求的失败。
代码示例
-
重试机制在许多客户端库中已经成为标准功能。这些客户端库提供了一些配置选项,以控制重试行为。下面是一个使用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-5次重试是一个常见的值,但具体数字应根据实际情况进行调整。
- 其次设置合理的超时时间,为了避免下游服务长时间等待不可用的上游服务,设置合理的请求超时时间是很重要的。超时时间应根据上游服务的预期响应时间和网络延迟进行调整。一般来说,超时时间应该在几秒钟的范围内,以确保及时地放弃不可用的请求并进行后续处理。
长时间不可用
会导致的问题
- 长时间不可用的情况可能导致严重的问题,其中包括系统资源被全部占用和级联故障的风险。
- 首先是上游服务可能直接崩溃。当上游服务面临大量请求失败时,如果下游服务仍然持续地发起请求并无脑重试,此时随着请求数的增加,系统的资源(例如线程、内存、连接等)将被耗尽,导致系统无法处理其他的请求。结果是整个系统变得不可响应,甚至崩溃。
- 其次是级联故障的风险。长时间不可用的上游服务可能引发级联故障。在分布式系统中,不同的服务通常相互依赖和交互,上游服务的不可用性可能会影响到下游服务。如果下游服务在遇到上游服务不可用时不具备合适的应对机制,它们可能会继续尝试发送请求,造成资源浪费和堆积。这样的情况可能导致下游服务也不可用,甚至影响到更多的依赖系统,最终导致整个系统的级联故障。
- 所以,我们在设计系统时,下游服务在一些关键节点应该采取适当的应对措施来处理上游服务不可用的情况,以确保系统的稳定性和可用性。
处理方式
- 当上游服务长时间不可用时,下游服务需要采取一些措施来处理这个问题,以避免系统资源被全部占用和防止级联故障的发生。有两种常见的处理方式:引入熔断器和数据降级。
- 首先可以引入熔断器:熔断器是一种用于监控上游服务可用性并在需要时进行熔断的机制。当上游服务长时间不可用时,熔断器会直接熔断下游服务对上游服务的请求,而不是无脑地继续发送请求。这样可以避免下游服务持续占用系统资源,并且防止级联故障的发生。
- 熔断器通常有三个状态:关闭状态、打开状态和半开状态。在关闭状态下,请求会正常通过;当错误率达到一定阈值时,熔断器会打开,所有请求都会被熔断;在一段时间后,熔断器会进入半开状态,允许部分请求通过,以检查上游服务是否已经恢复正常。通过熔断器的状态转换,下游服务可以更好地适应上游服务的不可用情况。
- 其次是进行数据缓存与降级,如果上游服务提供的数据不是实时性要求很高,下游服务可以考虑在上游服务可用时缓存一部分数据。当上游服务不可用时,下游服务可以使用已经缓存的数据作为降级策略,确保系统的正常运行。这样可以减少对上游服务的依赖,避免系统资源被全部占用。
- 两种常见的方式,可以帮助下游服务在上游服务长时间不可用的情况下保持稳定性,并避免系统资源被全部占用和级联故障的发生。
代码示例
-
下面展示一个示例代码,展示下游服务向上游服务发送请求,并实现熔断机制和缓存降级操作,从而在上游服务不可用时,保证下游服务的稳定性:
-
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中,使用熔断器包装请求函数,在请求函数内部实际发送请求给上游服务。如果请求成功,则更新缓存,并返回响应。
-
如果请求失败,则再次尝试从缓存中获取数据,如果缓存中存在,则返回缓存的响应。如果缓存中也不存在数据,则返回错误。
-
这样,下游服务可以在上游服务不可用的情况下,通过缓存提供一定的降级功能,避免对上游服务进行频繁无效的请求。而熔断机制则可以在下游服务可用性不高时,直接熔断请求,减少对上游服务的负载,提高整体系统的稳定性。
总结
- 本文主要介绍了上游服务不可用时,下游服务的应对措施。主要分为短暂不可用和长时间不可用两种情况。
- 短暂不可用通常由网络波动或其他暂时性问题导致。在这种情况下,可以采用重试机制来成功请求上游服务,确保资源的可用性。重试机制是一种简单而有效的方法,通过多次重试请求,以应对短暂的不可用情况,避免下游服务受到影响。
- 长时间不可用可能导致严重的问题,如上游服务崩溃或级联故障。为了应对这种情况,可以引入熔断器保护机制来确保下游服务的稳定性。熔断器能够快速切断对不可用的上游服务的请求,避免系统资源被全部占用。此外,根据具体服务的特性,可以考虑使用缓存降级来处理长时间不可用的情况。通过缓存上游服务的响应数据,即使上游服务不可用,下游服务仍可以从缓存中获取数据,保持服务的可用性。
- 综上所述,重试机制和熔断器是应对上游服务不可用的关键措施。重试机制适用于短暂不可用情况,而熔断器则用于长时间不可用的情况下保护下游服务。此外,根据具体情况,采用缓存降级等策略可以进一步提升服务的稳定性和可用性。
-----------------------------------------------------------------------------------
offer突击训练营简介:
1:针对不知道怎么面试,面试没有信心的小伙伴,我们会给你一个offer保障。
2:我们会监督你15-20天内把面试体系技术点掌握至少7成,这样足够你去找到满意的工作了。
3:我们是面向面试学习指导,不会带你们去写代码,会把项目真实开发的迭代过程和技术细节如何实现业务功能都详细教清楚,你能在面试中流畅表达清楚就行了,项目经验你不用担心(技术老师提供的真实项目经验肯定拿的出手),自己学和别人带着系统学,效率完全不一样。
详情请点击这里:offer突击训练营,给你一个offer的保障,求职跳槽的看过来!
相关文章:

上游服务不可用了,下游服务如何应对?
上游服务不可用了,下游服务如何应对? 引言 在系统中,上游服务和下游服务是两个关键概念。上游服务通常指的是提供某种功能或数据的服务端,它接收来自下游服务的请求,并根据请求进行处理和响应。下游服务通常指的是发…...

WebGL笔记:矩阵的变换之平移的实现
矩阵的变换 变换 变换有三种状态:平移、旋转、缩放。当我们变换一个图形时,实际上就是在移动这个图形的所有顶点。解释 webgl 要绘图的话,它是先定顶点的,就比如说我要画个三角形,那它会先把这三角形的三个顶点定出来…...

XTU-OJ 1187-Candy
WCB某天买了非常多的糖果并把它们分成N份,依次分别有1,2,3…,N个糖果。他想拿出其中的3份分给他的室友, 为了不让室友们闹意见,必须让这三份的糖果总数恰好能被三人均分。请问他一共有多少种不同的组合方案数ÿ…...

基于 nodejs+vue城市轨道交通线路查询系统mysql
目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…...

电商时代,VR全景如何解决实体店难做没流量?
近日,电商和实体经济的对立成为了热门话题,尽管电商的兴起确实对线下实体店造成了一定的冲击,但实体店也不是没有办法挽救。VR全景助力线下实体店打造线上店铺,打通流量全域布局,还能实现打开产品、查看产品内部细节等…...

操作系统-浅谈CPU与内存
目录 计算机的基本组成CPU内存虚拟内存内存分段内存分页 CPU与内存的交互过程高速缓存cache 所有图片均来自:小林coding 计算机的基本组成 计算机由软件和硬件组成 硬件由CPU(中央处理器)存储器(内存外存)外部设备组成。 软件由应用软件和系…...

K8s 部署 CNI 网络组件+k8s 多master集群部署+负载均衡
------------------------------ 部署 CNI 网络组件 ------------------------------ ---------- 部署 flannel ---------- K8S 中 Pod 网络通信: ●Pod 内容器与容器之间的通信 在同一个 Pod 内的容器(Pod 内的容器是不会跨宿主机的)共享同一…...

若依微服务上传图片文件代理配置
在使用若依微服务文件上传时候,文件上传成功会上传到D:/ruoyi/uploadPath目录下。默认使用9300端口进行访问图片文件,现在我想把它代理到80端口应该怎么做呢? 配置前:http://localhost:9300/statics/2023/09/24/test.jpg 配置后:http://localhost/statics/2023/09/24/test…...

物联网与 Linux 的相爱相生
Linux 无疑将在物联网中扮演一个关键角色,但是其光彩将与其它的一些分享。 随着 Canonical 重新关注于赢利和新技术,我们中的一些人发现我们正在思考 Linux 未来将走向何方,IoT(物联网)是否是 Linux 的未来࿱…...

python自动化测试(一):操作浏览器
通过Python的代码去操作浏览器的操作 目录 目录 1、导入自动化模块 2、定义打开的浏览器驱动、声明一个url变量保存打开的地址 3、使用函数:driver.get(url)打开浏览器的指定页面 4、最大化浏览器窗口:driver.maximize_window() 5、添加全局的等待…...

NReco.LambdaParser使用案例
使用案例集合: private async void RuleEngine_Click(object sender, EventArgs e){#region 获取变量string expression this.Rule.Text.Trim();string pattern "\$(.*?)\$";MatchCollection matches Regex.Matches(expression, pattern);foreach (Ma…...

苹果IOS安装IPA, plist形式 Safari 浏览器点击安装
快速链接 苹果开发者账号链接 网址: https://developer.apple.com/account 苹果应用上架链接 网址: https://appstoreconnect.apple.com/ 应用证书文件及打包 参考教程: 最新uniapp打包IOS详细步骤(2022) 证书在线制作工具 网址: https://app.121xuexi.…...

Django 注册及创建订单商品
注册功能的实现 user/views from rest_framework.generics import GenericAPIView from rest_framework.views import APIViewfrom apps.user.models import User from apps.user.serializers import UserSerializer from utils import ResponseMessage from utils.jwt_auth …...

15、Python -- 阶段总结:变量与流程控制
目录 变量变量没有类型,数据有类型 表达式程序流程 变量 变量:编程的本质就是处理数据,数据需要用变量保存 Python语言的特征: 所有变量无需声明,即可使用 变量没有类型 变量没有类型,数据有类型 已学过…...

信息检索与数据挖掘 | 【实验】排名检索模型
文章目录 📚实验内容📚相关概念📚实验步骤🐇分词预处理🐇构建倒排索引表🐇计算query和各个文档的相似度🐇queries预处理及检索函数🔥对输入的文本进行词法分析和标准化处理…...

玩转AIGC:打造令人印象深刻的AI对话Prompt
玩转AIGC:打造令人印象深刻的AI对话Prompt 《玩转AIGC:打造令人印象深刻的AI对话Prompt》摘要引言正文良好的Prompt:引发AI深度交流的法宝 ✨探讨不同的提问方式1. 常规提问2. 创意提问 对话交流的艺术:倾听与引导的巧妙平衡 ⚖️…...

uniapp vue国际化 i18n
一、安装 vue-i18n npm i vue-i18n 二、新建i18n目录 1、en.json 内容 {"loginPage":{"namePh":"Please enter your login account","passwordPh":"Please enter password"} } 2、zh-CN.json 内容 {"loginPage&qu…...

Docker 启动远程服务访问不了
今天一下午在弄这个 1、防火墙是否关了 firewall-cmd --state2、ip转发开没开 sysctl net.ipv4.ip_forward3、service iptables是不是打开并拦截了 4、检查docker启动的端口号是否一致,或者启动时对不对 5、检查docker的服务是否起来了,比如你的端口号…...

[ACTF2020 新生赛]Exec
【解题过程】 1.打开链接 得到一个能ping 的网站,可以推测这个可以在终端运行的网站。 2.解题思路 在执行的时候我们可以想到命令执行的“;”分号的作用:命令用分号分隔开来,表示它们是两个独立的命令,需要依次执行。…...

Git(三).git 文件夹详解
目录 一、初始化新仓库二、.git 目录2.1 hooks 文件夹2.2 info 文件夹2.3 logs 文件夹2.4 objects 文件夹【重要】2.5 refs 文件夹【重要】2.6 COMMIT_EDITMSG2.7 config2.8 description2.9 FETCH_HEAD2.10 HEAD【重要】2.11 index【重要】2.12 ORIG_HEAD2.13 packed-refs 官网…...

esp32-S3 + visual studio code 开发环境搭建
一、首先在下面链接网页中下载esp-idf v5.1.1离线安装包 ,并安装到指定位置。dl.espressif.cn/dl/esp-idf/https://dl.espressif.cn/dl/esp-idf/ 安装过程中会提示需要长路径支持,所以windows系统需要开启长路径使能 Step 1: 打开运行&…...

4.1 网络基础之网络IO
一、编写基本服务程序流程 1、创建套接字 #include <sys/types.h> #include <sys/socket.h>int socket(int domain, int type, int protocol);/* * 参数domain通讯协议族: * PF_INET IPv4互联网协议族(常用) * PF_INET6 …...

[100天算法】-和为 K 的子数组(day 39)
题目描述 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。示例 1 :输入:nums [1,1,1], k 2 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 说明 :数组的长度为 [1, 20,000]。 数组中元素的范围是 [-1000, 1000] ,且整…...

Leo赠书活动-02期 【信息科技风险管理:合规管理、技术防控与数字化】
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 赠书活动专栏 ✨特色专栏:…...

L2-026 小字辈 - java
L2-026 小字辈 时间限制 400 ms 内存限制 64 MB 题目描述: 本题给定一个庞大家族的家谱,要请你给出最小一辈的名单。 输入格式: 输入在第一行给出家族人口总数 N(不超过 100 000 的正整数) —— 简单起见,…...

排序算法-堆积树排序法(HeapSort)
目录 排序算法-堆积树排序法(HeapSort) 1、说明 2、算法分析 3、C代码 排序算法-堆积树排序法(HeapSort) 1、说明 堆积树排序法是选择排序法的改进版,可以减少在选择排序法中的比较次数,进而减少排序…...

名词解释 MongoDB
MongoDB 是一个面向文档的数据库管理系统,它不使用传统的表格结构,而是将数据组织成类似文档的形式,通常使用JSON格式。 文档数据库:数据以文档的形式存储,每个文档可以包含不同的字段,就像一个文件可以包…...

Look Back(cf div3 905)
题意:给你一个长度为n((1≤n≤10^5)数组a[],你可以进行一个操作 使a[i]a[i]*2,问最少经过多少次这样的操作使的a[]不递减,a[i]>a[i-1]。 输入样例: 6 1 1 2 1 1 3 1 2 1 4 2 3 2 1 5 4 5 4 5 4 10 1 7 7 2 3 4 3 2 1 100 输出…...

Spring框架的发展历程
Spring框架的发展历程 自2004年以来,Spring框架已经成为Java开发人员最受欢迎的开源框架之一。它提供了一个全面的编程和配置模型,旨在简化企业级Java应用程序的开发过程。本文将详细介绍Spring框架的发展历程,以及它如何为Java开发人员提供…...

vue 级联查询5级--省/市/区/街道/社区
<template> <div> 1234 <el-select v-model="provinceVal" @change="selectProvinceFn" placeholder="请选择"> <el-option v-for="item in provinceList" :key="item.code" :label="item.name&q…...