天天看点

单例模式与MX 记录

本章的内容来自一个真实的华尔街金融网站项目。

本章假设读者对使用JavaMail 库通过SMTP(Simple Mail Transfer Protocol)服务器发

送电子邮件有所了解,并且已经阅读过本书的“单例(Singleton)模式”一章。在运行本章

提供的代码之前,请到 www.javasoft.com 下载最新版的JavaMail 库。

16.1 问题与解决方案

问题

相信很多读者都接触过可以自动发送电子邮件的系统,大家想必知道可以利用

JavaMail 库通过一个SMTP 服务器发送电子邮件的工作。一般来说,一个公司会有数个

SMTP 服务器,虽然每一台服务器都会定期停机维护检查,但是公司总会保持至少一台

SMTP 服务器正常运行,以便处理发送电子邮件的工作。

假设一个公司有一个J2EE 系统需要自动发送电子邮件,而且这项工作必须是24/7 运

行的,也就是说是全天候的。换言之,不能因为某一台SMTP 服务器停机就停止发送。与

此同时,SMTP 服务器又不能不定时停机维护。这时候就需要系统架构设计师解决这个问

题。

于是项目经理将几个设计师请到会议室,并说明问题所在,并要求设计师提供可能的

设计方案。经过短暂的讨论,设计师们提出了两个可选方案。

第一个方案

安装一台自己的SMTP 服务器,由系统维护人员维护这台服务器。这台服务器不一定

要直接将电子邮件投递到客户那里,它只需要将电子邮件转投给公司里正在运行的SMTP

服务器就可以了。由于SMTP 服务器可以从DNS 服务器那里拿到正在工作的SMTP 服务

器的名字,因此可以保证邮件总可以发送出去。

这当然是可以的,只是维护一台SMTP 服务器等于添加了额外的维护工作,而且谁能

保证这台服务器不需要停机检修呢?

Java 与模式 ·250·

第二个方案

在方案一中需要自己维护的那个SMTP 服务器(以下称为源服务器)之所以能够找到

正在运行的SMTP 服务器(以下称为目标服务器),并将邮件转投给它,是因为源服务器能

够利用DNS 的目录服务进行目录查询。DNS 会向整个网络提供所有登记过的SMTP 服务

器的名字,这样源服务器可以一个一个地试验目标服务器清单上的所有名字,直到找到一

台正常运行的目标服务器为止。

使用Java 的JNDI 功能,Java 程序可以做同样的事情。这也就是说,可以写一个Java

程序自动从DNS 服务器那里得到一个公司内登记过的所有的SMTP 服务器的清单(称为

MX 记录),然后让它一个个地试验清单上所有的服务器,直到把邮件送出去为止。由于公

司内总会有至少一台SMTP 服务器是正常工作的,这样就可以保证总可以将邮件送出去。

第二个方案的进一步完善

如果只需要多写20 行代码,就可以省去日复一日的服务器维护工作,这当然是再好不

过的。于是,这个方案被列为首选方案加以讨论,以便能够最终得到一个完善的解决方案。

下面就是综合讨论中提出的各种意见达成的设计方案:

(1)既然需要JNDI 和DNS,那么不妨到Sun Microsystem 的网站去下载最新的库。

(2)不应当每一次发送邮件时都做DNS 查询,应当创建一个类负责查询和保存查询

所得的结果。

(3)MX 记录是一个相当静态的表,所以整个系统只需要一份表。系统在任何时候需

要发送电子邮件,就只需向这个对象调用这份列表即可。

所有这些都指向单例模式。

单例模式的使用

不能忘记本书引入这个例子是为了说明单例模式的使用,因此,有必要借此机会强调

一下为什么这里应当使用单例模式,以及在这里使用单例模式应当注意的地方。

首先,正如设计师们在讨论中所指出的,在整个系统中只需要一份这样的MX 记录表。

这当然就是使用单例模式的最重要的理由。

其次,发送电子邮件的操作可能随时触发。也就是说,这个保存MX 记录表的对象应

当在全运行时期时间存在,而不应当被垃圾收集器所收集。了解垃圾收集器的读者可能会

意识到,这就需要系统内保持至少一个对它的引用。那么由谁来保存对这个对象的引用,

以保证它不被收集呢?当然是它自己最好。这样,只要这个单例对象一旦被创建,就会永

远不会收集,直到服务器环境被重启为止。这也就是在这里使用单例模式的另一个重要的

理由。

因此,单例模式最适合使用在这个系统设计中。

第16 章 专题:单例模式与MX 记录 ·251·

为了给不熟悉JNDI,DNS 和MX 记录等概念的读者一个熟悉这些概念的机会,在进

行系统设计之前对这些概念进行一下复习。已经熟悉这些概念的读者可以跳过下面的几个

小节,直接阅读“系统设计”一节。

16.2 目录服务与MX 记录

命名–目录服务

一个生活中的命名–目录服务的例子就是电话台提供的电话目录查询服务。这个服务将

把列在电话簿上的商家名字、地址和电话联系起来。

计算机的命名–目录服务是计算机系统的一个基本工具,而且比电话目录服务更加强

大。命名–目录服务将一个对象与一个名字联系起来,使得客户可以通过对象的名字找到这

个对象。目录服务允许对象有属性,这样客户端通过查询找到对象后,可以进一步得到对

象的属性,或者反过来根据属性查询对象。

最常见的目录服务包括LDAP(Lightweight Directory Access Protocol)和DNS(Domain

Name Service)。

什么是DNS

DNS 即域名服务(Domain Name Service),电脑用户在网络上找到其他的电脑用户必

须通过域名服务。在国际网络以及任何一个建立在TCP/IP 基础之上的网络上,每一台电脑

都有一个惟一的IP 地址(Internet Protocol Address)。这些IP 地址就像街道上的门牌号码,

使得其他的电脑能找到某一台电脑。DNS 服务器提供DNS 服务。

MX 记录(MX Record)

MX(Mail Exchange)记录就是邮件交换记录。电子邮件服务器记录指定一台服务器

接收某个域名的邮件。也就是说,发往jeffcorp.com 的邮件将发往mail.jeffcorp.com 的服务

器,完成此项任务的MX 记录应当像下面这样

jeffcorp.com. IN MX 10 mail.jeffcorp.com

在上面的这个记录的最左边是国际网络上使用的电子邮件域名;第三列是一个数字

10,它代表此服务器的优先权是10。通常来说,一个大型的系统会有数台电子邮件服务器,

这些服务器可以依照优先权作为候补服务器使用。优先权必须是一个正整数,这个数字越

低,表明优先权越高。

Java 与模式 ·252·

16.3 JNDI 架构介绍

JNDI 的全称是Java 命名和地址界面(Java Naming and Directory Interface),是在1997

年初由Sun Microsystem 引进的,其目的是为Java 系统提供支持各种目录类型的一个一般

性的访问界面。

JNDI 架构由JNDI API 和JNDI SPI 组成。JNDI API 使得一个Java 应用程序可以使用

一系列的命名(naming)和目录(directory)服务。JNDI SPI 是为服务提供商,包括目录

服务提供商准备的,它可以让各种命名和目录服务能够以对应用程序透明的方式插入到系

统里。在JNDI 架构图中,给出了几个命名和目录服务作为例子,如下图所示。

JNDI API 由下面的四个库组成: