分类目录归档:golang

将您的 web 应用程序转化为多租户 SaaS 解决方案

想象一下,您有一个一直在市场上出售的 web 应用程序。您了解到云基础架构中的软件即服务 (SaaS) 是行业的未来趋势。您意识到您需要它,并且您的客户也要求您提供 SaaS 版本的产品。

问题是,您需要快速、有效地转换为 SaaS,并且最好能够维持或增强您的盈利能力。

SaaS 应用程序和常规的 web 应用程序之间存在许多差异需要加以考虑。一些与技术有关,而另一些与业务模型的变化有关,公司在交付 SaaS 时必须能够适应这种变化。

典型的 web 应用程序和 SaaS

SaaS 区别于其他应用程序的主要特征就是能够使客户在使用应用程序时按照使用量付费。他们不需要为软件购买许可,也不需要安装、托管和管理它。这方面的操作全部由提供 SaaS 软件的组织负责。

多租户是实现成功 SaaS 的关键

尽管订阅应用程序的能力已经最低程度地满足 SaaS 的基本条件,但是在实际中仍然不能满足要求。实际上,SaaS 应用程序必须同时也是一种多租户应用程序

归根结底都是与经济方面的因素有关。主要的 SaaS 公司的 CEO 们都一致认为,如果没有多租户,那么发展 SaaS 业务是绝无可能的。(参见边栏)。

多租户体现了 SaaS 的效率水平

影响效率的主要因素来自于多租户,即能够容纳应用程序的不同用户,同时使用户觉得应用程序完全服务于他们。我们已经习惯这种概念,因为它应用于系统中的各个用户,但是与 SaaS 环境仍然有些细微的差别。在一个典型的企业 SaaS 环境中,用户是指某个特定组织的员工组成的多个组,这个组织就是所谓的租户。这类似于您在一个普通的 web 应用程序中看到的一样,如果组织购买了这个应用程序的话;组织的员工就是应用程序的用户,而该组织就是所有者。在 SaaS 模型中,组织是租户,而非所有者,但是组织的员工仍然是用户的身份。每名用户都具体关联到一个特定租户(组织),而 SaaS 为每个租户分别提供一份应用程序的副本,以供他们的用户使用。

云中的虚拟化影响 SaaS

简单 web 应用程序和支持云的 SaaS 应用程序之间的区别涉及 IT 行业目前最主要的两个容量利用特性:

  • 多租户(之前引入)
  • 硬件虚拟化。

虽然应用程序的效率主要取决于应用程序的多租户架构,然而第二个影响效率的因素是硬件虚拟化。通过利用虚拟化技术,减少在使用普通的数据中心物理机器方法时产生的未使用的容量,云环境极大地提高了给定数量的硬件的利用率。

此外,云还提供了根据资源需求动态地为应用程序重新分配硬件的潜力。在较短的时间(几分钟)或从长远来看(几个月),这种灵活性都有助于将硬件与单一应用程序分离开来,然后再将它们分散到大量应用程序中,从而消除了差异,使硬件投资变得更加容易预测和管理。

现在,让我们看一下将一个比较传统的 web 应用程序转换为一个支持 SaaS 的应用程序通常需要执行哪些步骤。

将 web 应用程序转换为 SaaS 应用程序

要将您的 web 应用程序转换为 SaaS 应用程序,需要满足以下 7 个条件:

  1. 应用程序必须支持多租户
  2. 应用程序必须具备某种程度的自助注册功能。
  3. 必须具备订阅/记账机制。
  4. 应用程序必须能够有效地扩展。
  5. 必须能够监视、配置和管理应用程序和租户。
  6. 必须有一种机制能够支持惟一的用户标识和身份验证。
  7. 必须有一种机制能够支持对每个租户进行某种程度的自定义。

让我们逐个地详细了解这 7 个条件。

支持多租户

多租户是决定 SaaS 效率的关键因素。通常,应用程序支持多个用户,但是前提是它认为所有用户都来自同一个组织。这种模型适用于未出现 SaaS 的时代,组织会购买一个软件应用程序供自己的成员使用。但是在 SaaS 和云的世界中,许多组织都将使用同一个应用程序;他们必须能够允许自己的用户访问应用程序,但是应用程序必须只允许每个组织自己的成员访问其组织的数据。

能够让多个组织(SaaS 中的术语称为租户)共存于相同的应用程序,同时不会破坏这些组织的数据的安全性,具备这种能力的应用程序就可以称之为多租户应用程序。

多租户可以分为几个不同的类别(如列表下方的图所示):

  1. 云中的简单虚拟化,其中只对硬件进行共享。
  2. 共享应用程序,对每个租户使用不同的数据库。
  3. 共享应用程序和数据库(效率最高,真正的多租户)。
多租户模型

多租户模型

可以认为,第一个模型根本算不上是真正的多租户,但是在云环境中,它经常与虚拟化服务器配合使用,并被认为是一种多租户形式。在实际应用中,这种模型无法满足需求,并且仅仅比使用专用硬件的旧的 ASP 模型稍微好用一些。

效率最高的模型就是应用程序充分地共享数据库和应用程序的业务逻辑。实现这种模型非常的困难,因为需要修改数据库模式,向每个表和视图添加一个租户标识符,以及重写每一个 SQL 访问,向过滤器添加租户过滤条件。如果代码中任何必要的部分缺少这一逻辑,那么都将损害应用程序的数据安全性。

