存档

‘软件设计’ 分类的存档

代码中的Bad Smell坏味道

2010年3月6日 admin 没有评论

最近一段时间的工作都要维护一些老代码以及做一些Code Review。代码中的那些Bad Smell真的让人很抓狂,想起之前看过的Martin Fowler的《重构》,于是就整理了一些自己常能体会到的代码中的Bad Smell.

Bad Smell列举

1.重复代码

消除重复代码的基本方法是:如果是同一个类中的重复代码就把这个方法“抽取”出来;如果是两个兄弟类之间的重复代码需要把方法抽取出来,并提升到父类中;如果是两个不相关类间的代码重复,就

2.方法过大

应该更积极进取地分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中, 并以其用途(而非实现手法)命名。我们可以对一组或甚至短短一行代码做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我 们也该毫不犹豫地那么做。关键不在于函数长度,而在于函数[做什么]和[如何做]之间的语义距离。

3.类过大

类过大往往意味着想利用单一class做太多事情,其内往往就会出现太多instance变量。一旦如此,代码重复、God Object之类的问题也就是接踵而至了。

解决类过大的方法就是要思考类的定义和职责,将原本一个类中完成的功能分解到其他几个类中。

4.过长参数列

方法的参数要尽量的少,超过3个以上的参数就会直接影响方法的理解。这个可以根据参数间的关联性抽取出一个对象来。

6.多层的条件语句,循环语句嵌套.

If里面嵌套If,for里面继续循环for。这种情况可以适当的将嵌套的内容抽取出来。

7.变量名不具备说明性。

变量名应该具有足够的说明性。除了常用的i,j作为循环变量。通常一些全局变量和实例变量的变量名应该足够长,并具有足够的说明性,并保证不易跟其他变量混淆。

10.方法和类的职责要具有单一性,并且名称能够具备说明性。

 

参考资料

代码的坏味道,重构,模式
refactoring-从地狱中重生
God Object
Code Smell

对象健身操

分类: 软件设计 标签:

软件开发沉思录阅读笔记二

2010年2月18日 admin 1 条评论

这是《软件开发沉思录》这本书阅读笔记的第二篇,第一篇是软件开发沉思录阅读笔记一是对第六章——对象健身操的阅读笔迹。

第七章——迭代经理是什么角色

什么是迭代经理?

ThoughtWorks的高级架构师Fred George把迭代经理描述成“面向内部的管理角色。迭代经理负责保证故事在团队中流动的顺畅,这牵涉到合理分配任务、在技能需要改变的时候建议更换团队成员。”

怎样成为好的迭代经理?

每天,迭代经理都必须倾听团队的需要并且作出回应。其主要职责就是培养一台润滑良好的“机器”,并依据要求的质量在项目范微内交付功能。

迭代经理应该在技术熟练度和业务知识之间达到一个平衡。敏捷领袖应该“同时对客户和技术都由深刻的理解,这样才能赢得开发团队的尊重。”良好的沟通技巧是必须的。迭代经理的职责之一就是维护开团队与客户,以及与管理阶层之间的关系。

同时,迭代经理必须推动、主张和保障团队成员的权利。

迭代经理不做什么?

与项目经理不同,迭代经理需要在工作第一线,与团队成员一起面对每日的工作活动。

迭代经理与项目

作为次要目标,我期望看到因为迭代经理的工作,团队成员在项目结束的时候变得更为优秀。团队内充满信任,持续提高技能——这是迭代经理的工作。

迭代经理要争取把团队拧成一根绳,让所有的成员能同舟共济。

第十章——领域标注

在过去十年里,众多软件开发者意识到:软件应用程序中最大的复杂度来自于软件要处理的实际问题领域。正因为如此,所谓“领域驱动设计”有两个前提:

  • 大部分软件项目关注的焦点是业务领域和领域逻辑;
  • 复杂的领域设计应该基于模型来进行。

换句话说,领域驱动设计将用业务领域术语表述的业务领域的面向对象模型置于软件系统的核心。数据通常保存在关系数据库里,但看待数据的主视角是领域对象,而不是数据库表和存储过程。核心业务逻辑集中保存在领域模型中,而不是散步在用户界面和应用程序的服务层里。

如果遵循领域驱动设计方法,得到的软件系统就会清晰地区分出领域模型和基础设施(及界面):前者通常较为稳定、生命周期较长;后者通常生命周期较短,并且与具体的技术(例如O/R映射或者WEB框架)相关。这里的挑战在于如何保持两者之间的分野,从而使两者能够各自保持可复用性:一方面,领域模型应该能够在不同的应用程序、不同的服务中使用,或是在技术升级以后继续使用;另一方面,基础设施应该能用于支撑各种领域模型。当然最终的目标是实现基础设施的行业标准化(无论是采用商用软件还是开源软件),从而使应用程序开发者能专注于问题领域。

分类: 敏捷, 软件设计, 阅读 标签: ,

面向对象的概念和术语总结和UML类图说明

2010年2月7日 admin 1 条评论

最近打算给开发和QA的同学介绍一下面向对象和UML相关的内容。这是第一篇,介绍主要面向对象的基础概念和UML类图的绘制。

概念:类似对象的一种软件抽象,创建对象的模板。

UML图:属性和操作之前可附加一个可见性修饰符。加号(+)表示具有公共可见性。减号(-)表示私有可见性。#号表示受保护的可见性。省略这些修饰符表示具有package(包)级别的可见性。如果属性或操作具有下划线,表明它是静态的。在操作中,可同时列出它接受的参数,以及返回类型。

kmstheme

接口

概念:定义了一套内聚行为的一个或多个操作特性标记的集合。接口是确保送耦合的一种强大的方法。它们允许类参与一套共同的功能集,除了支持接口意外,不需要别的类知道任何有关他的事情。接口的设计要遵循“接口分离原则”,参见什么是面相对象设计的SOLID原则(s)

UML图:接口的UML图展现的样式会有几种变化,但内容都是一样的。interface

注:在StarUML中可以通过选择Interface元素的Format->【Stereotype Display】、【Suppress Attributes】、【Suppress Operations】来调整。

interface_ui

联系(Association)

概念:实体之间的一个结构化关系表明对象是相互连接的。箭头是可选的,它用于指定导航能力。如果没有箭头,暗示是一种双向的导航能力。

UML图:

