关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 算法设计 »采用断路器设计模式来保护软件

    采用断路器设计模式来保护软件

    2015-08-04 00:00:00 出处:ImportNew
    分享

    程序员的人生就像在一个快车道上行驶。几周甚至几小时完成某些特性编码,打包测试没有问题,盖上QA认证,代码部署到生产环境。接着最坏的事情发生了,部署的软件在运行中挂掉了。用墨菲法则来说,就是“会出错的,终将出错”。但是,如果我们写代码时就能考虑到这些情况会怎样?

    那么我们应对不好的事情并将其转变为好的事情呢?

    采用断路器设计模式来保护软件

    电子技术拯救了我们

    我至今记得我和我哥因为电涌不得不更换家里的保险丝情景,那时我甚至对事件的严重程度一无所知,而他却是电力方面的小能手。保险丝完全烧坏了,但它却保护了我家的电视机。在电子工程领域,保险丝和断路器用(Circuit Breaker)来处理这样的问题,超大功率可能带来一些严重的破坏,毁坏电子设备甚至烧掉整个屋子。保险丝包含一个小电线丝,电量过大时就会融化,就像烧掉的电灯泡,阻止危险的电流,保障电器和房屋安全。

    保险丝演变成断路器,通常利用电磁铁就可以断开电路,而不用烧掉它,这样断路器就可以重置反复地用。不过,它们的功能都是一样的,检测负载,接着迅速停止工作,保全其它部分不受破坏。

    回过头再想,这是一个多么神奇的概念。仅仅坏掉某个控件——保险丝彻底坏掉,就可以避免了整个系统严重的损坏。多亏电涌后保险丝自熔,保住了电视机。那么为何我们不能在软件里面做同样的事情?坏事发生后,软件中的某个控件会迅速停止工作。模仿现实生活中的场景,我们创造了断路器设计模式。

    在分布式系统中,某些故障是短暂的,通过快速连续重试就可以解决问题;但在某些场景中,关键依赖的连接丢失了,短时间无法恢复。比如,某个应用失去了与云中的持续化存储连接。在这样的场景中,关闭服务就可以避免错误的数据处理过程、甚至数据丢失或者级联故障,进而防止对系统其它部分的进一步损坏。

    借助于迅速停止工作,运维部门就可以容易地进行监控和响应。在他们重视起来之前,那些徒劳尝试重新连接的服务看起来仍然是正常的,因为本应该拉响的警报没有响起。倘若某个服务在恰当的时候彻底失效,警告灯熄灭了,运维就会知晓问题所在,及时做出响应。

    断路器设计模式

    在系统中可重用基础架构实现断路器设计模式是很容易实现的,它是这么发挥作用的:

    1 定义一个可重用的CircuitBreaker类,包含Trip和Reset方法,以及断路器跳闸就可以调用的action
    2 利用CircuitBreaker去监控系统依赖。针对每个单一的故障,断路器跳闸就会将其设置在一种布防状态,就像电涌出现时那样。
    3 倘若接下来在特定的时间窗口内尝试成功,那么就重置此断路器,一切恢复正常。
    4 倘若断路器没有在特定的时间重置,异常会持续发生,此时断路器就会调用你提供的action。你可以在断路器跳闸时选择快速停止工作(终止进程)或者其他action。

    应用案例

    本例中ExternalServiceAdapter类帮助系统与外部依赖建立连接。或许有个网络程序产生请求频繁地执行DoStuff操作。一旦执行,若此时GetConnection执行出错,异常就会发生,断路器就会被跳闸。倘若连接重新建立起来,断路器就会被重置。不过连接异常持续发生时,断路器就会跳闸,特定的跳闸action就会执行,在本例中将会迅速停止工作。

    public class ExternalServiceAdapter
    {
        private CircuitBreaker circuitBreaker;
    
        public ExternalServiceAdapter()
        {
            circuitBreaker = new CircuitBreaker("CheckConnection", /*断路器名称 */
                exception => /* 一旦断路器跳闸此action就会被调用 */
                {
                    Console.WriteLine("Circuit breaker tripped! Fail fast!");
                    //终止进程,略过接下来的任何try/finally块或者finalizers
                    Environment.FailFast(exception.Message);
                },
            3, /* 断路器跳闸前的最大阈值*/
            TimeSpan.FromSeconds(2)); /* Time to wait between each try before attempting to trip the circuit breaker */
        }
    
        public void DoStuff()
        {
            var externalService = GetConnection();
            externalService.DoStuff();
        }
    
        ConnectionDependency GetConnection()
        {
            try
            {
                var newConnection = new ConnectionDependency();
                circuitBreaker.Reset();
                return newConnection;
            }
            catch (Exception exception)
            {
                circuitBreaker.Trip(exception);
                throw;
            }
        }
    }

    断路器模式简单实现

    using System;
    using System.Threading;
    
    public class CircuitBreaker 
    {
        public CircuitBreaker(string name, /*操作名称*/
            Action<Exception> tripAction, /* 一旦断路器跳闸action就会被调用*/
            int maxTimesToRetry, /* 断路器跳闸前重试的时间*/
            TimeSpan delayBetweenRetries /*每一次重试的时间间隔*/) 
        {
            this.name = name;
            this.tripAction = tripAction;
            this.maxTimesToRetry = maxTimesToRetry;
            this.delayBetweenRetries = delayBetweenRetries;
    
            // 一旦用户迫使断路器跳闸,计时器就会开启
            timer = new Timer(CircuitBreakerTripped, null, Timeout.Infinite, (int)delayBetweenRetries.TotalMilliseconds);
        }
    
        public void Reset()
        {
            var oldValue = Interlocked.Exchange(ref failureCount, 0);
            timer.Change(Timeout.Infinite, Timeout.Infinite);
            Console.WriteLine("The circuit breaker for {0} is now disarmed", name);
    
        }
    
        public void Trip(Exception ex)
        {
            lastException = ex;
            var newValue = Interlocked.Increment(ref failureCount);
    
            if (newValue == 1)
            {
                // 开启重试计时器. 
                timer.Change(delayBetweenRetries, TimeSpan.FromMilliseconds(-1));
    
                // 记录已触发的断路器.
                Console.WriteLine("The circuit breaker for {0} is now in the armed state", name);
            }
        }
    
        void CircuitBreakerTripped(object state)
        {
            Console.WriteLine("Check to see if we need to trip the circuit breaker. Retry:{0}", failureCount);
            if (Interlocked.Increment(ref failureCount) > maxTimesToRetry)
            {
                Console.WriteLine("The circuit breaker for {0} is now tripped. Calling specified action", name);
                tripAction(lastException);
                return;
            }
            timer.Change(delayBetweenRetries, TimeSpan.FromMilliseconds(-1));        
        }
    
        readonly string name;
        readonly int maxTimesToRetry;
        long failureCount;
        readonly Action<Exception> tripAction;
        Exception lastException;
        readonly TimeSpan delayBetweenRetries;
        readonly Timer timer;
    }

    断路器单元测试

    [TestFixture]
    public class CircuitBreakerTests
    {
        [Test]
        public void When_the_circuit_breaker_is_tripped_the_trip_action_is_called_after_reaching_max_threshold()
        {
            bool circuitBreakerTripActionCalled = false;
            var connectionException = new Exception("Something bad happened.");
    
            var  circuitBreaker = new CircuitBreaker("CheckServiceConnection", exception =>
            {
                Console.WriteLine("Circuit breaker tripped - fail fast");
                circuitBreakerTripActionCalled = true;
                // You would normally fail fast here in the action to faciliate the process shutdown by calling:
                // Environment.FailFast(connectionException.Message);
            }, 3, TimeSpan.FromSeconds(1));
    
            circuitBreaker.Trip(connectionException);
            System.Threading.Thread.Sleep(5000); 
            Assert.IsTrue(circuitBreakerTripActionCalled); 
        }
    
        [Test]
        public void When_the_circuit_breaker_is_reset_the_trip_action_is_not_called()
        {
            bool circuitBreakerTripActionCalled = false;
            var connectionException = new Exception("Something bad happened.");
    
            var circuitBreaker = new CircuitBreaker("CheckServiceConnection", exception =>
            {
                Console.WriteLine("Circuit breaker tripped - fail fast");
                circuitBreakerTripActionCalled = true;
                // You would normally fail fast here in the action to faciliate the process shutdown by calling:
                // Environment.FailFast(connectionException.Message);
            }, 3, TimeSpan.FromSeconds(2));
    
            circuitBreaker.Trip(connectionException);
            System.Threading.Thread.Sleep(1000); 
            circuitBreaker.Reset();
            Assert.False(circuitBreakerTripActionCalled);
        }
    }

    上面代码案例采用Console.WriteLine,你可以选择自己喜欢的logger。

    最后结语

    断路器是现代社会重要的组成部分,可以说是最重要的安全设备之一。不论是一个熔化的保险丝或者跳闸的断路器,其背后都有充足的理由。

    监控重要的资源,一旦它们无法响应,就迅速停止工作,进而确保整个运维团队做出正确的响应。

    如果你想对这些设计模式做进一步了解,请看Michael T. Nygard 的《Release It》,它是本相当不错的读物。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。