除了应用程序中出现的数据访问实例外,还存在其他一些应用程序,比如报告编写器(writer)和工具应用程序,必须修改这些应用程序来包含必需的租户过滤功能,从而确保各个租户的数据只能由这些特定的租户访问。如果访问类型并不是直接来自于应用程序本身,那么就会产生问题,必须对这类问题进行控制。如果任何已授权的用户可以编写报告,那么必须阻止他们访问不属于其所属租户的数据。

自助注册

应用程序必须具备某种程度的自助注册功能,即便仅仅是一种请求机制,即产生一种向应用程序添加租户的业务流程。

订阅和记账

必须提供订阅和记账机制。因为 SaaS 应用程序被设计为根据各种因素进行支付,如每个租户的用户数、应用程序选择,还可能包括使用时间等,必须通过某种方式来跟踪和管理应用程序的使用,然后生成可由租户管理人员访问的记账信息。

扩展和管理应用程序

必须能够随着订阅的增长进行扩展。云基础架构是实现这一目的的逻辑方式,因为它嵌入了许多功能,可以支持您实现有效的、高效的扩展。

同样,必须提供治理和应用程序管理功能,以监视、配置和管理应用程序及所有租户。

用户 ID 和身份验证

需要提供一种机制以支持用户标识和身份验证,允许用户拥有惟一的身份标识。由于多租户要求识别注册到系统中的所有用户,从而确定他们所属的租户,因此必须有一种明确的关系来将用户识别为属于某个特定租户。这种用户与租户的关系是非常重要的信息,用于限制用户可以访问的数据。

电子邮件地址是实现这一目的的一种典型方法,通过这种方式可以确保惟一性,每个用户可以被识别和标识为属于某个特定租户。

有多种身份验证机制和集成方法,因此必须具备一种灵活的机制来识别用户。通常,某个特定的租户需要能够利用其现有的 LDAP 或其他目录服务或身份验证机制来支持对 SaaS 应用程序的单点登录。尽管这种外部的用户身份验证非常重要,但是,需要由 SaaS 应用程序确定已识别的用户是属于它们所宣称的租户的成员。

对每个租户进行自定义

必须提供一种机制来支持对每个租户进行一定程度的基本自定义,从而使它们具有惟一的 URL、登入页面、标识、配色方案、字体,甚至包括语言。

对每个租户的基本配置是预期的功能,但是要真正地满足多租户的需求,必须在基本配置的基础上对每个租户实现一定程度的自定义。

所需的典型定制类似于租户对内部应用程序版本所作出的定制。包括添加字段甚至是表格,设置特殊的业务逻辑,或集成另一种应用程序。在每个租户的基础上能够进行这类自定义,同时无需建立单独的实例(否则会降低多租户设计的效率),这就是高性能 SaaS 架构的典型特征。

需要考虑的性能问题

多租户 SaaS 应用程序的性能问题通常与那些容纳同样数量的用户、具备相同的活动程度的 web 应用程序所遇到的问题相同。有许多不错的方法可以解决 web 应用程序中日益增长的容量需求问题;通常来讲,这些方法也都适用于多租户 SaaS 应用程序。这些技巧通常包括横向/纵向扩展和在一组服务器之间实现负载均衡

横向/纵向扩展

云基础架构提供了许多以动态的、自动化的方式实现这种可扩展性的机会,从而能够在需要时提供资源,以及在使用更少的资源就可以满足性能服务水平协议(SLA)的情况下相应地减少资源。可以对这种灵活的能力进行调整,从而能够在提供服务的同时避免造成资源浪费:

  • 横向扩展通常用于应用服务器层。
  • 纵向扩展通常用于数据库层。

数据库集群化

对于 SaaS 应用程序,成功产品的用户总数可能会非常的高;在某些情况下,对数据库进行纵向扩展可能并不是一种好方法。许多数据库技术都能够提供一种集群化的数据库模型,允许对同一个数据库提供更多的容量。DB2® 提供的一些选项可以很好地应用于这种模型,并且可以用来创建数据库集群。

地理、分区和同步

然而,取决于 SaaS 应用程序及其用户群,其他一些技术也许更加合适。由于 SaaS 可以从世界上的任何位置访问,因此其用户群可能位于非常远的地方;这种距离会由于较长的网络拓扑而导致性能下降。

对于这类情况,使用分布在不同地区的云、对数据进行分区或使用同步维护一致性的效果可能会更好一些。究竟选择哪一种方法取决于具体应用程序的性质。一些用户可能无法接受长距离的同步。

独立的数据库

当然,在数据库容量无法满足需求时,最根本的方法(至少对于 SaaS 应用程序是这样)是建立单独的数据库。任何希望获得多租户 SaaS 应用程序的人都必须考虑到这种方式会导致不太可靠的解决方案,即需要针对每个租户提供数据库支持,这将直接导致效率低下,而这正是多租户力求避免的问题。

如果必须分割应用服务器以便只为一个数据库提供服务,那么多租户的高效率特性将进一步被折损。在竞争激烈的市场中,多租户的这些高效率特性对于 SaaS 公司获得成功至关重要。

在设计中利用负载均衡

