你的位置:首页 > ASP.net教程

[ASP.net教程]信息系统实践手记5


说明:信息系统实践手记系列是系笔者在平时研发中先后遇到的大小的问题,也许朴实和细微,但往往却是经常遇到的问题。笔者对其中比较典型的加以收集,描述,归纳和分享。

摘要:此文描述了笔者接触过的部分信息系统或平台之间的对接构型和情况,挂一漏万的总结分享之。

正文

系列随笔目录:信息系统实践手记 (http://www.cnblogs.com/taichu/p/5305603.html)

作者:太初

转载说明:请指明原作者,连接,及出处。

 

1.CACHE是啥?

      最近一直在弄scala,Spark,Python,数据挖掘的东西,以及复杂科学的一些研究,在充实和忙碌之余,虽然兴趣浓厚,但体力不支,疲劳的很,无奈耽搁了实践手记。

      原本计划按照至少2周挂出一篇来分享,实在抱歉。要分享的东西太多,码字太费时间,有时候甚至觉得开会也许更好(很多人反感开会,那是因为咱们没开过会)!其实很多东西除了领路人,还是考自己去走,去体会,总结。

      闲话不说,切入正题。

      很多研发人员,尤其是开发程序的同志,找到一个cache框架或类库可用,比如hibernate的cache功能,然后就一股脑用上去。虽然会有效果,且很愉悦,这是好事,既增加了经验,也体会了框架的魅力和效率,但其实还有最重要东西可以进一步去领会。究竟为啥要用CACHE?CACHE是啥?什么样的场景用什么样的CACHE?很少有人静下心来问。

      那CACHE到底是啥呢?还真难于表述清晰,但有一些要点:

  • CACHE的产生是为了缓解处理链上不同节点之间速度不协调的情况。举若干例子:
    • 计算机系统:一般有速度梯度:CPU内部寄存器(register)>内存>磁盘IO>网络IO,为协调它们之间传递数据的速度差,就可设置缓存结构,叫CACHE;
      • 说明1:以上说的是一般情况,不考虑特别的磁阵RAID,告诉SCSI,千兆或万兆网络等;
      • 说明2:CPU内部也分L1,L2,L3等多级CACHE,早先的386/486处理器没分那么多CACHE层次;
    • 地铁系统:有时候出事故,人特多,站内站外由管理员把守入口分批放人到1层大厅,则1层大厅是一个CACHE;管理员分批从大厅放人到B1层站台,则B1站台也是一个CACHE;当CACHE快撑满的时候,管理员就会喊“慢慢来,不要挤,暂时关闭,大家耐心等待!”,于是人就被囤积在不同的CACHE里,以便协调地铁运量和人流涌入的速度差问题。
    • 工厂流水线:另一个典型例子是工厂的流水线。以前老式流水线每个工位站1个人,处理1个装配动作,比如给黄小鸭玩具装上1条腿。一个工件,如果流经10个处理节点,那就会有10个人来处理10个步骤。显然,老板期望流水线一直处于充满状态,则最高效的源源不断有产出。但意外总会发生,比如某工件N流转到Px节点,Px节点的工人很累,处理较慢,导致Px-1步骤流出的工件不断堆积,而这个Px处理节点处理台的工件堆积场所,就是一个BUFFER缓存区(也就是CACHE),它越来越满,就提醒Px的工人,加紧干,否则一旦CACHE撑爆,就无法克服(追上)速度差(时间差)的问题!(画外音:可以看出流水线机制的高效和科学,以及“残酷”)。
    • 生产者消费者模式:经典的程序设计模式“生产者和消费者”模型也有一个CACHE的设计,就是协调生产和消费的速度差,类似“工厂流水线”
  • CACHE实际上是用空间上的代价来缓解时间上的不协调。
    • 你肯定要问“既然速度有差异,那CACHE再大也没用?”,的确,再大的CACHE也是用来临时克服速度差异的,它期望可以用空间暂时的堆积追回偶发导致的时间不协调(速度差是绝对的,这个在设计场景之前就知道,但速度不协调是偶发的,这个要区别开!)。如果永远无法“追回来”,那么再大的CACHE也会有问题,那么就是设计上的问题了,就不是CACHE机制可以解决的。
    • 处理节点Px往往速度不同,所以必须用不同算法来安排。比如:
      • 通过编译程序的安排,将C/C++等编译性语言的代码做安排,并充分考虑到CPU/MEM/DISK/NETWORK的差异;保持MEM代码段高命中,降低页丢失中断,否则就要从代存(相对于内存MEM来说,磁盘就是外存设备,磁盘IO比MEM慢很多)。
      • 通过解释器(如JAVA的虚拟机JVM),将JAVA等解释性语言的代码,弄成伪指令,它也充分的考虑了CPU/MEM/DISK/NETWORK的速度差异;程序本身是死的,但编译器或解释器可以做很多,合理的安排程序段,数据段,以及考虑如何导入多核心的CPU等等。
      • 程序本身也应该充分利用诸如“多线程”,“并发计算”,“分布式计算”等方法,来从宏观的算法层面,考虑到,解决掉速度差异,从而适当的利用CACHE来防止偶发的速度不协调,而不是算法本身有永久性的硬伤“速度不匹配”。可以适当的使用一些主流成熟前三名的框架来解决实际问题。
      • 程序终究是要达成业务需要的,也就是完成功能目标(Feature)。而功能和业务本身是有特点的,有时空特性的,是高并发业务?还是随机业务?业务的时空特性如何?是否和上下班高峰有关? 是否和网站的维护周期有关?是否和购物的时间段有关?总之从业务触发,需要专业的领域知识(行业支持),并且需要深厚的技术功底,这样的从场景出发的系统分析和系统设计行为,是何其重要!这方面的人才非常紧缺。
  • CACHE所处理的时空不协调,可能是偶发的,也可能是来自于业务模型;
    • 通过上述的不同层次(编译器,解释器,程序,框架类库,业务场景分析)的处置,基本上摸透并能够妥善的处理业务,达成场景,使用必要的CACHE结构了。但业务模型总是会偶发时空不一致,尤其是在当今多任务的计算机架构(以前主机时代只有大型机是多进程的,现在微机也是多任务并细化到线程了!)。很容易计算力不够需要增加更多CPU的core,数据量大没地方放,就增加MEM,MEM增加后也会导致低速的磁盘IO和网络IO也变大。
    • 另外在对业务模型不断调优和扩容的过程中,不协调也会自然产生的,而如果程序模型比较优秀,相关必要的地方本来就有CACHE,那么CACHE将为程序运作提供余地。
    • 即便是简单的业务模型,而且调优完成,且一直正常运作。但也可能因为“网络故障,CPU失效,内存或磁盘损坏,某个计算节点离线”等问题而打破平衡。这时候,甚至连写LOG日志来记录状态也显得很奢侈或捉襟见肘。此时,如果程序在框架设计上已经未雨绸缪的安排了充分和必要的CACHE,则完全可以提升健壮性,甚至在偶发灾难冲击中坚强运行,恢复运作。
  • 客观世界和程序世界都是复杂的,而复杂系统无处不存在意外和不协调,CACHE则应运而生,无处不在:
    • CPU里面的微指令集中用到了L1,L2,L3,它们就是各级CACHE;
    • MEM,DISK,网卡等设备,无论在硬件的接口程序BIOS中,还是在程序员编制的应用程序中,都安排了CACHE,来处理设备受到外界速度不匹配的冲击。
    • 程序设计中很多模式,比如消费者生产者,就必然有CACHE存在,以缓解生产和消费的速度不匹配;
    • 真实世界的仓库存货,小超市囤货,高架路上的紧急停车带,上下岔道区域,展览会门口的回形排队护栏,流水线的每个节点的BUFFER缓冲带等等,都是CACHE的好例子;

      只有理解了自然现象,业务规律和程序能力边界,才能深入体会如何在程序设计中,在什么样的业务场景下使用怎样的CACHE设计,以便到达好处和结果。不建议只为了技术用技术,必须明确使用的目的为何,才能正确的使用。

 

 

2.CACHE设计一例

      这里简单举一个例子,还是前几篇分享的客户端和服务器,它们是提供GIS地图服务的服务器和客户端;而这个提供GIS业务的服务器就用到很多CACHE; 劈开客户端业务展现不说,很多后台服务是由做GIS业务的应用SERVER提供,简称为GS,而GS需要从设备管理平台,简称DS获取设备信息;

  1. 因为DS不提供设备信息的订阅(很遗憾),则无法使用高效的观察者模式来获取设备变更信息,而只能由GS通过阶段同步(轮询模型)来花巨大的代价向DS获取其实很少变化的设备更新信息。为了阶段同步,GS只能在内存(或DB)做了一个CACHE,来存放比如每30分钟的同步信息;
  2. 最先因为是30分钟同步,而按照J2EE经典的用法,通过DAO,业务是从DB获取数据,所以同步数据放在DB。为了提升速度,解决业务场景的高速要求和DB处理的慢速情况,将DB的数据“上提”到内存的CACHE中,以后做业务直接插CACHE,处理CACHE;这里就自然的引发了一个问题,CACHE的脏数据需要同步回到DB,因为数据存在于连个地方,一个CACHE是临时的,一个DB是持久化的,那么它们之间也有同步问题,这是使用CACHE经常要面临的情况。其实最后因为原来程序框架做的不佳,导致了批量修改完成后,还遗留了不少小尾巴,只能casebycase的处理,找到一个算一个,当让也可以在人力允许的范围内做大面积的回归测试,不细说。
  3. 我们GS平台,还和第三方,比如一个城市的路口卡口管理平台(简称KS)对接,上次讲过平台对接问题了,这里假设顺利对接。那么KS的数据,也会阶段性的同步到我们GS来。一个典型的应用是GS向KS订阅了一种“告警A”,当KS发生时间A而发出“告警A”时,将会推送到GS平台,那么GS平台分发给客户端。这里显然有速度的不协调,如果告警频繁,GS一时处理不过来,那么就要CACHE,怎么弄?也可以不用自己写代码,我们GS和GS的客户端是通过ActiveMQ的消息中间件来传递消息。订阅之后,不同主题的消息和用户就会有自己的BUFFER来存放,即便KS平台突发大量告警A到GS,GS来不及分发,那就放入MQ自己管理的缓存队列,MQ自己处理。当然这里我们看到了上面讨论过的CACHE的几个问题:
    1. 如果KS平台的“告警A”一直高频持续发送,那么最终GS接收代码来不急放入MQ,或者来得及放入MQ,但MQ终将崩溃。所以这里就遇到了前几篇提到的平台对接中,对两个有交互的平台之间的信令和数据的“时空特性”一定要定义清晰,比如我们GS和KS就定义了最高并发不能超过1秒10条(10CAPS),否则GS拒绝而不是通过CACHE尽量容纳和处理。万事都有限度,讨论清楚就行!
    2. 这里还有一个隐蔽问题,大家不知道看出来没有。这里假设我GS的JAVA接收代码,从网络socket套接字获取的批量字节流,速度极快,放入MQ几乎不花时间。如果没有这个假设,而且放入速度的确很慢,那么MQ不会最终“爆炸”,首先奔溃的及时GS的接收代码片段。这个判断,一般是根据程序员的经验和业界的经验来确定的。它总是非常快,放入message,总是比消费处理message快不少。
  4. CACHE也能存在于高层次和高抽象级别的。比如当前的hibernate库支持对象到DATA MODEL的映射,而业务是针对对象,不是直接赤裸的和数据交互的。针对对象的操作比如func1()也是可以CACHE缓存的,以便第二次同样的调用func1()的时候,极大的提升速度和效率。 这里当然也会看到一个脏数据和失效的问题,不再讨论。

 

[END]