Dirty Approach正反两例(1)

本文围绕软件开发中Dirty but Fast的做法展开,以Blog电子报开发为例,讲述了正反两个案例。反例是按日期阅读电子报功能出现页面空白问题;正例是在硬件带宽有限时,通过判断RSS最后更新日期和文章URL/更新日期,用dirty算法减少抓取、解析和保存次数,提高效率。

前两天跟霍炬聊起软件开发,谈到所谓Dirty but Fast的做法,即为了更快完成某种功能、不惜使用“不太优雅”的模式(非Design Pattern)或代码实现。霍炬给这种方式起了一个英文短语,叫做dirty case;从遣词上看,dirty but fast approach更为贴切,不过dbfa太长,而且有时也并非为了“快”而dirty,而是为了堵住常规方法很难堵住的漏洞,例如霍炬写到的Unix体系中root远程登录的case。所以,不妨还是叫做dirty case,或者dirty approach。

编写代码,如文艺创作一般,也是有境界高下的。没入门的自然没办法,刚入门的用笨办法,提高一些后开始用正规的办法,高手敢于使用脏办法,而大师则已是无法即有法、“从心所欲而不逾矩”了。我不是高手,更不是大师,不过我懒,所以常常会选择不太好(dirty)但可能有效的实现方式。凡事不可太过,过犹不及,如果用得不是地方,dirty but fast恐怕就要变成dirty and slow了。下面两个例子,一正一反,讲述前不久在开发Blog电子报功能时发生的故事。

例一:反例

Blog电子报,有点类似blogline那样的rss聚合服务,但更偏向于“编辑自己的报纸,聚合给别人看”。在存储结构上,blog_Groups用来存放电子报列表,blog_Group_RssUrl存放Rss地址列表,blog_Group_GroupRss存放Group和RssUrl之间的多对多关系,而blog_Group_RssContent则存放Rss中每篇文章的条目信息(标题,作者,摘要,链接等)。

在考虑电子报的分页体系时,一时懒劲发作,觉得做分页查询太麻烦,想找一个投机取巧的办法。一琢磨,电子报->报纸->日报->日期,按日期阅读估计成!分页时不用考虑太多东西,选取特定日期的条目出来就好。于是吭哧吭哧搞半天,把按日期阅读的功能做好了。

上线测试后,发现两个问题,一个问题能解决,另一个问题没法解决。先说能解决的那个:选定日期没有文章。电子报是RSS的聚合,如果一份电子报所有RSS作者在某天都没有写文章,结果就是当天电子报页面空白。这实在有点郁闷。于是我又想了一个办法——找有文章的最近一天,显示这一天的文章列表。一阵好干,这个特性也实现了。虽然感觉怪怪的(类似:输入http://blog.youkuaiyun.com/group/experts/20050403.aspx却出来4月4日的文章),不过好歹不是空白一片。

第二个问题紧跟着跳出来。当天没文章好办,到有文章的最近一天就行,如果当天文章量很少怎么办呢?例如,只有一篇文章……惨了,页面上还是大片空白。其实页面有空白不是最要紧的,要紧的是这种方式让读者很不爽!我没有想清楚电子报和日报之间的相同和不同之处。关键在于:日报虽然是按天出版,不过它的内容量是一定的,文章写作日期可以不一样,但出版的时候,一定是同时出来。“保证在版面上有定量内容”就是关键。而“按日期导航”违反了这个原则。

当然,按日期导航也有用处,毕竟增加了一个入口点,且为“找特定某天的文章”提供可能。最后我还是保留了这种导航方式,只是将它作为第二导航方式,第一方式还是采用传统的每页固定数量列表方式。

 

例二:正例

电子报是一种RSS聚合服务,“抓取外部RSS、解析和保存内容”自然就是其中最基本的特性。当RSS数量变得庞大起来是,效率就会有问题。这里不说多线程、分布式,假设硬件带宽资源有限,怎么解决效率问题?

答案就是“尽量减少抓取、解析、保存次数”。矛盾之处在于,当RSS地址越多,抓取程序就应该运行越频繁,这样才能保证“及时更新”。试问读者,你会怎么处理?我的处理方法是:对RSS最后更新日期、和文章URL/更新日期进行判断。

对于每个RSS,有两个关键时间点:1、整个RSS的最后更新时间 2、每篇文章的最后更新时间。至于HTTP返回的“Not Modified”不能作为关键要素,除非是第一次取这个RSS。

在第一次取RSS时,我会把LastModified(RSS规范中的一个属性)记入该RSS在数据库中的条目信息,下次轮到取这个RSS的时候,把保存的LastModified和远程RSS的对应字段进行对比,如果RemoteRss.LastModified>LocalRssInfo.LastModified,才会解析RSS。RSS是XML格式,做一次解析虽然耗时不多,但加起来就可观了。

请注意上段中的“轮到”,读者应该已经猜到,我不会每次都把所有RSS地址从数据库拿出来、到远程抓一遍。网络上一个roundtrip成本太高了。所以我用一种dirty的算法,来判断程序本次运行应该抓哪些RSS。现在隆重介绍这种“算法”——

1、在解析一个RSS时,记录每篇文章之间的发表时间间隔,假设是x1,x2,x3……xn
2、在解析整个RSS完成后,求x1,x2,x3……xn的平均值,假设是y
3、RemoteRss.LastModified+y是下次新文章可能出现的日期

细节不作描述,原因不作解释,道理很简单,读者可以自己研究。也许有读者会提出,数据库中保存的每个RSS的所有文章发表日期间隔的平均值,是不是该更接近距离下次更新文章的日期?其实似是而非,因为Blog发表文章,未见得会长期维持稳定发表量,不同时期内的发表量会有变化。所以,最近15篇文章的发表间隔平均值,比较有可能接近距离下次发文章的日期。

当然,还有一种情况,就是下次发表文章的日期大大提前了。事属正常,不过它似乎违反了我们的规则。发生这种事时,这个可怜的RSS就有可能轮不上被及时抓取。漏抓结果可能很严重——因为如果一篇文章信息从RSS中消失,可能你就再也无法通过RSS得到它了。在系统资源有限的情况下,可以安排一些随机的“幸运RSS”,无论是否“当值”,都去尝试抓一下。

(待续)

在IT领域,特别是编程和网络安全方面,"Dirty Dozen"通常指代一组常见且影响重大的错误或漏洞,这些错误可能引发严重的安全风险,甚至导致系统被完全攻破。这一术语在不同上下文中有不同的具体应用,但其核心思想是识别并防范那些最容易被攻击者利用的缺陷。 在编程领域,"Dirty Dozen"常指OWASP(开放网络应用安全项目)定义的12类常见Web应用安全漏洞。这些漏洞包括但不限于: 1. 注入(Injection):攻击者通过将恶意输入注入到查询或命令中,从而操控数据库或执行任意代码。 2. 失效的身份认证和会话管理(Broken Authentication and Session Management):错误的会话处理机制可能导致攻击者冒充合法用户。 3. 敏感数据泄露(Sensitive Data Exposure):未正确加密或保护的敏感数据可能被窃取。 4. XML外部实体(XXE):不安全的XML解析器配置可能导致攻击者读取服务器上的文件或发起拒绝服务攻击。 5. 不安全的直接对象引用(Insecure Direct Object References):允许攻击者通过修改参数访问未经授权的数据。 6. 安全配置错误(Security Misconfiguration):错误的服务器或应用配置可能导致信息泄露或远程代码执行。 7. 跨站脚本(XSS):未正确过滤或转义的用户输入可能导致恶意脚本在受害者的浏览器中执行。 8. 不安全的反序列化(Insecure Deserialization):恶意构造的数据可能导致反序列化时执行任意代码。 9. 使用含有已知漏洞的组件(Using Components with Known Vulnerabilities):依赖库或框架中存在已知漏洞可能被攻击者利用。 10. 不足的日志记录与监控(Insufficient Logging & Monitoring):缺乏有效的日志记录和监控机制可能导致攻击行为未被及时发现。 11. 服务器端请求伪造(SSRF):应用程序未能正确验证用户提供的URL可能导致服务器发起恶意请求。 12. 缺乏适当的访问控制(Broken Access Control):未正确实施访问控制可能导致用户访问其不应访问的资源[^1]。 在网络安全领域,"Dirty Dozen"有时也指代系统管理员或安全人员最常忽视的安全问题,例如弱密码策略、未打补丁的系统、缺乏防火墙规则等。这些问题虽然看似简单,但往往成为攻击者入侵系统的突破口。 例如,在系统管理中,常见的12项安全疏忽包括: - 使用默认凭证 - 未及时更新系统和软件 - 缺乏网络分段 - 未启用多因素认证 - 未对日志进行集中管理 - 未对敏感数据进行加密 - 未实施最小权限原则 - 忽视第三方软件的安全性 - 未进行定期的安全审计 - 缺乏入侵检测系统 - 忽略物理安全 - 未对员工进行安全意识培训 这些问题和漏洞的共同特点是它们往往容易被忽视,但一旦被攻击者利用,可能造成严重后果。因此,识别并修复这些“Dirty Dozen”问题对于保障系统的整体安全至关重要。 ```python # 示例代码:检测是否存在常见的SQL注入漏洞 def check_sql_injection(input_string): # 常见的SQL注入关键字 sql_keywords = ["'", "OR", "=", "--", ";", "DROP", "UNION"] for keyword in sql_keywords: if keyword in input_string: return True # 检测到潜在的SQL注入尝试 return False # 未检测到明显的SQL注入迹象 # 测试输入 user_input = "test' OR '1'='1" if check_sql_injection(user_input): print("检测到潜在的SQL注入风险!") else: print("输入安全,未检测到SQL注入风险。") ```
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值