在不得不使用独立数据库的情况下(不管出于什么原因),尽可能保持高效率的一种方法就是采用一种特殊的多租户设计,能够允许负载均衡的集群中的任何应用服务器访问多个数据库中最合适的那个数据库。通过这种方式,负载均衡的集群的效率就可以得到保持,所有应用服务器都能够连接到任意数据库以完成它们所支持的用户会话。这在多数据库的限制下实现了最大程度的效率。

容量并不是惟一的要求

除了满足容量需求外,使用多个数据库还出于正当理由,比如要求对某些高度安全的租户使用加密的数据库。容量不是问题,因此重要的是采用一种可以最大化效率的设计,即使需要一种本身比较低效的模型。

需要考虑的安全性问题

大量调查显示,SaaS 应用程序的用户通常将安全性排在首位,或至少非常接近首位。任何 SaaS 提供商都不能忽视安全性。但是,数据安全性的概念常常被局限于 SaaS 应用程序本身的上下文内。

大多数 SaaS 应用程序架构采取了一些保护数据安全性的措施,将阻止一个租户查看另一个租户的数据作为一个基本要求。但是:

  • SaaS 应用程序必须具备的一种能力就是与其他应用程序集成并交互。
  • 这些所谓的其他应用程序中的其中一些可能位于应用程序的外部(不受 SaaS 提供商的控制)。
  • 并不是所有 SaaS 应用程序都被设计为对外部应用程序具有访问性。

这些其他应用程序可以是需要访问或共享数据的内部应用程序;也可以是对数据进行挖掘以获得趋势的分析或报告编写工具。即使是数据库管理员使用的实用工具也会引起安全问题,如果租户可以使用它们访问,或者更糟,操作不属于它们的数据的话。

面向 SaaS 的最佳实践架构必须考虑到并不是所有数据访问都在应用程序的控制之下;必须有一些机制来确保为每个租户提供数据保护,不管是通过 SaaS 应用程序还是通过一些外部应用程序实现访问。

选择技术堆栈

在选择技术堆栈时经常需要进行一些权衡;对于 SaaS 应用程序尤其如此,因为要对所有租户作出决策。让我们看看在这个过程中需要考虑的一些问题。

操作系统考虑

web 应用程序中的操作系统也许是与用户最不相干的,因为用户是通过浏览器进行交互的。然而,一些经济和技术方面的因素可能会有一些影响:

  • 如果需要依赖特定的代码,而这些代码又依赖于操作系统,那么在做选择时需要考虑这种约束。
  • 如果存在集成外部应用程序的普遍需求,而在某种操作系统上进行这种集成要比在另外一种操作系统上更好,那么在选择时也要考虑到。

考虑到云的经济效益,人们总是倾向于那些性能良好、许可费更低的操作系统。扩展 web 应用程序的主要方法就是横向扩展;这意味着随着 SaaS 应用程序的发展,web 应用服务器实例的总数也将随之增加,这将产生直接的操作成本。

数据库考虑

web 应用程序中的数据库对于最终用户来说可能也不是一个关键的考量因素,因为他们的交互是通过浏览器完成的,并且只要应用程序能够存储和检索其数据,那么数据库与他们就没有多大的关系。

经济和技术方面的因素再一次对应用程序开发人员产生了影响。如果应用程序需要依赖数据库的一些特殊特性,那么在做选择时就会受到限制。

数据库的选择对于应用程序的许多设计都非常重要,对 SaaS 环境的特殊需求也会影响数据库的选择。

在 SaaS 应用程序中,由于最终的用户数的原因,对数据库的要求会更高一些;因此数据库的可扩展性非常重要。数据库可扩展性通常在一个单独的实例上实现,同时使用越来越强大的数据库服务器满足要求更加苛刻的应用程序。但是 SaaS 应用程序要求的可扩展性可能会超出纵向可扩展性的极限,因此,数据库可扩展性的下一步是采用集群化功能。

在云环境中实现这种集群化的能力可能会影响对数据库的选择。例如,可能会选择 DB2,因为它能够运行在广泛的操作系统上,提供纵向可扩展性,以及它可以灵活地选择通过多实例集群和冗余实现可扩展性。例如,可以在云中建立一个 DB2 HADR(高可用性灾难恢复)集群。

应用服务器考虑

和选择其他技术堆栈一样,选择应用服务器也是一项与 SaaS 应用程序开发人员有关的决策,因为最终用户交互只通过浏览器完成。但是,出于许多应用程序设计方面的考虑,应用服务器的选择存在很重要的差别,对 SaaS 环境的特殊要求也会影响它的选择。

使用了特殊特性或 WebSphere® 之类的应用服务器的插件(add-on)组件的应用程序将需要在云中使用这种应用服务器。

选择应用服务器的基本原则通常是在应用程序生命周期的早期作出的,因为应用程序可能会因为某些特性而受益,或者某些第三方插件或集成功能对于应用程序来说非常重要。有些时候,可能仅仅是因为组织的专有技术和内部标准要求在技术堆栈中使用特定的组件。

在选择和配置用于 SaaS/云环境的技术堆栈时,决策点包括平衡技术和经济方面的力量,从而实现一个较好的结果。

灵活性是技术演化的关键

