2009年3月27日

架构设计:生产者/消费者模式[1]:如何确定数据单元?

  既然前一个帖子已经搞过扫盲了,那接下来应该开始聊一些具体的编程技术问题了。不过在进入具体的技术细节之前,咱们先要搞明白一个问题:如何确定数据单元?只有把数据单元分析清楚,后面的技术设计才好搞。

★啥是数据单元?


  何谓数据单元捏?简单地说,每次生产者放到缓冲区的,就是一个数据单元;每次消费者从缓冲区取出的,也是一个数据单元。对于前一个帖子中寄信的例子,我们可以把每一封单独的信件看成是一个数据单元。
  不过光这么介绍,太过于简单,无助于大伙儿分析出这玩意儿。所以,后面咱们来看一下数据单元需要具备哪些特性。搞明白这些特性之后,就容易从复杂的业务逻辑中分析出适合做数据单元的东西了。

★数据单元的特性


  分析数据单元,需要考虑如下几个方面的特性:

◇关联到业务对象


  首先,数据单元必须关联到某种业务对象。在考虑该问题的时候,你必须深刻理解当前这个生产者/消费者模式所对应的【业务逻辑】,才能够作出合适的判断。
  由于“寄信”这个业务逻辑比较简单,所以大伙儿很容易就可以判断出数据单元是啥。但现实生活中,往往没这么乐观。大多数业务逻辑都比较复杂,当中包含的业务对象是层次繁多、类型各异。在这种情况下,就不易作出决策了。
  虽说这一步有难度,但是很重要!如果选错了业务对象,会导致后续程序设计和编码实现的复杂度大为上升,增加了开发和维护成本。

◇完整性


  所谓完整性,就是在传输过程中,要保证该数据单元的完整。要么【整个】数据单元被传递到消费者,要么完全没有传递到消费者。不允许出现【部分】传递的情形。
  对于寄信来说,你【不能】把半封信放入邮筒;同样的,邮递员从邮筒中拿信,也【不能】只拿出信的一部分。

◇独立性


  所谓独立性,就是各个数据单元之间没有互相依赖,某个数据单元传输失败【不应该】影响已经完成传输的单元;也【不能】影响尚未传输的单元。
  为啥会出现传输失败捏?假如生产者的生产速度在一段时间内一直超过消费者的处理速度,那就会导致缓冲区不断增长并达到上限,之后就很不妙了(有些数据单元会被无情地抛弃)。如果数据单元相互独立,等到生产者的速度降下来之后,后续的数据单元继续处理,不会受到牵连;反之,如果数据单元之间有某种耦合,导致被丢弃的数据单元会影响到后续其它单元的处理,那就会使程序逻辑变得非常复杂。
  对于寄信来说,某封信弄丢了,不会影响后续信件的送达;当然更不会影响已经送达的信件。

◇颗粒度


  前面提到,数据单元需要关联到某种业务对象。那么数据单元和业务对象是否要一一对应捏?很多场合确实是一一对应的。
  不过,有时出于性能等因素的考虑,也可能会把N个业务对象打包成一个数据单元。那么,这个N该如何取值就是颗粒度的考虑了。颗粒度的大小是有讲究的。太大的颗粒度可能会浪费空间;太小的颗粒度可能会影响时间性能。颗粒度的权衡要基于多方面的因素,以及一些经验值的考量。
  还是拿寄信的例子。如果颗粒度过小(比如设定为1),那邮递员每次只取出1封信。如果信件多了,那就得来回跑好多趟,浪费了时间。
  如果颗粒度太大(比如设定为100),那寄信的人得等到凑满100封信才拿去放入邮筒。假如平时很少写信,就得等上很久,也不太爽。
  可能有同学会问:生产者和消费者的颗粒度能否设置成不同大小(比如对于寄信人设置成1,对于邮递员设置成100)。当然,理论上可以这么干,但是在某些情况下会增加程序逻辑和代码实现的复杂度。后面讨论具体技术时,大伙儿应该能体会到数据单元的重要性。

  好,数据单元的话题就说到这。希望通过本帖子,大伙儿能够搞明白数据单元到底是怎么一回事。下一个帖子,咱们来聊一下“基于队列的缓冲区”,技术上如何实现。

回到本系列的目录

5 条评论:

  1. 寄信的颗粒度~ :)能不能这样呢~ 定时+定量
    闲时 超过时间阈值 就可以搬了
    忙时 超过量的阈值就搬了 不过会不会增加复杂性呢?

    回复删除
  2. 楼上的同学,
    如果按照这种策略,则每个数据单元包含的业务对象数是不定的。对于某些类型的缓冲区,可能会增加一定的复杂度。

    回复删除
  3. :) 那再加一个限制,定时的时候 只搬走完整的颗粒可以吗?剩下的继续留着 ~~~~~

    回复删除
  4. 额 :-) 复杂度就是这样来的了~ 呵呵 抱歉

    回复删除