image

  • 方向性。开口箭头指出关联的方向。只有一个开口箭头的关联是单向的:它仅可以在一个方向(箭头的方向)上移动。没有箭头时,表示关联是可以在两个方向上移动。
  • 标签。标签是可选的,一般有一到两个词组成,用来描述关联。
  • 多重性(Multiplicity)。关联的多重性在线的两端标出,每个方向有一个多重性指示器。

聚合(Aggregation)

概念:有时对象会有其他对象组成。例如,飞机由机身、机翼、引擎、起落架等组成。这些全部都是聚合这个概念的例子,它表示“is part of”关系。

UML图:

uml-aac-diff-02.png

class Node
{
  private:
    vector<Node*> itsNodes;
};
     上述代码只有当子节点不会成为父节点的父节点时(即,必须是树结构,不能是图结构),才能称之
为聚合。

组合(Composition)

     概念:组合是更强形式的聚合,其中“整体”负责部分,每个“部分”对象也仅与一个整体对象联系。例如,
在任何给定时间,引擎是且仅是飞机的一部分。而且,除了飞机以外,其他对象都不能直接与引擎对象发
生交互。
        UML图:
uml-aac-diff-03.png
class Car
{
  public:
    virtual ~Car() {delete itsCarb;}
  private:
    Carburetor* itsCarb
};

依赖(Dependency)

概念:对象之间存在临时关系,只有一个原因,它们可能会相互协作。一个对象与另一个对象进行协作,它需要了解这个对象。这意味着两个对象之间必须存在对象关系或part-of关系。当两个对象之间不存在持久关联的时候,我们需要在两个类之间建立依赖。

UML图:

dependency

泛化(Generalization)

概念:表示一个更泛化的元素和一个更具体的元素之间的关系。

UML图:

image

AbstractKmsTheme被标记为抽象的(名字是斜体)。

实现(Realization)

概念:指定两个实体之间的一个合同。换言之,一个实体定义一个合同,而另一个实体保证履行该合同。

UML图

image

总结面向对象的概念和术语汇总表

术语 描述
Abstract Class抽象类 不能实例化的类
Abstraction抽象 一个项目(可能是类或者操作)的本质特征
Aggregation聚合 两个类或者组件之间的关系,定义为“is part of”
Association关联 两个类或者对象之间的关系
Attribute属性 类了解的东西(数据/信息)
Cardinality基数 表示“有多少”的概念
Class类 类似对象的一种软件抽象,创建对象的模板
Cohesion内聚 封装单元(如组件或者类)的相关程度
Component组件 一种内聚功能单元,可以独立开发、发布、由其他组件编辑组成更大的单元。
Composition组合 强类型的聚合,其中“整体”完全负责各个组成部分,每个部分对象仅与一个“整体”对象相关联
Concrete Class具体类 可以从中实例化对象的类
Coupling耦合 两个项目之间的依赖程度
Generalization泛化 表示一个更泛化的元素和一个更具体的元素之间的关系。
Inheritance继承 定义为“is a”或者“is like”的关系
Instance实例 一个对象,它是某个类的一个示例
Instantiate实例化 从类定义中创建对象
Interface接口 定义了一套内聚行为的一个或多个操作特性标记的集合
Message消息 请求星系或者执行任务
Messaging消息传递 对象之间通过发送消息相互协作的过程
Method方法 有执行值操作的类实现的一个过程(与结构化编程中的函数相似)
Multiple Inheritance多重继承 直接继承自一个以上的类
Object对象 基于类定义的人物、地点、事件、事物等等
Optionality选择性 概念“你需要它吗?”
Override 在子类中重新定义属性和/或方法,以使它们与父类中的定义有区别
Pattern 在考虑相关因素的情况下,通用问题的一个可行性解决方案
Polymorphism多态 不同的对象可以以不同的方式响应同一消息,使对象可以交互而不需要知道确切的类型
Property 在UML2中,是一个命名的值,例如,属性和关联,包括组合,指定元素(例如类或者组件)的一个特征。在Java中,属性的组合包括Getter和Setter
Single Inheritance多重继承 仅从一个类直接继承
Stereotype构造型 建模元素的一种通用用法
Subclass子类 继承自另一个类的类
Superclass父类 另一个类从中继承的类

参考

The Ojbect Primer中文版第二章

UML中的联系、聚合与组合的区别

全面认识UML类图元素

分类: 软件设计 标签: ,

Daniel-Journey Weekly Dose-2010/2/6

2010年2月6日 admin 没有评论
分类: 软件设计, 阅读 标签:

软件开发沉思录阅读笔记一

2010年2月6日 admin 没有评论

软件开发沉思录简介

从编程技术到项目管理,Roy Singham、Martin Fowler、Rebecca Parsons等来自ThoughtWorks的思想领袖通过本书中的13篇美文,将自己多年沉思和实践所得倾囊相授,引领你走向敏捷软件开发的成功之路。
本书内容丰富,涵盖了软件开发的各个阶段,既包含DSL、SOA、多语言开发和领域驱动设计等热门主题,也有对象设计、一键发布、性能测试和项目管理等方面的经验之谈和独到见解。不论你是开发人员还是项目管理人员,都将从本书中获益匪浅。

第六章 对象健身操

  • 优秀设计背后的核心概念其实并不高深。比如内聚性、松耦合、零重复、封装、可测试性、可读性以及单一职责。这七条评判代码质量的原则就已经被广泛接受了。然而真正困难的是如何把这些概念付诸实践。理解了封装就是隐藏“数据、实现细节、类型、设计或者构造”,这只是设计出良好封装代码的第一步而已。
  • 更为严格的编码标准。
    • 方法只使用一级缩进
    • 拒绝使用else关键字
    • 封装所有的原生类型和字符串
    • 一行代码只有一个“.”运算符
    • 不要使用缩写
    • 保持实体对象简单清晰
    • 任何类型中的实例变量都不要超过两个
    • 使用一流的集合
    • 不使用任何Getter/Setter/Property
      • 这条规则通常被描述为“讲述而不要询问”(“Tell, dont’t ask”)
  • 如果努力地拥抱简单性,那么开发过程会快乐得多。
分类: 软件设计 标签: ,

转载:做人、做事,做架构师——架构师能力模型解析

2010年1月31日 admin 没有评论

要想从一名普通程序员发展成为优秀的架构师,“个人特性”与“技术技能”缺一不可;而“技术专业能力”、“人际关系能力”和“业务能力”更是优秀架构师重要的三种能力。
/ 周爱民(《程序员》2008年4月刊)
引子
究竟是什么让你在同一个位置上——例如程序员或技术负责人——工作了三年、五年或者更久,而仍然得不到任何的发展空间?你觉得自己已成为技术圈中的大牛,并信心满满地去拿明天就要颁发的某某大奖,然而却仍然停留在同样的技术职位上,去年到今年涨的薪水甚至填不平物价升幅?于是,你开始对老板不满,对员工不满,对昨天升职的那个同事不满……你开始计划明天就要跑单,或者准备考虑提出加薪却又心怀忐忑。
如果技术人员有发展的轨迹,那么他要么“看透工具的本质,把关注点转移到‘团队’的圈子里去”,要么“顺着代码铺就的道路,亦步亦趋地成为良匠大师”。仅以技术方向而言,你大概可以做到架构师、总架构师甚至首席架构师;但问题是:你现在还只是一个程序员。那要如何才能踏上通往架构师之路呢?本文为你解析一个架构师的能力模型。
你能不能做一个好的架构师?
架构师不是界定一个技术高下的职位名称,而是一个职务。所谓职务,包括职——职位,务——工作。前者决定了你具备哪些资源,可以影响到怎样的范围,以及面向的机构,后者则简单地是你需要完成的工作列表。
所以我说“架构师”不是指“一个能做架构的人”。前者是把架构师当职能,后者是当工人。能做一份工作列表中的事,并不等于就成为相应职位上的人。在管理体系里面,你的个人特性决定了你在哪个位置,而技术技能只是做事实施的必需。架构师这个职务,同时要求较高的个人素质和技术能力,因此它的进取之路总结起来就是:做人、做事,做架构师。
因此“模型”由“个人特性”和“技术技能”两个方面构成,在第一张图中,我特别说明“个人特性”既包括人际关系的能力,也包括(具体)业务能力;“技术技能”也是如此。所以个人特性主要与“做人”有关,部分地也包含“做事”的要素。

dd1
                                                           图1 架构师能力模型
“有效沟通”以及“学会谈判”与做具体的事无关,是个人能力特性的公共方面。前者是过程,后者是知道如何定目标与求结果。而“风险与防备”是做事过程控制的关键,与前面两项正好构成了一个做事基本能力的完整体系。基本上,这三项个人特性都是一个“普通程序员”所不具备的,甚至在大多数情况下,普通程序员并不愿意去具备这样的个人特性,因为在许多陷于技术泥淖的开发人员看来:沟通总是会使事情变得更加麻烦,谈判则徒耗时间而无济于事。然而事实上,在整个的架构决策过程中,架构师需要不停地沟通与谈判。将“架构”变成“决策”的过程,其实就是对各个技术角色(及其思想)兼容并包的过程,你需要不断地协调需求、实现之间的各种问题,也需要面对各种投资者(时间、资金、人才等方面的决策者)进行谈判,以确定项目的规模——没有规模也就没有范围,没有范围如何展开设计呢?
一部分开发人员会认为上述过程是“项目经理”的事情,但真的如此吗?当你作为一个更高级别的架构师,以至于要影响到多个项目的决策时,你就全然不会有这种感受了。因为这种情况下,你的决策将先于项目的启动,或者说你已经不单单是一个技术角色了。
设计是架构能力的一部分,但架构师不是设计师——看清楚二者之间的不同,你才真正迈出了架构师职业生涯的第一步。
抽象是思维能力、模型化是表达能力
个人特性中另一个非常重要的方面是“抽象思维”,而这是与架构师角色直接相关的一种能力。这种能力既有职业技能特征,又是普遍性的能力。
所谓普遍性的能力,是指“抽象”在我们——作为人这种个体的——生活中无处不在。例如我们说花、草,说桌、椅……我们用语言去指称任何一个既已存在的(可以脱离我们的语言而自然存在的)事物时,就用到了抽象。说“桌子”的时候,既没有描述桌子的具体形式,也没有说明它的规格,但我们用这个名词时,所有人都知道“桌子是什么”。所以,名词概念是整个抽象逻辑系统中的主体。如果失去了这些名词定义,我们基本上不能说话,也不能描述任何东西——那便到了“只可意会不可言传”的境地。
用现有的成熟语汇去描述你的系统时,大多数人会理解你所表达的含义,例如我们说“这个系统设计为一个三层结构”。然而架构师面临的系统在许多细节上并不见得能够用成熟的语汇去描述,因此必须自已构建一个抽象系统,这就需要概念抽象能力、概念表达能力和基于概念的逻辑表达能力。
概念抽象能力是一种思维能力。简单地说,就是“把目标分解或概括清楚”:你要么概而言之“它是什么”,要么详细地说明“它包括什么”。必须使用大量的语汇来陈述这个“什么”,这不单单是表达为文字,也表达为你在思想过程中的一个完整系统。通常用的方法是“映射系统”。例如你可以用数学中的“数轴”来映射“实数域”。将目标系统形式化为一个概念化的、可讨论的结构系统后,你的抽象过程就基本结束了。
dd3
                                          图2 能力模型中的个人特性