在 SaaS 架构中,拥有对将要使用的技术堆栈作出决策和权衡的能力,以及在稍后的阶段重新审视这些决策的能力是非常宝贵的。随着技术的不断变化,领先的云提供商将并入新的中间件应用程序和功能,能够为您的 SaaS 应用程序采用这些应用程序和功能的能力也是一种优势。

强制您作出选择并使您锁定到某种特定的技术堆栈,这种 SaaS 应用程序架构将不利于 SaaS 应用程序的未来演变和适应能力。面向服务架构的核心概念十分适合于提供 SaaS 应用程序所需的敏捷性和灵活性。降低耦合程度将提供灵活性,而这种灵活性使实现战略战术演变成为可能。

在本文的其余部分中,我们将使用 Corent 的多租户服务器(Multi-Tenant Server™)提供一个示例,演示如何通过最少的步骤将一个 Java™ 开源记账 web 应用程序转换为一个多租户 SaaS 应用程序。

使用 Corent 的多租户服务器自动转换为 SaaS 应用程序

云提供商提供了一系列广泛的功能,可以容纳几乎任何 web 应用程序。要将这种应用程序完全转换为一个多租户 SaaS 应用程序,通常需要对应用程序代码作出大量修改,并重新设计和配置数据库。

Corent 的多租户服务器使 ISV 能够采用一种不同的方法,使用中间件层提供关键的多租户特性(从而保护对现有代码的投资)。我将描述如何通过 4 个步骤将一个典型的单租户应用程序转换为一个多租户 SaaS 应用程序。我们将要转换的应用程序就是 jBilling,这是一个流行的开源 Java web 应用程序。

步骤 1. 将数据库模式转换为一个抽象模型

web 应用程序的典型堆栈如图 1 所示;应用程序与数据库通信,通常通过 Hibernate 这样的数据持久化层完成。

图 1. 云中的典型 web 应用程序堆栈

云中的典型 web 应用程序堆栈

要将应用程序转换为多租户应用程序,必须重新设计数据库以添加更多的字段来管理租户身份识别数据,在根据租户进行数据过滤时需要用到这些数据。

SaaS-Factory™ 应用程序用于读取现有应用程序数据库的模式。随后,它将为这个数据库创建一个模型,该模型稍后用来在 MySQL 中生成一个新的数据库,该数据库在表中新增了 TenantID 字段。此时,用于多租户服务器的几个额外的表(包括租户信息表)就创建好了。

图 2. 创建 MetaModel 数据库模式

创建 MetaModel 数据库模式

由于 MetaModel 数据库看上去与原始应用程序完全相同,具有相同的表和字段,因此原始应用程序可以继续与 MetaModel 数据库交互,就好象和此前的 MySQL 数据库交互一样。隐藏在模型下的真正的数据库可以用多租户应用程序所需的 TenantID 字段生成。

MetaModel 数据库仅仅是一种抽象,并没有实际保存任何数据:它只是一个模型。因此,在生成真正的数据库时,没有理由不可以在任何受支持的关系数据库管理系统(RDBMS)中生成。在 ISV 希望通过选择不同的 RDBMS(比如 DB2)而修改技术堆栈,以利用某些特性或为应用程序实现更好的性能时,这种抽象性就可以派上用场。

步骤 2. 扩展用户身份验证流程

任何多租户 SaaS 应用程序必须能够为通过身份验证的用户管理必要的会话信息,从而建立用户所属的租户。应用程序可以采用多种方法进行身份验证;因此,对于进行转换的任何应用程序,必须进行分析并理解其身份验证方法。

步骤 1 向您展示了如何建立一个多租户表以及添加一个用户表,从而在应用程序数据中维护这种关系。我们的目标是实现一种方法来扩展应用程序的用户身份验证流程,从而使用户在登录后即可与对应的租户匹配,而 TenantID 则成为会话信息的一部分。

可以通过许多方式实现这一点;这取决于应用程序的实现。如果使用 Tomcat 作为应用程序 servlet 容器,那么可以利用容器托管式身份验证来发起一条业务规则,该业务规则运行在多租户服务器之上,用于查找与用户关联的 TenantID。或者,使用一个 servlet 过滤器来检测并设置线程本地变量 TenantID。对于本例中的 jBilling,应用程序直接对其用户表进行验证来完成身份验证。因此,要提供增强的身份验证,需要对身份验证代码进行一些简单的修改,添加一些流程来查找存储在新表中的用户的租户信息。

用户经过身份验证,并且知道了他们所属的 TenantID,现在已经具有足够多的信息来过滤数据,从而使用户只能够访问其所属的租户的数据。

步骤 3. 配置数据库连接

Corent 多租户服务器构建在面向服务的架构之上,使用了一个 MetaModel 数据库(如 步骤 1所述)。该 MetaModel 被用作一个抽象层,建模应用程序的原始数据库,而应用程序的数据库通信被重定向到 MetaModel 抽象而不是实际的数据库实现。方法就是重新配置 jBilling 的 JDBC 连接以访问 MetaModel 数据库而不是实际的 MySQL 数据库。对于使用 Hibernate 的应用程序,配置 Hibernate 的 Corent 方言(dialect)。

