IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

关于柔性服务的一些实践和思考

Vimer 2011-01-12 23:09:07 累计浏览 2,416 次
本机暂存

最近花了大力气在做openapi的优化,使其尽量柔性可用,借此也有些想法想和大家分享一下。
柔性服务,google一下,在网上并没有这样一个标准的概念,所以应该是公司自己取的一个名字。但是这种概念,相信大家都应该很容易能明白,即:

最大程度的保证关键服务的可用性

通俗点来说,一个人不能走路了,他起码可以说话,不能说话了,起码可以点头,头都不能点了,起码得能活着,即心脏还在跳动。这就是柔性。
对应互联网服务来说就是要实现两点:

1.要尽可能成功返回关键数据
2.要尽可能正常接收请求,不能堵死

笔者总结了一下,只要CGI满足其中一个或几个特点,就可以考虑使用柔性服务:

1.在整个CGI的执行过程中,存在关键路径和非关键路径
2.CGI中存在循环调用接口,导致执行时间不确定

我们分上面两种特点来看:
对于第一种,我们举一个简单的例子,比如有一个CGI,做了两件事情分别是:

1.验证登录态
2.获取用户信息

很明显可以看出,验证登录态这个接口是关键路径,而获取用户信息这个接口是非关键的。所以按照柔性服务的定义,当获取用户信息接口失败时,起码还应该返回登录成功。
但是这个时候毕竟还是要区分出完全成功和部分成功的,所以我们可以定义返回码如下(目前腾讯社区开放平台的openapi就是如下定义):

ret==0:完全成功
0<ret<1000:部分成功
ret>=1000:完全失败

这样就很明白了,我们在调用获取个人信息接口失败是,返回ret=1即可。

但是这样是否足够了呢?
考虑一下,如果获取个人信息接口此时是超时,那么会导致整个CGI的返回变慢,从而导致接收进程挂死。此时虽然我们做了所谓的柔性服务,但是仍然是无法正常提供服务的。

怎么解决呢?我们需要一个能够动态调整超时时间的算法,当接口的响应时间远大于平常的平均值时,分配给接口的超时时间也要响应的调小。

伪编码如下:

以下是代码片段:
EMA g_CGITimeoutMng;//超时时间管理
 
class CCGI 
{
    public:
        CCGI ()
        {
            m_TrueRet=0;
            g_CGITimeoutMng.start();
        }
        virtual ~CCGI ()
        {
            g_CGITimeoutMng.stop();
        }
 
        int HandleInput()
        {
            int ret = check_login(g_CGITimeoutMng.remainTime());
            if (ret!=0)
            {
                return 1000;
            }
            ret = get_profile(g_CGITimeoutMng.remainTime());
            if (ret!=0)
            {
                m_TrueRet++;
            }
            return m_TrueRet;
        }
    private:
        int m_TrueRet;//容错返回码
};
 

那这里的动态调整算法使用什么呢?目前使用的是EMA来估算出下一时间点应当分配的超时时间,EMA一开始是用于股票的,后来公司的某大牛将其引入进来动态计算超时时间,大家可以google一下EMA的定义。

现在看来问题是解决了,但是这里会有一个比较大的限制,即:

关键路径和非关键路径要有明确的分割,即不可以颠倒顺序,或者互相穿插

但是万一就是有出现不符合这种限制的情况,该怎么办呢?其实也是有解决方法的,之前我们是只定义了一个g_CGITimeoutMng,他负责分配CGI的总处理时间,但是我们还可以再定义一个g_InterfaceTimeoutMng1,来动态调整某个接口的超时时间。
一般情况下我们仅需要对非关键接口加上这种单独的调整,并且取g_CGITimeoutMng.remainTime()和g_InterfaceTimeoutMng1.remainTime()中的较小值。

示例代码如下:

以下是代码片段:
EMA g_CGITimeoutMng;//超时时间管理 
EMA g_InterfaceTimeoutMng1;//超时时间管理 
class CCGI  
{ 
    public: 
        CCGI () 
        { 
            m_TrueRet=0; 
            g_CGITimeoutMng.start(); 
        } 
        virtual ~CCGI () 
        { 
            g_CGITimeoutMng.stop(); 
        } 
  
        int HandleInput() 
        { 
            int ret = check_login(g_CGITimeoutMng.remainTime()); 
            if (ret!=0) 
            { 
                return 1000; 
            } 
            g_InterfaceTimeoutMng1.start() 
            timeout = g_CGITimeoutMng.remainTime()<g_InterfaceTimeoutMng1.remainTime()?g_CGITimeoutMng.remainTime():g_InterfaceTimeoutMng1.remainTime() 
            ret = get_profile(timeout); 
            g_InterfaceTimeoutMng1.stop(); 
            if (ret!=0) 
            { 
                m_TrueRet++; 
            } 
            ret = check_right(g_CGITimeoutMng.remainTime()); 
            if (ret!=0) 
            { 
                return 1001; 
            } 
            return m_TrueRet; 
        } 
    private: 
        int m_TrueRet;//容错返回码 
};

这样的话,基本就可以完美解决了。

再回来说一下第二种,即有循环调用的问题,其实要做柔性的主要原因也是因为如果不计算使用的时间而直接容错的话,会导致时间很长。
示例代码如下:

以下是代码片段:
EMA g_CGITimeoutMng;//超时时间管理 
  
class CCGI  
{ 
    public: 
        CCGI () 
        { 
            m_TrueRet=0; 
            g_CGITimeoutMng.start(); 
        } 
        virtual ~CCGI () 
        { 
            g_CGITimeoutMng.stop(); 
        } 
  
        int HandleInput() 
        { 
            int ret; 
            for (i = 0; i < count; i++) 
            { 
                ret = get_somedata(g_CGITimeoutMng.remainTime()); 
                if (ret!=0) 
                { 
                    m_TrueRet++; 
                    continue; 
                } 
                //做取到数据之后该做的事情 
            } 
            return m_TrueRet; 
        } 
    private: 
        int m_TrueRet;//容错返回码 
};

PS:

以下是引用片段:
上面的代码在调用接口接口之前并没有判断g_CGITimeoutMng.remainTime()<=0,这是因为我们假定接口内部对timeout<=0都是直接返回错误的。如果不是这样的话,需要在调用接口前进行判断。
 
这样,服务才能做到真正的柔性可用。

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. 15个最好的免费开源电子商务平台 (累计阅读 12,541)
  2. 好的API设计 (累计阅读 12,394)
  3. Twitter/微博客的学习摘要 (累计阅读 12,258)
  4. 面试题 – 为什么我的朋友圈不见了? (累计阅读 11,951)
  5. Facebook 网站架构 (累计阅读 11,112)
  6. Feed架构-我们做错了什么 (累计阅读 8,732)
  7. 架构师给程序员的一封信 (累计阅读 7,986)
  8. 腾讯抄你肿么办 (累计阅读 7,755)
  9. Java技术路线 (累计阅读 7,725)
  10. 聊聊ThoughtWorks面试 (累计阅读 7,614)