然而这个“抽象系统”可能只构建在你的思维意识里,还必须把它描绘出来。因为不能只是你自己思考清楚,系统就能设计完成。这个“描绘”就依赖于后面两种表达能力,一种是描绘概念实体,一种是描绘实体上的逻辑——有趣的是,这似乎又回到了“程序=结构+算法”。
现在大家回过头来看看UML,或者更多种类的ML(建模语言),他们就用于表达这两个方面的东西:要么是概念实体(例如用一个框表明系统边界),要么是实体上的逻辑(例如用箭头表明逻辑时序)。
所以大家应该清楚,我们再如何称赞UML,它也只是一种对模型化系统的“表达能力”,你只能把它当一种辅助表达的工具去使用,它本身既不能帮助思考,也不见得能作为抽象过程中的或抽象思维环境中的参考。
任何一个优秀的架构师都有自己独特的思考方式,这决定了他如何抽象系统,以及如何“创造性地”设计与构画这个系统。这种“独特的思考方式”贯彻他从孩童开始的整个成长过程,直至他形成独立的社会观、人生观与世界观。他认识世界的方式和接受世界的能力决定于他如何思考,也反映了他这种思考方式的“独特性”。但这并不表明他有特立独行的行为特性(我们这里只说他的思考方式),我们不应介意他是否用某种语言(例如UML或者形式化编程语言)来表达他的思考结果。
推动:设计做大,实施做小
架构师首先是把问题的真正目标确定下来,然后变成系统设计、平台设计或架构设计。而在此之后设计输出将会有两个方向的发展,一是被忠实地贯彻下来,二是被变形地发展下去。两个方向都存在致命的危险:架构最终能否被完整实现。对前者来说,可能是架构设计过度,或设计本身出现了错误;后者则是对架构直接的伤害。
所以架构师必须参与实施的全程——尤其是在架构被映射为目标系统的前期。在这个阶段中,架构师的任务就是推动架构实施,以保证在项目全程的设计/架构/体系的一致性。除了直接跟设计师或设计团队沟通,以保证他们的设计在你可以控制的范围之内以外,架构师还必须有阶段化设计的能力。这种能力用于将一个原本规模宏大的架构设计,变成较小的、易于实施的、对开发团队来说可控的关键点。例如在体系层次的规划上,设计可能是独立、异质的、可迁移的存储框架来实现数据层,但在(前期的)实施上,这里可能被表达为本地数据库,并要求前端开发人员必须通过一个清晰的数据交互层来访问——例如一组数据存取接口,或一个独立数据服务组件。开发人员可能在这里遇到障碍:因为要通过这些中间层来访问本地数据库,纯粹是多余的。然而,正是这“多余的工作”提供了系统弹性,为并行团队开发公共存储服务争取了周期,也为将来的灵活部署与数据迁移提供了可能。
这里的关键就在于,无论原始系统设定有多大,实施时总是在“做小”。每一个局部的实施块都是可控的,并为它在整个体系空间中留下了位置和接口,这样才可能由“小的部分”做大。一个大系统的架构师可能同时在考虑许多个项目中的、不同位置的架构,并且清楚这些项目最终的总体规模。而这,就是平台架构师和体系架构师所涉的领域。
dd4
                               图3 架构师模型图中的“实现能力”
架构真的是“好不好”的问题吗?如同我对工程的理解一样,架构问题的根本,也并不在于它是否完美或漂亮,而是在于是否合用。因此架构师必须对实施架构的团队以及实施过程有充分了解,知道他们的能力缺陷,知道实现过程要消耗的资源,清楚每个环节可能的故障以及先兆。只有这样,架构师才能设计一个让这个团队能实现,而且在实现过程中能受控的架构。
要知道,你作为架构师被请来,不是画几张图纸交给项目经理,说:你们去做吧,做不出来是你们不会做。即使你可以身体力行,在这个团队中教大家、培养大家,那么公司的开销呢?风险呢?这些东西难道就不考虑了?项目的周期因为实现的复杂程度而无法控制时,项目就死掉了。那么,追根究底来说,是不是架构师的问题?是啊,你为什么会做了一份“不合用”的架构呢?——你都不知道项目如何开发、由谁实施、如何管理等等,又如何能面对这些实际环境去设计架构呢?
所以这一部分能力,是要在你的开发经验、团队经验以及用人识人的经验中去找的。参考模型图的“实现能力”下的“设计能力→了解你的主要沟通对象”和“架构推行”等分支,对你或有一些可用的提示。
局部与全局
架构是一个从全局到局部的过程,而实施正好反过来,是从局部到全局。这也正是“设计做大,实施做小”的另一个层面的含义。设计大才可以见到全局,才知道此全局对彼全局的影响;实施小才可能关注细节,才谈得上品质与控制。
事实上,大多数情况下架构是在为“当前项目之外”去考虑,这可以看成全局关注的一个组成部分。因此我们需要界定所谓“全局”的范围:超出公司或整个产品系列、产品线或规划的范围才是多余的。
所以当架构决策谈及“全局”时,其目标并不见得是“保障当前项目”,而又必须由当前项目去完成。
一个经常被用到的例子是:如果仅为当前项目考虑,那么只需要做成DLL模块;如果为产品线考虑,可能会是“管道+插件”的结构形式。而“管道+插件”的形式显然比做成DLL模块更费时,这个时间成本(以及其它成本)就变成了当前项目的无谓开销。
这种全局策略对局部计划的影响是大多数公司不能忍受的,也被很多团队所垢病。然而这却是架构师角色对体系的“近乎必然”的影响——如果你试图在体系中引用架构师这个角色的话。一些情况下,体系能够容纳这种影响,例如“技术架构师”试图推动某种插件框架,而正好开发人员对这项技术感兴趣,那就顺其自然地花点工夫去实现了。但如果不是这样,实施者或实施团队看不到“多余的部分”对他们的价值时,来自局部的抵触就产生了。
这种情况下,平衡这些抵触就成了架构推行的实务之一。在我看来,“平衡”是全局的艺术和局部的技术。也就是说,一方面架构师要学会游说,另一方面也要寻求更为简洁的、成本更小的实现技术。只有当整个体系都意识到(你所推行的)架构的重要性,而且实施成本在他们可以接受的范围之内时,他们才会积极行动起来。
所以所谓平衡,其实也是折衷的过程。构架师只有眼中见大,才知道哪些折衷可以做,而哪些不能。所谓设计评估(模型图中的实现能力->设计能力->设计评估分支)并不是去分析一个设计结果好或不好,而是从中看到原始的需求,看到体系全局的意图,然后知道在将设计变得更为“适当”时可以做哪些折衷。同样的原因,架构师也必须知道自己的决策会产生的影响,才能控制它们,以防它们变成团队的灾难。有些时候,架构师甚至需要抛弃一些特性,以使得项目能够持续下去。因为产品的阶段性产出只是整个战略中的一个环节,而不是全部。
其它
“怎么做一个架构师”这个问题得分成两个部分来看,一个是“做到”,一个是“做好”。由于架构师本身不过是一个技术职位,所以时机成熟了自然会做得到。但问题是,真有一天你被放在这个位置上了,你能做得好吗?
我浏览过几套所谓培训机构的有关架构师的教程,也翻阅过一些讲架构的书。我发现他们普遍地是将架构作为一种“职业技术”来讲,就像培养程序员或者缝纫工一样来教育。但就我的经验来说,架构并不是一件纯粹表现技术能力的工作,所以并不是翻几本书学几种方法就可以投入“实战”的。更深层的问题是,架构师其实不是“战”出来的。昨天跟同事讨论这个话题,他把我们这几年来的一些思考用了三句话来概括,非常精彩:从无到有的,是架构;从表到里的,是抽象;从粗到细的,是设计。
那么到底什么是架构呢?从上面的概括中你是看不到答案的。到底如何做架构呢?从本文中你也是看不到答案的。然而我说,“你看不到答案”的根源其实是在于你的眼光与心性——后面这个词换成现代白话,就是“思想”。真正阻碍了你成为优秀架构师的,也许正是你既有的知识与思想方法,扔掉它们,接受一些全然有别的信息,也许正是良好的开端。
或许你现在正愤愤然:这篇文章怎么空洞无物?——我甚至能想象到一些读者的表情。然而请在问题面前停下来,不要急于给出答案。正如你将“?”稍微变下形,它就成为了“!”一样,问题的本身,就是答案。
作者简介
周爱民(aimingoo),具有十余年的软件开发、项目管理和团队建设的经验,现担任盛大网络的平台架构师,著有《大道至简》、《Delphi源代码分析》等。