在作出这些修改后,应用程序数据库调用现在被指向到 MetaModel 数据库。这些 SQL 事件被 Corent Agile Controller™ 截获并被传递给 Corent 的 ADBC™(敏捷数据库连接器,Agile DataBase Connector)。ADBC 获得应用程序提交给 MetaModel 数据库的 SQL 语句并进行解析,然后为在会话中提交了 SQL 的用户的 TenantID 添加过滤条件(例如,TenantID = <myTenantID>)。这将确保始终严格地维护共享数据库中的数据安全性。即使 ReportWriter 这样的外部应用程序连接到数据库,它所看到的数据仍然被严格限制为与租户对应的数据。因为我们已经知道登录的用户的身份,因此不管用户来自什么应用程序,我们都知道它属于哪个租户,因而将应用相应的过滤。

由于对 SQL 语句的处理是在将它们提交给 MetaModel 数据库后完成的,并且用户所属的租户已知,Corent Agile Controller 将截获并执行更加复杂的操作,包括查找特定于租户的流程和配置。因此,可以针对每个租户执行一些具体的操作,甚至是替换不同的数据库连接,从而使某个租户可以使用 DB2 数据库,即使所有其他租户使用一个 MySQL 数据库。或者,某个租户可以进行额外的 SQL 操作,将它们可以检索的数据限制为最近 90 天以内输入的记录。所有这些基于每个租户作出的自定义都可以通过多租户服务器内置的 Agile Rules Engine™ 实现,不需要对原始的应用程序代码做任何修改。

图 3. 基于每个租户的自定义结构

基于每个租户的自定义结构

完成 jBilling 应用程序的全部转换只需要对应用程序做一些细微的修改,增强身份验证的最主要问题是在会话信息中包括租户信息,以及修改数据库连接以指向多租户服务器。您可以看到对 jBilling 的修改总结为以下几条:

  • 原始应用程序
    • 源文件数:897(Java 和 jsp)
    • 总的代码行数:76,621
  • 转换后的应用程序
    • 增加的源文件的数量:2(标准模板)
    • 修改的代码的行数:少于 100
    • 应用程序业务逻辑修改:0

包括所有分析和准备工作(判断哪些位置需要修改代码)在内,对 jBilling 的转换只用了不到一周的时间。许多修改就是在用于 JDBC 访问的 Java 代码片段中重复修改某个行,如果使用的是 Hibernate 之类的数据库持久层,那么这些修改都是可以省去的。

步骤 4. 将新的多租户 SaaS 应用程序部署到云中

现在,可以使用 SaaS-Factory 将 SaaS 应用程序部署到一个指定的服务器中,包括云中的服务器。

为应用程序和数据库选择了一个服务器后,将生成目标数据库(例如,对于我们的 jBilling 应用程序,就是在 Windows® 服务器上使用 MySQL 数据库),并将进行充分配置的多租户服务器应用程序以一组 .WAR 文件的形式部署到所选的应用服务器。多租户服务器可以使用任何现代的 J2EE Servlet 容器,而且还针对 WebSphere 应用服务器进行了认证。

部署后的应用程序现在已经能够处理多个租户。然而,原始应用程序现在缺少一个治理或管理界面来管理租户或监视多租户应用程序。

SaaS-Factory 还可以生成并安装一个称为 SaaS-Cockpit™ 的配套应用程序,该应用程序提供了这些基础的多租户服务。它可以像主应用程序那样访问 MetaModel 数据库并具有一个管理屏幕,用于指配租户、分配 Tenant Administrator 帐户和为各种可用的基于每租户的应用程序配置设置基本参数。还提供了一些管理工具,用于监视租户及其用户并进行报告。由于 SaaS 应用程序的典型特征之一就是需要跟踪租户订阅和记账,因此将出现一些用于记账的工具或与外部记账系统的集成。

图 4. 将新的 SaaS 应用程序部署到云中的结构

将新的 SaaS 应用程序部署到云中的结构

就 SaaS 应用程序部署而言,这种程度的部署是相当基础的。应用程序的特征在转换后仍然保持不变,标准的部署场景,包括使用云管理工具创建模板和传播应用程序实例,可以用于部署满足应用程序对可扩展性、灵活性、弹性和冗余性的要求的运行架构。

一个典型的 SaaS 应用程序可能会允许通过一个负载均衡器访问一组应用服务器并连接到数据库服务器。数据库服务器本身可以作为一个集群部署,同时使用多个数据库服务器提供冗余和可扩展性。

IBM DB2 提供了一些技术和配置,可用于实现弹性和有保障的恢复时间,以及允许对数据库上的负载进行分配:

  • 通过使用备用模式(secondary mode)作为只读数据库,DB2 HADR(高可用性灾难恢复)配置可以同时提供具有弹性的可用性和一些负载均衡。
  • IBM pureScale 技术和 Tivoli System Automation (TSA) 支持集群化的数据库环境,该环境中的多个节点共享处理负载,但是对硬件和操作系统配置的限制较多。

对于 SaaS 应用程序,数据库可用性和故障转移正变得越来越复杂,因为与传统的客户端软件(图 5)相比,任何一次宕机对客户/租户产生的影响的范围都会更大。

图 5. SaaS 部署架构,实现可扩展的弹性云运行

SaaS 部署架构,实现可扩展的弹性云运行

未来的发展趋势将是那些使应用程序能够持续运行的能力,即使出现数据离线重组、模式演变、版本升级和高负载变化。IBM 的 pureScale 技术正在从 z System 迁移到 AIX® 和 Linux®,并支持以前专门针对专用大型机环境的容量处理和正常运行时间。

