关闭 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》,它是本相当不错的读物。

    上一篇返回首页 下一篇

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

    别人在看

    帝国CMS7.5编辑器上传图片取消宽高的三种方法

    帝国cms如何自动生成缩略图的实现方法

    Windows 12即将到来,将彻底改变人机交互

    帝国CMS 7.5忘记登陆账号密码怎么办?可以phpmyadmin中重置管理员密码

    帝国CMS 7.5 后台编辑器换行,修改回车键br换行为p标签

    Windows 11 版本与 Windows 10比较,新功能一览

    Windows 11激活产品密钥收集及专业版激活方法

    如何从 Windows 11 中完全删除/卸载 OneNote?无解!

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    IT头条

    华为Pura80系列新机预热,余承东力赞其复杂光线下的视频拍摄实力

    01:28

    阿里千问3开源首战告捷:全球下载破千万,国产AI模型崛起新高度!

    01:22

    DeepSeek R1小版本试升级:网友实测编程能力已达到国际一线水平

    23:15

    NVIDIA 与 Dell 合作,大规模交付 Blackwell AI 系统

    20:52

    Cerebras 以最快的 Llama 4 Maverick 性能引领 LLM 推理竞赛

    20:51

    技术热点

    PHP中的随机性——你觉得自己幸运吗?

    搞定Ubuntu Linux下WPA无线上网

    Java使用内存映射实现大文件的上传

    MySQL安全性指南

    MySQL两项性能的基本测试浅谈

    教您使用UniqueIdentifier选取SQL Server主键

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

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