分类: 学习, 软件设计 标签:

转载:WordPress数据表结构

2010年1月28日 admin 没有评论

wp_categories

用于保存分类相关信息的表。包括了5个字段,分别是:
cat_ID – 每个分类唯一的ID号,为一个bigint(20)值,且带有附加属性auto_increment。
cat_name – 某个分类的名称,为一个varchar(55)值。
category_nicename – 指定给分类的一个便于记住的名字,也就是所谓的slug,这是一个varchar(200)值。
category_description – 某个分类的详细说明,longtext型值。
category_parent – 分类的上级分类,为一个int(4)值,对应是的当前表中的cat_ID,即wp_categories.cat_ID。无上级分类时,这个值为0。

wp_comments

用于保存评论信息的表。包括了15个字段,分别为:
comment_ID – 每个评论的唯一ID号,是一个bigint(20)值。带有附加属性auto_increment。
comment_post_ID – 每个评论对应的文章的ID号,int(11)值,等同于wp_posts.ID。
comment_author – 每个评论的评论者名称,tinytext值。
comment_author_email – 每个评论的评论者电邮地址,varchar(100)值。
comment_author_url – 每个评论的评论者网址,varchar(200)值。
comment_author_IP – 每个评论的评论者的IP地址,varchar(100)值。
comment_date – 每个评论发表的时间,datetime值(是加上时区偏移量后的值)。
comment_date_gmt – 每个评论发表的时间,datetime值(是标准的格林尼治时间)。
comment_content – 每个评论的具体内容,text值。
comment_karma – 不详,int(11)值,默认为0。
comment_approved – 每个评论的当前状态,为一个枚举值enum(’0′,’1′,’spam’),0为等待审核,1为允许发布,spam为垃圾评论。默认值为1。
comment_agent – 每个评论的评论者的客户端信息,varchar(255)值,主要包括其浏览器和操作系统的类型、版本等资料。
comment_type – 不详,varchar(20)值。
comment_parent – 某一评论的上级评论,int(11)值,对应wp_comment.ID,默认为0,即无上级评论。
user_id – 某一评论对应的用户ID,只有当用户注册后才会生成,int(11)值,对应wp_users.ID。未注册的用户,即外部评论者,这个ID的值为0。

wp_linkcategories

用于保存在WP后台中添加的链接的相关信息的表。包括13个字段:
cat_id – 每个链接分类的唯一ID,bigint(20)值,为一个自增量auto_increment。
cat_name – 每个链接分类的名字,tinytext值。
auto_toggle -这个字段所包含的是一个比较特别的属性。如果为Y,则当该分类中加入了新链接时,其它的链接会变为不可见。它是一个枚举型的值enum(’Y’,’N’),默认为N。
show_images – 该字段也是枚举值enum(’Y’,’N’),默认为Y。用户指定是否允许在该链接分类显示图片链接。
show_description – 该字段指定相应的链接分类下的链接,是否再专门[换行]显示它们的说明,这是一个枚举型值enum(’Y’,’N’),默认为N,即不显示说明(但会通过title属性中显示说明)。
show_rating – 显示该分类下链接的等级。它也是一个枚举值enum(’Y’,’N’),默认为Y。此时,你可以用链接等级的方式来对该链接分类下的链接进行排序。
show_updated – 指定该链接分类有更新是,是否进行显示,枚举值enum(’Y’,’N’),默认为Y。
sort_order – 指定该链接分类中链接的排序依据,varchar(64)值。一般用链接的名字(name,即wp_links.link_name)或ID(id,即wp_links.link_id)。
sort_desc – 指定链接分类的排序方式,枚举值enum(’Y’,’N’),默认为N,即用降序。
text_before_link – 该链接分类下每个链接的前置html文本,varchar(128)值,默认是’列表开始标签’。
text_after_link – 该链接分类下每个链接的中,链接与说明文字(wp_links.link_description)之间的html文本,varchar(128)值,默认是’换行标签’。
text_after_all – 该链接分类下每个链接的后置html文本,varchar(128)值,默认是’列表结束标签’。
list_limit – 用于规定某一链接分类中显示的(可设定的?)链接的个数,int(11)值,默认为-1,即对链接分类下链接的个数无限制。

wp_links

用于保存用户输入到Wordpress中的链接(通过Link Manager)的表。共14个字段:
link_id – 每个链接的唯一ID号,bigint(20)值,附加属性为auto_increment。
link_url – 每个链接的URL地址,varchar(255)值,形式为http://开头的地址。
link_name – 单个链接的名字,varchar(255)值。
link_image – 链接可以被定义为使用图片链接,这个字段用于保存该图片的地址,为varchar(255)值。
link_target – 链接打开的方式,有三种,_blank为以新窗口打开,_top为就在本窗口中打开并在最上一级,none为不选择,会在本窗口中打开。这个字段是varchar(25)值。
link_category – 某个链接对应的链接分类,为int(11)值。相当于wp_linkcategories.cat_id。
link_description – 链接的说明文字。用户可以选择显示在链接下方还是显示在title属性中。varchar(255)值。
link_visible – 该链接是否可以,枚举enum(’Y’,’N’)值,默认为Y,即可见。
link_owner – 某个链接的创建人,为一int(11)值,默认是1。(应该对应的就是wp_users.ID)
link_rating – 链接的等级,int(11)值。默认为0。
link_updated – 链接被定义、修改的时间,datetime值。
link_rel – 链接与定义者的关系,由XFN Creator设置,varchar(255)值。
link_notes – 链接的详细说明,mediumtext值。
link_rss – 该链接的RSS地址,varchar(255)值。

wp_options