结束语

信息技术产业正在向 SaaS 演变,正如您所臆测的一样,该领域已经开始出现一些显著的变化。云计算正在蓬勃发展,其发展速度远远超过了其他任何 IT 浪潮,而 SaaS 正是背后驱动这种增长的因素。这种转变要求公司重新考虑他们的业务组织,并通过一些新的思路思考以云为中心的 IT 世界中的服务交付。对于软件供应商,需要更加迫切地理解、准备和实现这种转变,否则就会被远远地抛在后面,它们的产品也会过时,成为数字垃圾。

云中的 SaaS 在技术和业务方面都与传统的软件供应商模型有着显著的不同。这些差异增加了 ISV 过渡到 SaaS 的风险。要缓解这种风险并缩短面市时间,ISV 可以利用一些可靠的技术、产品和合作伙伴来实现这种转变。

设置Golang的GOPATH

Go是一门全新的静态类型开发语言,具有自动垃圾回收丰富的内置类型,函数多返回值错误处理匿名函数,并发编程反射等特性.

golang安装

golang下载

sudo tar -zvxf go1.6.2.linux-amd64.tar.gz
sudo mv go /usr/local/go
#设置环境变量
vi /etc/profile
export GOROOT=/usr/local/go  #设置为go安装的路径
export GOPATH=$HOME/gocode   #默认安装包的路径
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
source /etc/profile

GOPATH设置

go命令依赖一个重要的环境变量:$GOPATH
GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个目录的时候Windows是分号;,Linux系统是冒号:
当有多个GOPATH时默认将go get获取的包存放在第一个目录下
$GOPATH目录约定有三个子目录

  • src存放源代码(比如:.go .c .h .s等)
  • pkg编译时生成的中间文件(比如:.a)
  • bin编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中,如果有多个gopath,那么使用${GOPATH//://bin:}/bin添加所有的bin目录)

代码目录结构规划

GOPATH下的src目录就是接下来开发程序的主要目录,所有的源码都是放在这个目录下面,那么一般我们的做法就是一个目录一个项目,例如: $GOPATH/src/mymath 表示mymath这个应用包或者可执行应用,这个根据package是main还是其他来决定,main的话就是可执行应用,其他的话就是应用包,这个会在后续详细介绍package。

下面我就以mymath为例来讲述如何编写应用包,执行如下代码
新建一个自己golang代码的路径myGolang

export GOPATH=$HOME/gocode:$HOME/myGolang
export PATH=$PATH:$GOROOT/bin:${GOPATH//://bin:}/bin
cd $GOPATH/src
mkdir mymath

新建文件fabnacci.go

package mymath

func Fabnacci(num int) int {
  if num == 0 || num == 1 {
    return num
  }
  return Fabnacci(num -1) + Fabnacci(num -2)
}

编译应用

上面我们已经建立了自己的应用包,如何进行编译安装呢?有两种方式可以进行安装

  • 进入对于的安装包目录,然后执行go install
  • 在任意的目录下指定需要编译的包go build mymath
    编译后可以在$GOPATH/pkg/${GOOS}_${GOARCH}下看到mymath.a文件
    .a文件是应用包,那么我们如何进行调用呢?
    接下来我们新建一个应用程序来调用这个应用包
cd $GOPATH/src
mkdir mathapp

新建Fabnacci.go文件

package main

import (
  "mymath"
  "fmt"
  "os"
  "strconv"
)
func main() {
  if len(os.Args) < 2 {
    fmt.Println("input number")
    return
  }
  num, err := strconv.Atoi(os.Args[1])
  if err != nil {
    fmt.Println("input must be number", err)
    return
  }
  fmt.Println("Fabnacci :", num, mymath.Fabnacci(num))
}

如何编译程序呢?进入该应用目录,然后执行go build,在该目录下会生成mathapp可执行文件

time ./mathapp 45
Fabnacci : 45 1134903170

real    0m10.836s
user    0m10.208s
sys 0m0.032s

如何安装应用程序?进入该目录执行go install,那么会在$GOPATH/bin/增加一个可执行文件mathapp
$GOPATH/bin/目录已经加入了环境变量,可以直接运行mathapp

golang 定时执行任务

上网查了下相关资料,基本上都介绍的是github.com\robfig\cron这个包来执行定时任务,试了下确实可以执行。但是此包下没有删除任务的方法,只有暂停的方法(Stop),若要停止之前的任务只执行新定义的任务,除非服务器重启再定义新任务。后来又参考了一下其他人的建议,采用了github.com\jakecoffman\cron这个包。后者是在前者的基础上做了一定的修改,给每个任务添加的一个新属性Name,然后新添加一个remove(name)方法,用来删除指定名字的任务,经测试使用后可正常删除任务,只不过之前的计划任务名得新建一个全局变量来存储。

以下先简单介绍下定义时间集合的信息:

字段名 是否必须 允许的值 允许的特定字符
秒(Seconds) 0-59
* / , –
分(Minutes) 0-59
* / , –
时(Hours) 0-23
* / , –
日(Day of month) 1-31
* / , – ?
月(Month) 1-12 or JAN-DEC
* / , –
星期(Day of week) 0-6 or SUM-SAT
* / , – ?

注:
1)月(Month)和星期(Day of week)字段的值不区分大小写,如:SUN、Sun 和 sun 是一样的。
2)星期
(Day of week)字段如果没提供,相当于是 *

