微服务可用性-超时控制

超时控制

@2024/07/12

好久不见,甚是想念,今天我们来聊聊保证微服务架构高可用性的另一个手段,超时控制。超时控制的作用可以及时释放资源,提高系统资源的利用率,而且还能保证客户端在预期的时间内可以获得响应。而超时控制在微服务领域存在两种形态,第一种就是我们熟悉的客户端-服务端的超时控制,可以称之为端到端的超时控制,比如服务A需要调用服务B,如果调用服务B的时间超过了预期的时间,那么服务A就不会再管服务B是否完成了,而是释放连接,返回给用户一个超时的响应。第二种也是微服务领域比较常用的一种,链路超时控制,这种类型的超时控制视角变大了,保证的是一个业务的超时控制,比如一个下载订单的业务,需要通过用户服务A获取用户信息,判断他是否有权限查看订单数据,然后通过订单服务B获取订单数据,格式化数据,最后通过文件服务C将订单数据打包成一个文件返回给用户,这个业务经历了三个服务 A→B→C,链路超时控制指的就是这个调用链路的调用时间不能超过预期的值,超过了就必须中断业务,返回超时响应。这就是目前主流的超时控制思路,但是超时控制的时间如何设置才是超时控制中最重要落地的一部分,因为如果时间设置的过长,就会导致系统资源耗尽(线程资源,连接资源等)服务崩溃,设置过短会导致服务一直在频繁重试得不到真正的业务执行。所以合适的超时时间是非常重要的。那么有没有什么方法论可以帮助我们在确定超时时间这件事上有迹可循呢?答案是有的,我们回到超时控制的目标上来,超时控制的目标有两个,一个是保证系统的可用性,即及时释放资源,另一个就是保证客户端在预期的时间内获取反馈。所以我们可以根据用户的体验来设置超时时间。这种事情我们可以循环产品的设计者--产品经理,如果这种方法行不通,我们可以根据被调用服务的响应时间来设置超时时间,基本上以被调用服务响应时间的99线,或者999线为超时时间,这里补充一个点,99线,就是99%请求的响应时间都这这个时间内。999线类似。但是如果你没有被调服务的99线,就只能模拟线上环境,进行压力测试。然而很多公司并没有模拟好的线上环境,所以这个办法也有可能行不通,所以你需要估算被调服务的代码中的耗时操作的时间,比如被调服务A 进行了2次数据库连接,1次Redis访问,那么估算耗时时间为:数据库响应时间 X 2 + Redis响应时间 X 1。稍微小结一下,确定超时时间有四种办法,优先级逐级降低,复杂程度逐步升高

  1. 根据用户体验

  2. 根据被调服务响应时间

  3. 根据模拟线上环境的压力测试

  4. 根据被调服务的代码估算

确定了超时时间,接下来我们来思考一个问题,那就是在服务端执行业务的过程中超时了,我们是中断业务的执行呢?还是继续执行业务呢?按照我们认知的来说确实应该中断业务,因为毕竟这个响应超时了,但是如果要实现这一目标,尤其是在端到端的超时控制中,你不得不在业务手动判断当前这个请求是否处理超时了,据此来判断业务是否继续执行。而在链路超时控制中,由于业务被分为了几个步骤,我们可以在这几步中,进行超时校验来中断业务。链路超时控制中,最重要的事情就是传递超时时间,是传递过期时间段,还是传递过期的具体时间呢?比如超时时间是1s,现在是9:30:58,那么传递的过程中是传递timeout:1000,还是timeout: 9:30:59呢?两者都有利弊,先来说说传递时间段,传递时间段的问题在于,网络传输时间的不确定性,比如服务A调用服务B,在调用服务B之前,已经消耗了300ms,那么交给服务B的超时时间就只能为700ms了,当服务B接受到服务A的请求时,拿到的超时时间也不过是协议头中设置的700ms,没有了传输时间,所以我们需要根据大量的测试,来估算出这段传输时间。当然你也可以结合时间戳,比如在协议头中记录了调用服务B时的时间戳,然后当服务B接收到的时候,根据自己的时间减去协议头中记录的时间就可以得出传输耗费的时间,但是这个策略多此一举,与其这样,不如直接使用超时时间戳,还是这个例子,服务A调用服务B,当服务A接收到用户的请求时,根据这个业务的超时时间,记录超时时间戳,比如当前时间为9:30:40,这个业务的超时时间为1s,那么超时时间戳为9:30:41,将这个时间戳传递给服务B,服务B在执行业务的时候判断当前时间是否超过了这个时间戳就可以了。但是使用时间戳是存在问题的,即时钟回拨问题,简单而言就是两个服务之间的时间不一致,就会发生一种情况,明明超时了,服务B觉得没有超时,明明没超时,服务B觉得超时了。这就是我所知道的超时控制的全部内容,下一期我们讲远程调用。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
pandaer
作者分享
#AI应用开发 springAI是如何实现mcp的工具调用的,在springai中,工具调用被抽象成了一个toolcallback,无论是我们自己写的工具调用,还是mcp提供的,最终都会变成这个类的实例,然后再大模型返回的结果里,如果有工具调用,就会调用toolcallback的call方法,来执行真正的工具调用
4
#AI应用开发 今天学习了如何利用springAI来开发mcp server以及mcp client。开发mcp client很简单,只需要引入依赖,配置一下就好。开发一个mcp server也是一样 先要引入一个依赖,然后手动注入toolcallbackprovider这个bean就行了。需要利用tool注解来自定义需要对外提供的工具
4
#AI应用开发 今天学习了toolcall,本质还是给大模型提供上下文信息,让其生成调用。在传递给大模型的消息中有一个额外的字段叫做tools,里面定义了工具的声明。在模型的响应中有一个字段叫做tool_calls里面定义了要调用哪些工具。 工具的定义要清晰明了,参数结构尽可能的简单,不要试图在一次请求中拼装复杂的工具链。满足需求的情况下,越简单,越清晰越好
4
#AI应用开发 springAI实现短期记忆(会话内记忆)的实现是基于chatmemory来实现的,chatmemory这个接口专注于我要把那些消息交给大模型,而chatmemoryrepostory 这个接口专注的是会话中产生的消息怎么存? 存哪里? 是数据库还是内存?
4
#AI应用开发 springAI中 目前学习了两套api,一个是chatmodel,一个是chatclient。 chatmodel是封装了大模型提供商的chat请求,让我们可以很方便的调用模型,而不需要手写http请求。他提供了两个方法 一个是call,一个是stream 对应chat请求的两套模式,一个是非流式输出,一个是流式输出。所以这个更像是对大模型的抽象。 chatclient是基于chatmodel的封装,提供了更多的api,方便我们快速搭建相关的功能。 springAI 提供了prompt来抽象给chat的请求内容,不仅仅包含消息列表,还有一些模型配置,但是我感觉prompt这个名字不好,我宁愿叫chatrequest,也不叫prompt,因为一段小文本就是prompt
3
下载 APP