用于保存Wordpress相关设置、参数的表,共11个字段。最重要是的option_value字段,里面包括了大量的重要信息。
option_id – 选项的ID,bigint(20)值,附加auto_increment属性。
blog_id – 不详。或许用在单在用户的WP版本上并不重要吧,或许是针对不同用户的Blog来设置的一个值。int(11)值,默认为0,即当前blog。
option_name – 选项名称,varchar(64)值。
option_can_override – 该选项是否可被重写、更新,枚举enum(’Y’,’N’)值,默认为Y,即可被重写、更新。
option_type – 选项的类型,作用不详,int(11)值,默认为1。
option_value – 选项的值,longtext值,这个字段的内容比较重要。Wordpress初始化时就会设定好约70个默认的值,这里暂不介绍。
option_width – 选项的宽(?),作用不详。int(11)值,默认为20。
option_height – 选项的高(?),作用不详。int(11)值,默认为8。
option_description – 针对某个选项的说明,tinytext值。
option_admin_level – 设定某个选项可被操纵的用户等级(详情见我的相关文章),int(11)值,默认为1。
autoload – 选项是否每次都被自动加载,枚举enum(’yes’,’no’)值,默认为yes。

wp_post2cat

用于保存文章(posts)与分类(categories)之间的关系的表,只有三个字段:
rel_id – 关联ID,bigint(20)值,是个有auto_increment属性的自增量。
post_id – 文章的ID,bigint(20)值,相当于wp_posts.ID。
category_id – 分类的ID,也是bigint(20)值,相当于wp_categories.ID。
文章与分类的关系的形成是这样的:rel_id是一个不断增加的自增量,它用于识别每不同的post。post_id可以重复(当它对应多个分类时),因为它可被rel_id识别,所以不会出现混乱。每个post_id可对应多个category_id时,一个rel_id + post_id组合,可以识别某一个分类,因此每个文章的分类可以是不同的。通过这张表,可以非常快速、高效地找出某篇文章(post)对应了哪些分类 (category),反之亦然。

wp_postmeta

用于保存文章的元信息(meta)的表,四个字段:
meta_id – 元信息ID,bigint(20)值,附加属性为auto_increment。
post_id – 文章ID,bigint(20)值,相当于wp_posts.ID。
meta_key – 元信息的关键字,varchar(255)值。
meta_value – 元信息的值,text值。
这些内容主要是在文章及页面编辑页(Write Post, Write Page)的”Add a new custom field to this post(page):”下进行设定的。meta_key就对应名为”key”的下拉列表中的项,而值由用户自己填上(某些时候,wp也会自动加入,如文章中有的音频媒体)。

wp_posts

用于保存你所有的文章(posts)的相关信息的表,非常的重要。一般来讲,它存储的数据是最多的。一共包括了21个字段。
ID – 每篇文章的唯一ID,bigint(20)值,附加属性auto_increment。
post_author – 每篇文章的作者的编号,int(4)值,应该对应的是wp_users.ID。
post_date – 每篇文章发表的时间,datetime值。它是GMT时间加上时区偏移量的结果。
post_date_gmt – 每篇文章发表时的GMT(格林威治)时间,datetime值。
post_content – 每篇文章的具体内容,longtext值。你在后台文章编辑页面中写入的所有内容都放在这里。
post_title – 文章的标题,text值。
post_category – 文章所属分类,int(4)值。
post_excerpt – 文章摘要,text值。
post_status – 文章当前的状态,枚举enum(’publish’,’draft’,’private’,’static’,’object’)值,publish为已发表,draft为草稿,private为私人内容(不会被公开) ,static(不详),object(不详)。默认为publish。
comment_status – 评论设置的状态,也是枚举enum(’open’,’closed’,’registered_only’)值,open为允许评论,closed为不允许评论,registered_only为只有注册用户方可评论。默认为open,即人人都可以评论。
ping_status – ping状态,枚举enum(’open’,’closed’)值,open指打开pingback功能,closed为关闭。默认值是open。
post_password – 文章密码,varchar(20)值。文章编辑才可为文章设定一个密码,凭这个密码才能对文章进行重新强加或修改。
post_name – 文章名,varchar(200)值。这通常是用在生成permalink时,标识某篇文章的一段文本或数字,也即post slug。
to_ping – 强制该文章去ping某个URI。text值。
pinged – 该文章被pingback的历史记录,text值,为一个个的URI。
post_modified – 文章最后修改的时间,datetime值,它是GMT时间加上时区偏移量的结果。
post_modified_gmt – 文章最后修改的GMT时间,datetime值。
post_content_filtered – 不详,text值。post_parent – 文章的上级文章的ID,int(11)值,对应的是wp_posts.ID。默认为0,即没有上级文章。
guid – 这是每篇文章的一个地址,varchar(255)值。默认是这样的形式: http://your.blog.site/?p=1,如果你形成permalink功能,则通常会是: 你的Wordpress站点地址+文章名。
menu_order – 不详,int(11)值,默认为0。
post_type – 文章类型,具体不详,varchar(100)值。默认为0。
post_mime_type – 不详。varchar(100)值。
comment_count – 评论计数,具体用途不详,bigint(20)值。


wp_usermeta

用于保存用户元信息(meta)的表,共4个字段:
umeta_id – 元信息ID,bigint(20)值,附加属性auto_increment。
user_id – 元信息对应的用户ID,bigint(20)值,相当于wp_users.ID。
meta_key – 元信息关键字,varchar(255)值。
meta_value – 元信息的详细值,longtext值。
这是在Wordpress 2.0中才新加入的表。


wp_users

用于保存Wordpress使用者的相关信息的表。WP官方对2.0.2版本中该表的情况的说明有些矛盾(称有22个字段,但详细的列表中只有11个),所以这里只能列出11个字段进行说明:
ID – 用户唯一ID,bigint(20)值,带附加属性auto_increment。
user_login – 用户的注册名称,varchar(60)值。
user_pass – 用户密码,varchar(64)值,这是经过加密的结果。好象用的是不可逆的MD5算法。
user_nicename – 用户昵称,varchar(50)值。
user_email – 用户电邮地址,varchar(100)值。
user_url – 用户网址,varchar(100)值。
user_registered – 用户注册时间,datetime值。
user_level – 用于等级,int(2)值,可以是0-10之间的数字,不同等级有不同的对WP的操作权限。
user_activation_key – 用户激活码,不详。varchar(60)值。
user_status – 用户状态,int(11)值,默认为0。
display_name – 来前台显示出来的用户名字,varchar(250)值。

分类: 网站, 软件设计 标签:

疯狂的sql查询,随时可能引发性能问题

2010年1月26日 admin 没有评论

今天review了群发sms的一段代码,差点没吓死我。 代码如下