2、特殊字符说明
1)星号(*)
表示 cron 表达式能匹配该字段的所有值。如在第5个字段使用星号(month),表示每个月

回到顶部
2)斜线(/)
表示增长间隔,如第1个字段(minutes) 值是 3-59/15,表示每小时的第3分钟开始执行一次,之后每隔 15 分钟执行一次(即 3、18、33、48 这些时间点执行),这里也可以表示为:3/15

回到顶部
3)逗号(,)
用于枚举值,如第6个字段值是 MON,WED,FRI,表示 星期一、三、五 执行

回到顶部
4)连字号(-)
表示一个范围,如第3个字段的值为 9-17 表示 9am 到 5pm 直接每个小时(包括9和17)

回到顶部
5)问号(?)
只用于 日(Day of month) 和 星期(Day of week),表示不指定值,可以用于代替 *

golang 指针

不像 Java 和 .NET,Go 语言为程序员提供了控制数据结构的指针的能力;但是,你不能进行指针运算。通过给予程序员基本内存布局,Go 语言允许你控制特定集合的数据结构、分配的数量以及内存访问模式,这些对构建运行良好的系统是非常重要的:指针对于性能的影响是不言而喻的,而如果你想要做的是系统编程、操作系统或者网络应用,指针更是不可或缺的一部分。

由于各种原因,指针对于使用面向对象编程的现代程序员来说可能显得有些陌生,不过我们将会在这一小节对此进行解释,并在未来的章节中展开深入讨论。

程序在内存中存储它的值,每个内存块(或字)有一个地址,通常用十六进制数表示,如:0x6b0820 或 0xf84001d7f0

Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。

下面的代码片段(示例 4.9 pointer.go)可能输出 An integer: 5, its location in memory: 0x6b0820(这个值随着你每次运行程序而变化)。

var i1 = 5
fmt.Printf("An integer: %d, it's location in memory: %p\n", i1, &i1)

这个地址可以存储在一个叫做指针的特殊数据类型中,在本例中这是一个指向 int 的指针,即 i1:此处使用 *int 表示。如果我们想调用指针 intP,我们可以这样声明它:

var intP *int

然后使用 intP = &i1 是合法的,此时 intP 指向 i1。

