说到编程,对于程序员来说并不陌生。防御性编程相信大家都听说过,但是它具体包括什么呢?什么是有效的?解决方案呢?我们如何正确应用它?本文作者结合实际工作中的一些应用经验,对防御性编程进行了全面剖析。本文主要内容:什么是防御性编程防御性编程的重要性InputCheckingAssertionApplicationErrorHandlingIsolationDefenseStrategiesandRecommendations1.什么是防御性编程关于防御性编程,这个概念是从防御性驾驶汽车技术开始的,也就是说你永远不能确定另一名司机将要做什么,以确保另一名司机在进行危险操作时不会伤害您。在防御性编程的应用过程中,并不是说要保护自己,对别人持有“批评或攻击”的态度,而是把保护意识放在自己的程序上,使用一些防御手段,使自己的代码程序不是Crashonerrorduetobaddatapassedin。人们通常会说,“代码有问题很正常”。确实是这样,所以写程序的时候要提高防御的重要性,尤其是核心程序能力,要做好程序错误影响的包容性。2.防御性编程的重要性随着当前互联网向各行各业的渗透,每一个细小的风险问题都可能被放大到足以影响整个行业。1996年6月4日,欧洲航天局的阿丽亚娜5号航班501在起飞后40秒被引爆。由于导航软件中的错误,价值数十亿的运载火箭不得不自毁。2019年1月,拼多多被曝出重大漏洞。用户可领取100元无门槛优惠券,导致大量用户开始“扫羊毛”,一晚上充值话费超过200亿。2019年5月,部分用户反映支付宝出现网络故障,无法登录或支付。支付宝相关负责人表示,此次故障是杭州市萧山区某处光纤被剪断所致。该事件导致部分用户无法使用支付宝。系统服务的稳定性对企业来说非常重要。不仅会给企业带来直接的经济损失,还会对行业和人民生活造成非常严重的影响。3、输入检查学习编码的时候,估计大家都听说过“don'ttrusttheuser'sinput”,指的是检查用户输入的必要性。说到输入,常见的web开发主要包括以下两个方面:3.1系统外所有数据的校验在系统搭建的过程中,我们经常需要和外部系统交互进行数据处理,包括:文件、接口、消息queues,Formuserinput等。对于系统外输入的数据内容,我们需要明确做:数据格式是否准确,数据类型是否准确,数据长度是否准确,检查预期的准确度数据,并确保输入的数据在我们程序可接受的范围内。事实上,所有安全问题的本质都是信任问题。数据检查,这类似于车站和机场的安全检查。通过安全检查(过滤、净化)过程,可以筛选出不知名的人或事,使其值得信赖。划分出的不同信任级别的区域称为信任域,两个不同信任域之间的边界称为信任边界。对于异常的数据处理,做好防御检查的同时,还需要做好日志记录,以防后续。哈哈~3.2检查接口API参数值对于系统内部接口API请求,需要检查程序的入参值。这与检查来自外部系统的数据相同。/***请求处理通用类**/publicclassCommonRequest{@NotBlank(message="参数str不能为空")privateStringstr;@NotNull(message="参数i不能为空")privateIntegeri;@Min(value=0,message="最小值不能小于0")privateintmin;@Max(value=100,message="最大值不能为100左右")privateintmax;}??正常情况下需要验证以下项:必填和非必填字段类型是否相同参数值是否为是否合法,长度是否符合要求对于异常的接口参数/字段,可以按照以下思路验证问题:Q1:如果参数丢失或遗漏,会有默认值吗?Q2:如果参数有问题,业务逻辑会出现哪些不合理的情况?Q3:如果字段丢失或者不合法,会不会导致写操作产生垃圾数据?注:增加一个关键情况,需要结合业务Scenarios来评估可能的影响范围。如有必要,设置白名单而不是黑名单。例如,当您设置图像扩展时,不要设置无效类型,而是检查有效类型并排除其他类型。PHP中有无数的开源验证库可以让您的工作更轻松。请记住:进攻是最好的防守。总之,不要过度考虑代码之外的函数调用或方法调用。在调用外部API和库之前,请确保您了解并测试错误。4.断言的应用4.1什么是断言?所谓断言,是指开发时使用的代码,让程序在运行时进行自检。通常是一个子程序或宏。断言的目的是表达和验证软件开发者的预期结果。当程序执行到断言的位置时,对应的断言应该为真;如果断言不正确,程序将停止执行并给出错误信息。例如:如果系统假设一个数据文件包含不超过20,000条记录,那么可以在程序中设置记录数<=20,000的断言。只要记录数<=20000,这个断言就不会触发,但是一旦记录数超过20000,就会断言程序有错误。4.2断言的形式断言可以有两种形式:assertExpression1assertExpression1:Expression2其中,Expression1应始终为布尔值,Expression2是断言失败时输出的失败消息字符串。如果Expression1为false,则抛出AssertionError,这是一个错误,而不是异常,也就是说,是一个未经检查的异常。AssertionError是一个错误,所以不能被捕获,但是不推荐。因为那会使您的系统进入不稳定状态。公共类TestAssert{undefinedpublicstaticvoidmain(String[]args){undefinedStringname="abnerchai";//字符串名称=空;assert(name!=null):"变量名为空null";系统。out.println(名字);}}5.错误处理根据前面的介绍,断言可以用来处理代码中不应该出现的错误,那么如何处理预期中可能出现的错误呢?异常和错误处理是防御性编程的一个组成部分。想象一下启动一个异步操作,运行它并输出结果,没有异常,这是一个理想的情况。如果执行过程中出现错误怎么办?与任何未处理的异常一样,应用程序通常会崩溃。假设任何异步操作都将成功运行而没有任何错误,它可能会失败。在高级语言中,trycatch一般用于捕获异常处理,如下例所示:try{//逻辑代码}catch(exceptione){//异常处理代码}try{//逻辑代码}finally{//必须执行的代码}try{//逻辑代码}catch(exceptione){//异常处理代码}finally{//必须执行的代码}而Golang的错误处理规范也是最大的亮点之一围棋语言。error接口标准库将error定义为接口类型,这样就可以自己定义错误类型了。typeerrorinterface{Error()string}Paincgolang的内置方法可以改变程序的控制流程。当函数调用panic时,函数会停止运行,但是defer函数会运行,当前panic的所有goroutine都出栈后程序会崩溃。recoverrecover也是golang内置的一个方法,用于恢复panicgoroutine的控制权。recover只在defer函数中生效。如果当前goroutine即将panic,recover将捕获panic并恢复正常执行。当Defer讲到panic和recover的时候,就需要讲到关键字defer,后面会看到defer在异常处理机制中的作用。Go的defer用于延迟函数的执行,延迟发生在调用函数返回之后。6.隔离所谓隔离,是指程序可以包含错误造成的损害,称为损害容忍策略。软件行业最常见的解决方案是建设多个机房。实现双机房部署建设服务,承受单机房故障,保障用户体验。本次实现的难点主要有以下三点:跨机房网络延迟和带宽限制带来的数据级一致性问题。一种实现方案:高德基于地理位置单元,不同服务集群间双向数据复制,内部调用路由。微博MySQL多机房同步(写的时候写到一个机房,有专门的组件负责同步写到另一个机房)。隔离的应用程序还体现了在架构设计中指定应用程序应如何应用以及如何处理错误的价值。7.防御策略和建议在防御编程的道路上,没有灵丹妙药。在产品中留下太多防御性代码会与精益代码实现发生冲突。从产品本身入手,保证程序稳定运行,不影响用户体验。整理出以下建议:保留重要错误检查的代码,去掉检查小错误的代码,保留让程序安全崩溃的代码,去掉会导致程序崩溃的代码,确认错误代码中的message是友好的,记录错误信息给技术支持人员其实对于防御性编程,我们其实是想在保证程序的稳定性和程序不至于过于臃肿之间找到一个合理的平衡点。防御性编程技术可以使错误更容易发现、更容易修改,并且错误对代码的破坏更小。断言可以帮助人们更早地发现错误。关于如何处理错误输入的决定是一个关键的错误处理决定。一个关键的高层设计决策。防止看似很小的错误,其好处可能远超你的想象。