for (MtnPlanTosendlist mtnPlanTosendlist:tempList){
           int memberSendTodayCount = getToDayLimit(defaultBo,getMemberSentDal(mtnPlanTosendlist));
           int memberSendMonthCount = getMonthLimit(defaultBo,getMemberSentDal(mtnPlanTosendlist));
           if ((memberSendTodayCount < smsTodayLismitMember) && memberSendMonthCount < smsMonthLimitMember){
              insertList.add(mtnPlanTosendlist);
           }
}

public static int getToDayLimit(BaseBo defaultBo, MtnSmsLog mtnSmsLog) {
       int count = (Integer) defaultBo.get(mtnSmsLog, COUNT_TODAY_EXT)
              .getDefaultModel();
       return count;
}

public static int getMonthLimit(BaseBo defaultBo, MtnSmsLog mtnSmsLog) {
       int count = (Integer) defaultBo.get(mtnSmsLog, COUNT_MONTH_EXT)
              .getDefaultModel();
       return count;
}

这段代码看的出问题吗? For 循环类名套了2个count 查询。如果前面的那个for循环有100个的话, 执行完这个代码就需要200行的sql(远程调用),悲惨呀!

开发人员切莫要把数据库操作当做local方法一样的使用。一则,db操作本质上也是远程访问,远程调用很浪费时间,特别有大量的远程访问的时候;二则,数据库资源有限,大量的数据库访问会导致数据库load的升高,从而影响程序的性能。所以。劲量要减少对数据库的访问,例如通过batch进行操作,或者batch的load到内存中,进行处理。另外,如果count的数量数据行太多的话就需要考虑中间结果,例如做一张中间表来记录当前月、当天的数量,这样也可以有效的减少数据库的访问。

分类: 软件设计 标签:

对产品化和平台化的思考

2010年1月17日 admin 1 条评论

2009年4月加入公司以后做的第一个项目是给公司的事业部A做个类似Google Calendar的时间、任务管理平台,使事业部A能够在这个平台上开展一项具体的业务,并加以管理和统计。所以公共Calendar服务差别最大的一点是我们的Calendar需要实现对事业部A或者未来其他事业部的“应需而变”。当时做第一版设计的时候就提出了“抽象出一个通用的任务制定、整理、展现的平台。这个平台可以方面的加以扩展以满足多种业务对任务管理的需求”这样一个目标,可在经过之后的若干次的业务和功能的扩展以后,我发现这个产品离产品化和平台化越来越远,直到最后成为了事业部A的专属产品。最近的一段时间内对这个产品如果实现产品化和平台化做了进一步的思考,这篇文章就是这些思考的汇总。

2009年4月的第一版设计——基础设计的失败

当时在设计的时候,有两个设计重点。其中一个是“任务”这个概念,当时主要的精力放在了对任务的抽象上,所以针对任务这个对象设计了开始时间、结束时间、重要度、标题,还包括了任务的创建、完成、推迟等行为。当时考虑如果通用的任务概念无法满足某个事业部的需求时就为这个事业部定制“任务”这个概念,这种设计的具体表现体现在当时设计了一张Task表并预留了许多字段,并且自欺欺人地认为这些字段能够支持未来业务平台的扩展。

剩下的另一个设计重点是如何为这个业务部门定制Calendar的展现样式。业务部门的需求是要根据这个事业部的需求定制一个Calendar的每日单元格。当时对Calendar控件提出了“能够支持日历、月历、周历等多种操作模式的变换以取得类似Outlook中日历功能的效果”,“Calendar容器能够支持多种Widget的Plug in。有需要使用Calendar功能的业务可以根据自身业务需求定制Widget,并能方便地跟Calendar容器整合在一起。”。Calendar控件在之后得到了基本的实现,但应需而变的Widget方案并没有能够实现。

当时就是带着这两个巨大的问号,我们完成了这个功能的第一版。

2009年12月的第三版功能升级——平台化的终结

2009年12月还是这个事业部提出了很多“管理”方面的需求,这些需求使得这个事业部成为了这个产品的“惟一”主人。

2010年1月——新的思考

对于一个大型的企业应用需求往往总是来自于某个较小单位,并逐步的扩展到更大的范围,而另一方面软件设计往往力求能最大程度的支持未来业务的扩展,也就是想实现产品化、平台化的目标,当有新的需求需要实现的时候,能够付出最小的成本。这两者之间某种程度上存在着很大的矛盾,要解决这种矛盾,我目前的思考是要依赖以下几点内容

  • 分析需求,识别出需求涉及的领域。如果没有对需求进行领域的划分,或者划分是错误的,结果只有一个——产品化和平台化的终结。
  • 在领域划分的基础上,规划每个领域的基础、高阶服务。每个领域可能会有若干个层次的服务:基础、高阶。以Calendar领域为例,基础服务可能就有任务的管理(创建、删除),以日历、月历、周历等多种操作模式的变换。高阶的服务可能包括时间冲突的提醒,任务的提醒等等。
  • 将具体的需求通过,某个领域的服务,或者多个领域的服务组合来加以实现。组合式的服务基于几个领域的基础服务构成一个新的服务。每个领域的基础服务要力求“最强大化”,否则整个平台的扩展就会受到很大的限制。

image

还是以那个事业部A的需求为例。

  • 我们识别出需求涉及Calendar和A事业部两个领域。
  • Calendar领域提供的基础服务包括任务、时间的管理、Calendar页面的展现,高阶服务包括任务提醒等等。A事业部领域包括了具体任务的制定业务、任务执行的监督、任务执行状态在Calendar页面的展现、任务执行的具体内容和后续跟踪等等。
  • 通过将Calendar领域的任务管理服务和A事业部领域的具体任务制定服务组合在一起实现A事业部任务安排的需求。将Calendar领域的任务管理服务和A事业部领域的结果反馈服务组合在一起完成A事业部的结果反馈需求。
  • 当后续B事业部想要在Calendar平台上开展自己的业务的时候,由于Calendar领域的服务没有夹杂任何其他领域的内容并且实现了功能的最大化能够很有效的被B事业部加以重用。

 

总结

  • 分析需求,识别出需求涉及的领域。对领域间的隔离态度上是“决不妥协的”。
  • 每个领域的服务要追求最大化、最强大化。
  • 具体的需求通过,某个领域的服务,或者多个领域的服务组合来加以实现。
分类: 软件设计 标签:

SOA实践指南读书笔记

2010年1月16日 admin 没有评论

最近读完了《SOA实践指南——分布式系统设计的艺术 》这本书,以下是这次的读书笔记。