(指针的格式化标识符为 %p

intP 存储了 i1 的内存地址;它指向了 i1 的位置,它引用了变量 i1。

一个指针变量可以指向任何一个值的内存地址 它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,并且与它所指向的值的大小无关。当然,可以声明指针指向任何类型的值来表明它的原始性或结构性;你可以在指针类型前面加上 号(前缀)来获取指针所指向的内容,这里的 号是一个类型更改器。使用一个指针引用一个值被称为间接引用。

当一个指针被定义后没有分配到任何变量时,它的值为 nil

一个指针变量通常缩写为 ptr

注意事项

在书写表达式类似 var p *type 时,切记在 号和指针名称间留有一个空格,因为 `- var ptype` 是语法正确的,但是在更复杂的表达式中,它容易被误认为是一个乘法表达式!

符号 可以放在一个指针前,如 `intP`,那么它将得到这个指针指向地址上所存储的值;这被称为反引用(或者内容或者间接引用)操作符;另一种说法是指针转移。

对于任何一个变量 var, 如下表达式都是正确的:var == *(&var)

现在,我们应当能理解 pointer.go 中的整个程序和他的输出:

示例 4.21 pointer.go:

package main
import "fmt"
func main() {
    var i1 = 5
    fmt.Printf("An integer: %d, its location in memory: %p\n", i1, &i1)
    var intP *int
    intP = &i1
    fmt.Printf("The value at memory location %p is %d\n", intP, *intP)
}

输出:

An integer: 5, its location in memory: 0x24f0820
The value at memory location 0x24f0820 is 5

我们可以用下图来表示内存使用的情况:

程序 string_pointer.go 为我们展示了指针对string的例子。

它展示了分配一个新的值给 *p 并且更改这个变量自己的值(这里是一个字符串)。

示例 4.22 string_pointer.go

package main
import "fmt"
func main() {
    s := "good bye"
    var p *string = &s
    *p = "ciao"
    fmt.Printf("Here is the pointer p: %p\n", p) // prints address
    fmt.Printf("Here is the string *p: %s\n", *p) // prints string
    fmt.Printf("Here is the string s: %s\n", s) // prints same string
}

输出:

Here is the pointer p: 0x2540820
Here is the string *p: ciao
Here is the string s: ciao

通过对 *p 赋另一个值来更改“对象”,这样 s 也会随之更改。

内存示意图如下:

注意事项

你不能得到一个文字或常量的地址,例如:

const i = 5
ptr := &i //error: cannot take the address of i
ptr2 := &10 //error: cannot take the address of 10

所以说,Go 语言和 C、C++ 以及 D 语言这些低级(系统)语言一样,都有指针的概念。但是对于经常导致 C 语言内存泄漏继而程序崩溃的指针运算(所谓的指针算法,如:pointer+2,移动指针指向字符串的字节数或数组的某个位置)是不被允许的。Go 语言中的指针保证了内存安全,更像是 Java、C# 和 VB.NET 中的引用。

因此 c = *p++ 在 Go 语言的代码中是不合法的。

指针的一个高级应用是你可以传递一个变量的引用(如函数的参数),这样不会传递变量的拷贝。指针传递是很廉价的,只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中,直到没有任何指针指向它们,所以从它们被创建开始就具有相互独立的生命周期。

另一方面(虽然不太可能),由于一个指针导致的间接引用(一个进程执行了另一个地址),指针的过度频繁使用也会导致性能下降。

指针也可以指向另一个指针,并且可以进行任意深度的嵌套,导致你可以有多级的间接引用,但在大多数情况这会使你的代码结构不清晰。

如我们所见,在大多数情况下 Go 语言可以使程序员轻松创建指针,并且隐藏间接引用,如:自动反向引用。

对一个空指针的反向引用是不合法的,并且会使程序崩溃:

示例 4.23 testcrash.go:

package main
func main() {
    var p *int = nil
    *p = 0
}
// in Windows: stops only with: <exit code="-1073741819" msg="process crashed"/>
// runtime error: invalid memory address or nil pointer dereference

问题 4.2 列举 Go 语言中 * 号的所有用法。

Mac OS X下Go语言环境搭建过程及遇见的一些问题

一直对Google的Go很好奇,最近看到Go的开发团队专门做了Go tour来帮助开发者学习,就决定尝试下。

本来感觉一个很简单的环境搭建却出现了几个意想不到的问题,所以有必要记录一下。

一.安装

Go的官网上有安装指南,看了下决定用brew下载安装,简单方便。

$ brew install go

安装后输入go可以查看一些命令的使用。

看下版本

$ go version

go version go1.1.2 darwin/amd64

安装包会自动安装在 /usr/local/Cellar/go目录下。

二.配置

安装好后,在/usr/local/Cellar/go/1.1.2/libexec/misc中,可以看到有很多编辑器版本, 在其下可以自动配置不同编辑器来使用Go开发。

配置$GOPATH和$GOROOT

输入以go env命令来查看环境变量

$ go env

GOARCH=”amd64″

GOBIN=””

GOCHAR=”6″

GOEXE=””

GOHOSTARCH=”amd64″

GOHOSTOS=”darwin”

GOOS=”darwin”

GOPATH=”/Users/Jackie/gocode”

GORACE=””

GOROOT=”/usr/local/Cellar/go/1.1.2/libexec”

GOTOOLDIR=”/usr/local/Cellar/go/1.1.2/libexec/pkg/tool/darwin_amd64″

CC=”gcc”

GOGCCFLAGS=”-g -O2 -fPIC -m64 -pthread -fno-common”

CGO_ENABLED=”1″

$GOROOT是go的安装路径,配置后可直接在终端执行相关命令。注:Go 1.0之后安装包自带的Go tool会自动设置。

$GOPATH用来设置workspaces,即工作路径,可以设置多个。在Go 1.1之后被强制要求设置。具体见官方文档GoLang.org

关于这个$GOPATH,我安装时报错不断,真是让人头痛。

开始一直提示”$GOPATH not set.“,尝试了Stack Overflow上所有的方法,都没用,最后索性不弄了。没想到第二次打开又可以了,难道必须要重启?

后来就是在安装gotour时出现的一个问题,下面再说。

三.安装Gotour

首先需要hg(Mercurial),这是一个跨平台的分布式版本控制软件。安装后可以使用hg等命令。

当然,我还是用brew安装

$ brew install hg

安装成功后后输入hg命令会出现Mercurial的相关使用信息。

接下来需要建立$GOPATH所需要的目录,下面是官方给出的一个例子

GOPATH=/home/user/gocode
 
/home/user/gocode/
    src/
        foo/
            bar/               (go code in package bar)
                x.go
            quux/              (go code in package main)
                y.go
    bin/
        quux                   (installed command)
    pkg/
        linux_amd64/
            foo/
                bar.a          (installed package object) 按照官方文档,这时在终端下使用

$ sudo go get code.google.com/p/go-tour/gotour

就可以安装Go tour了

如果安装中文的话地址改为

$ sudo go get bitbucket.org/mikespook/go-tour-zh/gotour

:中文地址我始终不能下,貌似被qiang了。

不过我安装的时候又出现了问题,

abort: error: EOF occurred in violation of protocol

package code.google.com/p/go-tour/gotour: exit status 255

反复尝试无果后,决定用hg直接手动下载

$ hg clone https://code.google.com/p/go-tour/

再复制

cp -r go-tour $GOPATH/src/pkg/code.google.com/p/go-tour/gotour

再安装

cd $GOPATH/src/pkg/code.google.com/p/go-tour/gotour

go install ./

这时gotour就安装到$GOPATH/bin目录下了

gotour

进入这个目录打开它就行了

$ ls $GOPATH/bin

$ gotour

2013/11/26 15:48:56 Serving content from /Users/Jackie/gocode/src/code.google.com/p/go-tour

2013/11/26 15:48:56 A browser window should open. If not, please visit http://127.0.0.1:3999

这时会自动打开浏览器,如图

gotour

最后终于弄好了,不过不能每次都进这个目录再输入命令吧。

再配置一下bashrc就可以了。

$ sudo vi /etc/bashrc

然后在下面加上

#Go tour

alias gotour=’/Users/Jackie/gocode/bin/gotour’

这样就完美了。