什么是SOA  

在计算科学中,术语“面向服务的架构”(SOA)表达了一种软件架构的概念,它定义为使用服务来满足满足软件用户的需求。在SOA环境中,网络上的节点以独立服务的形式将自己的资源开发给网络上其他参与者,其他参与者按一种标准的方式使用资源。大多数对于SOA的定义都提到了实现SOA时使用Web Services(即使用SOAP或REST)。然而,可以使用任何基于服务的技术实现SOA。

……

与传统点对点架构不同,各种SOA都由松耦合、高度可以互操作的应用服务构成。这些服务基于某种格式定义进行互操作,该定义独立于底层平台和编程语言(例如,WSDL)。接口定义封装(隐藏)了供应商和语言相关的实现。SOA独立于开发技术(比如JAVA和.NET)。由此,软件组件将具备非常高的重用性,因为接口按与标准兼容方式定义。

基于SOA的逻辑架构模型

image

无状态服务为什么更好

    首先,对于无状态服务来说,在服务层做到负载均衡和失效备援相当简单。ESB仅需实现能使用下一个最容易获得的服务实例即可。它甚至可以是其他消费进程以前使用过的线程。这意味着如果一个系统失败了,其他系统可以轻易地接手,继续工作。如果服务层的通量不够高,你可以将服务实例的数量翻倍,从而是通量翻倍。这意味着解决方案能线性伸缩,并且,如果一个服务实例意外死亡,其他服务实例能投入进来。

而有状态服务的缺点是,它们绑定在会话上。这样一来,知道消费者会话结束(或者连接失效)之前,为它们分配的资源都绑定在一个特定的消费会话上。除此以外,你必须能让ESB把连续的服务调用路由到同样的服务实例上。

然而,要注意的是,有状态服务仍然可以线性伸缩。使用一种“粘性”路由的政策,根据可用的资源,你在第一个服务调用被执行时找到你的服务实例。这一来,对服务实例数量的翻倍就又能将可能的会话数量翻倍了。然而,此时不具备服务资源可以在不同消费者之间共享的优点。并且,如果一个服务实例意外死亡,会话状态就丢失了。

同时也要注意,有状态服务可以有失效备援机制(并且仍然可以线性伸缩)。此时,每个服务器不但返回它自己的会话ID,还返回失效备援会话的ID。只要这个失效备援会话不在一个中央服务器上,而是在邻近的服务器上(并且,每个服务器的邻近服务器各不相同),这样就不会造成瓶颈。

等幂性

等幂性是服务的一种可能的属性。在服务的上下文环境中,它意味着对同一服务调用的多次递交/处理不会引发问题。假设你有一个向银行中增加钱的服务。如果一个消费者调用这个服务(例如,为了处理银行转账)并且没有得到应答,消费者不会知道服务的请求还是服务的应答失败了。例如下图中,服务可能在请求处理之前或之后丢失。

image

在后一种情况下,从供应者的角度来看,服务成功了,指定数量的钱被增加到了银行账户上。在前一种情况下,服务没有影响(即没有增加钱)。

当服务的消费者没有得到应答时,它不知道服务是否成功了。为求确保,它可能重试服务调用,再次发出同样的请求。结果是,达到供应者的服务请求可能有一次或两次。如果服务是幂等的,那么,服务请求到达多少次都没有关系:同一服务调用的多次请求只产生一次的效果。

刚刚描述的银行服务不是幂等的,因为,它依赖于第一次服务调用失败发生在何处,客户的余额可能最终被增加了两次,而不是消费者想要的一次。为了避免这样的问题,通常说来,只要有可能就是服务幂等是个好主意。

所有的读取服务都是幂等的,因为一个服务供应者无论多频繁地执行返回数据的请求都没有关系。然而,要注意,一旦做了类似每个请求都写一个备忘录项这种事,读取服务就变成了写入服务。如果这个备忘录和业务有某种关联,这就可能成为问题(例如,对同一样的事项,最终可能有多个备忘录项)。

写入服务可能是幂等的,也可能不是。当一个服务调用的影响依赖于后端现有的状态时,写入服务不是幂等的。举个例子,在前面的例子中,银行账户的最终余额依赖与最初的余额。如果开始的余额是500美元,服务调用增加100美元,结果将是600美元。如果因为最初的应答丢失了,另一个请求到达并且得到处理,最后的余额将是700美元。

一个幂等的写入服务的例子是,一个通过发送所有地址来设定客户地址的服务。供应者处理多少次请求都没有关系,结果将是一样的。

注意:你总是可以避免让服务不幂等。例如,我们可以修改银行服务语义,发送新值而不是应该增加的值,从而使服务幂等。这就是说,不是调用:

addToBalance(100);

我们可以调用:

setBalance(600);

这个调用可以反复地发送和处理,余额的结果都会是600。当然,其他服务也能在相同的账户中增加或减少资金,这就带来了问题。这就带来了问题。你如何才能知道最终的余额应该是多少呢?正如你看到的,从业务的角度来看,让服务幂等可能变得复杂。

除此以外,从业务角度来看,有的服务内生就是不幂等的。所有创建某些东西的服务都没有办法和一个状态去比较(除非你知道创建新资源的工厂的状态)。例如,如果你有一个创建银行账户的服务,你就是在创建账户,仅此而已。

如果从业务的角度来看,引入幂等性是个难题的话,那么,你需要一些对幂等性的技术支持。实现幂等性的普通方法相当简单,消费者发送数据的方式必须能让供应者看的出两个技术性的请求其实是同一个。为此,消费者可以随着每次新请求发送一个唯一的ID。如果消费者没有得到应答,它的每次重试都是同样的ID。下图是对此的图解。

image

在处理每个进入的请求前,供应者应将请求及相关联的唯一ID存储到一个中央数据库中。如果供应者收到一个ID已经在存储库中的请求,它就知道该请求已经被处理过。于是,供应者就不会再处理它,而是向消费者重新发送最初的应答(应答在第一次被送出去之前,也保存下来)。如果第二次请求到达是,服务实现正处于运行中,则供应者可以回复一个应答,说服务调用在运行中,或者供应者可以等到第二次请求处理完毕以后,再次把应答发送出去。

作为一项优化,消费者能和请求一起发送一个标志,这样供应者就知道此请求是否是一个重试。这样能做到改善性能,因为如果调用没有被标记为重试的华话,供应者就不必验证请求是否已经在存储库中了。

分类: 软件设计, 阅读 标签: