快乐成长


每天进步一点点



把学习变为一种快乐,快乐长能长久,才能坚持,才能降低学习成本,带给您学习乐趣。 每天进步点点, 一直是我们的愿望,欢迎您光临!!

为了给您增加学习乐趣,并且学到有意思,简单高效的,有用的学习,为零基础的朋友们录制了趣味编程视频,欢迎您浏览,谢谢!

月度归档:2014年07月

注释驱动的 Spring cache 缓存介绍

概述

Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。

Spring 的缓存技术还具备相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCache 集成。

其特点总结如下:

  • 通过少量的配置 annotation 注释即可使得既有代码支持缓存
  • 支持开箱即用 Out-Of-The-Box,即不用安装和部署额外第三方组件即可使用缓存
  • 支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 key 和 condition
  • 支持 AspectJ,并通过其实现任何方法的缓存支持
  • 支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性

本文将针对上述特点对 Spring cache 进行详细的介绍,主要通过一个简单的例子和原理介绍展开,然后我们将一起看一个比较实际的缓存例子,最后会介绍 spring cache 的使用限制和注意事项。OK,Let ’ s begin!

原来我们是怎么做的

这里先展示一个完全自定义的缓存实现,即不用任何第三方的组件来实现某种对象的内存缓存。

场景是:对一个账号查询方法做缓存,以账号名称为 key,账号对象为 value,当以相同的账号名称查询账号的时候,直接从缓存中返回结果,否则更新缓存。账号查询服务还支持 reload 缓存(即清空缓存)。

首先定义一个实体类:账号类,具备基本的 id 和 name 属性,且具备 getter 和 setter 方法

清单 1. Account.java

package cacheOfAnno;

public class Account {
private int id;
private String name;

public Account(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

然后定义一个缓存管理器,这个管理器负责实现缓存逻辑,支持对象的增加、修改和删除,支持值对象的泛型。如下:

清单 2. MyCacheManager.java

package oldcache;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MyCacheManager<T> {
private Map<String,T> cache =
new ConcurrentHashMap<String,T>();

public T getValue(Object key) {
return cache.get(key);
}

public void addOrUpdateCache(String key,T value) {
cache.put(key, value);
}

public void evictCache(String key) {// 根据 key 来删除缓存中的一条记录
if(cache.containsKey(key)) {
cache.remove(key);
}
}

public void evictCache() {// 清空缓存中的所有记录
cache.clear();
}
}

好,现在我们有了实体类和一个缓存管理器,还需要一个提供账号查询的服务类,此服务类使用缓存管理器来支持账号查询缓存,如下:
清单 3. MyAccountService.java

package oldcache;

import cacheOfAnno.Account;

public class MyAccountService {
private MyCacheManager<Account> cacheManager;

public MyAccountService() {
cacheManager = new MyCacheManager<Account>();// 构造一个缓存管理器
}

public Account getAccountByName(String acctName) {
Account result = cacheManager.getValue(acctName);// 首先查询缓存
if(result!=null) {
System.out.println("get from cache..."+acctName);
return result;// 如果在缓存中,则直接返回缓存的结果
}
result = getFromDB(acctName);// 否则到数据库中查询
if(result!=null) {// 将数据库查询的结果更新到缓存中
cacheManager.addOrUpdateCache(acctName, result);
}
return result;
}

public void reload() {
cacheManager.evictCache();
}

private Account getFromDB(String acctName) {
System.out.println("real querying db..."+acctName);
return new Account(acctName);
}
}

现在我们开始写一个测试类,用于测试刚才的缓存是否有效

清单 4. Main.java

package oldcache;

public class Main {

public static void main(String[] args) {
MyAccountService s = new MyAccountService();
// 开始查询账号
s.getAccountByName("somebody");// 第一次查询,应该是数据库查询
s.getAccountByName("somebody");// 第二次查询,应该直接从缓存返回

s.reload();// 重置缓存
System.out.println("after reload...");

s.getAccountByName("somebody");// 应该是数据库查询
s.getAccountByName("somebody");// 第二次查询,应该直接从缓存返回

}

}

按照分析,执行结果应该是:首先从数据库查询,然后直接返回缓存中的结果,重置缓存后,应该先从数据库查询,然后返回缓存中的结果,实际的执行结果如下:
清单 5. 运行结果

real querying db...somebody// 第一次从数据库加载
get from cache...somebody// 第二次从缓存加载
after reload...// 清空缓存
real querying db...somebody// 又从数据库加载
get from cache...somebody// 从缓存加载

可以看出我们的缓存起效了,但是这种自定义的缓存方案有如下劣势:

缓存代码和业务代码耦合度太高,如上面的例子,AccountService 中的 getAccountByName()方法中有了太多缓存的逻辑,不便于维护和变更
不灵活,这种缓存方案不支持按照某种条件的缓存,比如只有某种类型的账号才需要缓存,这种需求会导致代码的变更
缓存的存储这块写的比较死,不能灵活的切换为使用第三方的缓存模块

如果你的代码中有上述代码的影子,那么你可以考虑按照下面的介绍来优化一下你的代码结构了,也可以说是简化,你会发现,你的代码会变得优雅的多!
 

Hello World,注释驱动的 Spring Cache

Hello World 的实现目标

本 Hello World 类似于其他任何的 Hello World 程序,从最简单实用的角度展现 spring cache 的魅力,它基于刚才自定义缓存方案的实体类 Account.java,重新定义了 AccountService.java 和测试类 Main.java(注意这个例子不用自己定义缓存管理器,因为 spring 已经提供了缺省实现)

需要的 jar 包

为了实用 spring cache 缓存方案,在工程的 classpath 必须具备下列 jar 包。

图 1. 工程依赖的 jar 包图

图 1. 工程依赖的 jar 包图注意这里我引入的是最新的 spring 3.2.0.M1 版本 jar 包,其实只要是 spring 3.1 以上,都支持 spring cache。其中 spring-context-*.jar 包含了 cache 需要的类。

定义实体类、服务类和相关配置文件

实体类就是上面自定义缓存方案定义的 Account.java,这里重新定义了服务类,如下:

清单 6. AccountService.java

package cacheOfAnno;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;

public class AccountService {
@Cacheable(value="accountCache")// 使用了一个缓存名叫 accountCache
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
System.out.println("real query account."+userName);
return getFromDB(userName);
}

private Account getFromDB(String acctName) {
System.out.println("real querying db..."+acctName);
return new Account(acctName);
}
}

注意,此类的 getAccountByName 方法上有一个注释 annotation,即 @Cacheable(value=”accountCache”),这个注释的意思是,当调用这个方法的时候,会从一个名叫 accountCache 的缓存中查询,如果没有,则执行实际的方法(即查询数据库),并将执行的结果存入缓存中,否则返回缓存中的对象。这里的缓存中的 key 就是参数 userName,value 就是 Account 对象。“accountCache”缓存是在 spring*.xml 中定义的名称。

好,因为加入了 spring,所以我们还需要一个 spring 的配置文件来支持基于注释的缓存
清单 7. Spring-cache-anno.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/cache

http://www.springframework.org/schema/cache/spring-cache.xsd">

<cache:annotation-driven />

<bean id="accountServiceBean" class="cacheOfAnno.AccountService"/>

<!-- generic cache manager -->
<bean id="cacheManager"
class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="default" />

<bean
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="accountCache" />
</set>
</property>
</bean>
</beans>

注意这个 spring 配置文件有一个关键的支持缓存的配置项:<cache:annotation-driven />,这 个配置项缺省使用了一个名字叫 cacheManager 的缓存管理器,这个缓存管理器有一个 spring 的缺省实现,即 org.springframework.cache.support.SimpleCacheManager,这个缓存管理器实现了我们刚刚自定义的缓 存管理器的逻辑,它需要配置一个属性 caches,即此缓存管理器管理的缓存集合,除了缺省的名字叫 default 的缓存,我们还自定义了一个名字叫 accountCache 的缓存,使用了缺省的内存存储方案 ConcurrentMapCacheFactoryBean,它是基于 java.util.concurrent.ConcurrentHashMap 的一个内存缓存实现方案。

OK,现在我们具备了测试条件,测试代码如下:
清单 8. Main.java

package cacheOfAnno;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-cache-anno.xml");// 加载 spring 配置文件

AccountService s = (AccountService) context.getBean("accountServiceBean");
// 第一次查询,应该走数据库
System.out.print("first query...");
s.getAccountByName("somebody");
// 第二次查询,应该不查数据库,直接返回缓存的值
System.out.print("second query...");
s.getAccountByName("somebody");
System.out.println();
}
}

上面的测试代码主要进行了两次查询,第一次应该会查询数据库,第二次应该返回缓存,不再查数据库,我们执行一下,看看结果
清单 9. 执行结果

first query...real query account.somebody// 第一次查询
real querying db...somebody// 对数据库进行了查询
second query...// 第二次查询,没有打印数据库查询日志,直接返回了缓存中的结果

可以看出我们设置的基于注释的缓存起作用了,而在 AccountService.java 的代码中,我们没有看到任何的缓存逻辑代码,只有一行注释:@Cacheable(value="accountCache"),就实现了基本的缓存方案,是不是很强大?
如何清空缓存

好,到目前为止,我们的 spring cache 缓存程序已经运行成功了,但是还不完美,因为还缺少一个重要的缓存管理逻辑:清空缓存,当账号数据发生变更,那么必须要清空某个缓存,另外还需要定期的清空所有缓存,以保证缓存数据的可靠性。

为了加入清空缓存的逻辑,我们只要对 AccountService.java 进行修改,从业务逻辑的角度上看,它有两个需要清空缓存的地方

  • 当外部调用更新了账号,则我们需要更新此账号对应的缓存
  • 当外部调用说明重新加载,则我们需要清空所有缓存
清单 10. AccountService.java

点击查看代码清单

清单 11. Main.java

点击查看代码清单

清单 12. 运行结果

first query...real querying db...somebody
second query...
start testing clear cache...
real querying db...somebody1
real querying db...somebody2
real update db...somebody1
real querying db...somebody1
real querying db...somebody1
real querying db...somebody2

结果和我们期望的一致,所以,我们可以看出,spring cache 清空缓存的方法很简单,就是通过 @CacheEvict 注释来标记要清空缓存的方法,当这个方法被调用后,即会清空缓存。注意其中一个 @CacheEvict(value=”accountCache”,key=”#account.getName()”),其中的 Key 是用来指定缓存的 key 的,这里因为我们保存的时候用的是 account 对象的 name 字段,所以这里还需要从参数 account 对象中获取 name 的值来作为 key,前面的 # 号代表这是一个 SpEL 表达式,此表达式可以遍历方法的参数对象,具体语法可以参考 Spring 的相关文档手册。
如何按照条件操作缓存

前面介绍的缓存方法,没有任何条件, 即所有对 accountService 对象的 getAccountByName 方法的调用都会起动缓存效果,不管参数是什么值,如果有一个需求,就是只有账号名称的长度小于等于 4 的情况下,才做缓存,大于 4 的不使用缓存,那怎么实现呢?

Spring cache 提供了一个很好的方法,那就是基于 SpEL 表达式的 condition 定义,这个 condition 是 @Cacheable 注释的一个属性,下面我来演示一下
清单 13. AccountService.java(getAccountByName 方法修订,支持条件)

@Cacheable(value="accountCache",condition="#userName.length() <= 4")// 缓存名叫 accountCache
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
return getFromDB(userName);
}

注意其中的 condition=”#userName.length() <=4”,这里使用了 SpEL 表达式访问了参数 userName 对象的 length() 方法,条件表达式返回一个布尔值,true/false,当条件为 true,则进行缓存操作,否则直接调用方法执行的返回结果。
清单 14. 测试方法

s.getAccountByName("somebody");// 长度大于 4,不会被缓存
s.getAccountByName("sbd");// 长度小于 4,会被缓存
s.getAccountByName("somebody");// 还是查询数据库
s.getAccountByName("sbd");// 会从缓存返回

清单 15. 运行结果

real querying db...somebody
real querying db...sbd
real querying db...somebody

可见对长度大于 4 的账号名 (somebody) 没有缓存,每次都查询数据库。
如果有多个参数,如何进行 key 的组合

假 设 AccountService 现在有一个需求,要求根据账号名、密码和是否发送日志查询账号信息,很明显,这里我们需要根据账号名、密码对账号对象进行缓存,而第三个参数“是否发送日 志”对缓存没有任何影响。所以,我们可以利用 SpEL 表达式对缓存 key 进行设计
清单 16. Account.java(增加 password 属性)

private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}

清单 17. AccountService.java(增加 getAccount 方法,支持组合 key)

@Cacheable(value="accountCache",key="#userName.concat(#password)")
public Account getAccount(String userName,String password,boolean sendLog) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
return getFromDB(userName,password);

}

注意上面的 key 属性,其中引用了方法的两个参数 userName 和 password,而 sendLog 属性没有考虑,因为其对缓存没有影响。
清单 18. Main.java

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-cache-anno.xml");// 加载 spring 配置文件

AccountService s = (AccountService) context.getBean("accountServiceBean");
s.getAccount("somebody", "123456", true);// 应该查询数据库
s.getAccount("somebody", "123456", true);// 应该走缓存
s.getAccount("somebody", "123456", false);// 应该走缓存
s.getAccount("somebody", "654321", true);// 应该查询数据库
s.getAccount("somebody", "654321", true);// 应该走缓存
}

上述测试,是采用了相同的账号,不同的密码组合进行查询,那么一共有两种组合情况,所以针对数据库的查询应该只有两次。
清单 19. 运行结果

real querying db...userName=somebody password=123456
real querying db...userName=somebody password=654321

和我们预期的一致。
如何做到:既要保证方法被调用,又希望结果被缓存

根据前面的例子,我们知道,如果使用了 @Cacheable 注释,则当重复使用相同参数调用方法的时候,方法本身不会被调用执行,即方法本身被略过了,取而代之的是方法的结果直接从缓存中找到并返回了。

现实中并不总是如此,有些情况下我们希望方法一定会被调用,因为其除了返回一个结果,还做了其他事情,例如记录日志,调用接口等,这个时候,我们可以用 @CachePut 注释,这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中。
清单 20. AccountService.java

@Cacheable(value="accountCache")// 使用了一个缓存名叫 accountCache
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
return getFromDB(userName);
}
@CachePut(value="accountCache",key="#account.getName()")// 更新 accountCache 缓存
public Account updateAccount(Account account) {
return updateDB(account);
}
private Account updateDB(Account account) {
System.out.println("real updating db..."+account.getName());
return account;
}

清单 21. Main.java

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-cache-anno.xml");// 加载 spring 配置文件

AccountService s = (AccountService) context.getBean("accountServiceBean");

Account account = s.getAccountByName("someone");
account.setPassword("123");
s.updateAccount(account);
account.setPassword("321");
s.updateAccount(account);
account = s.getAccountByName("someone");
System.out.println(account.getPassword());
}

如上面的代码所示,我们首先用 getAccountByName 方法查询一个人 someone 的账号,这个时候会查询数据库一次,但是也记录到缓存中了。然后我们修改了密码,调用了 updateAccount 方法,这个时候会执行数据库的更新操作且记录到缓存,我们再次修改密码并调用 updateAccount 方法,然后通过 getAccountByName 方法查询,这个时候,由于缓存中已经有数据,所以不会查询数据库,而是直接返回最新的数据,所以打印的密码应该是“321”
清单 22. 运行结果

real querying db...someone
real updating db...someone
real updating db...someone
321

和分析的一样,只查询了一次数据库,更新了两次数据库,最终的结果是最新的密码。说明 @CachePut 确实可以保证方法被执行,且结果一定会被缓存。

@Cacheable、@CachePut、@CacheEvict 注释介绍

通过上面的例子,我们可以看到 spring cache 主要使用两个注释标签,即 @Cacheable、@CachePut 和 @CacheEvict,我们总结一下其作用和配置方法。

表 1. @Cacheable 作用和配置方法

@Cacheable 的作用    主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@Cacheable 主要的参数
value    缓存的名称,在 spring 配置文件中定义,必须指定至少一个    例如:
@Cacheable(value=”mycache”) 或者
@Cacheable(value={”cache1”,”cache2”}
key    缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合    例如:
@Cacheable(value=”testcache”,key=”#userName”)
condition    缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存    例如:
@Cacheable(value=”testcache”,condition=”#userName.length()>2”)

表 2. @CachePut 作用和配置方法

@CachePut 的作用    主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
@CachePut 主要的参数
value    缓存的名称,在 spring 配置文件中定义,必须指定至少一个    例如:
@Cacheable(value=”mycache”) 或者
@Cacheable(value={”cache1”,”cache2”}
key    缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合    例如:
@Cacheable(value=”testcache”,key=”#userName”)
condition    缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存    例如:
@Cacheable(value=”testcache”,condition=”#userName.length()>2”)

表 3. @CacheEvict 作用和配置方法

@CachEvict 的作用    主要针对方法配置,能够根据一定的条件对缓存进行清空
@CacheEvict 主要的参数
value    缓存的名称,在 spring 配置文件中定义,必须指定至少一个    例如:
@CachEvict(value=”mycache”) 或者
@CachEvict(value={”cache1”,”cache2”}
key    缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合    例如:
@CachEvict(value=”testcache”,key=”#userName”)
condition    缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存    例如:
@CachEvict(value=”testcache”,
condition=”#userName.length()>2”)
allEntries    是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存    例如:
@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation    是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存    例如:
@CachEvict(value=”testcache”,beforeInvocation=true)

基本原理

和 spring 的事务管理类似,spring cache 的关键原理就是 spring AOP,通过 spring AOP,其实现了在方法调用前、调用后获取方法的入参和返回值,进而实现了缓存的逻辑。我们来看一下下面这个图:

图 2. 原始方法调用图

图 2. 原始方法调用图上图显示,当客户端“Calling code”调用一个普通类 Plain Object 的 foo() 方法的时候,是直接作用在 pojo 类自身对象上的,客户端拥有的是被调用者的直接的引用。

而 Spring cache 利用了 Spring AOP 的动态代理技术,即当客户端尝试调用 pojo 的 foo()方法的时候,给他的不是 pojo 自身的引用,而是一个动态生成的代理类

图 3. 动态代理调用图

图 3. 动态代理调用图如 上图所示,这个时候,实际客户端拥有的是一个代理的引用,那么在调用 foo() 方法的时候,会首先调用 proxy 的 foo() 方法,这个时候 proxy 可以整体控制实际的 pojo.foo() 方法的入参和返回值,比如缓存结果,比如直接略过执行实际的 foo() 方法等,都是可以轻松做到的。

扩展性

直 到现在,我们已经学会了如何使用开箱即用的 spring cache,这基本能够满足一般应用对缓存的需求,但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓 存不满意了,因为其不支持高可用性,也不具备持久化数据能力,这个时候,你就需要自定义你的缓存方案了,还好,spring 也想到了这一点。

我们先不考虑如何持久化缓存,毕竟这种第三方的实现方案很多,我们要考虑的是,怎么利用 spring 提供的扩展点实现我们自己的缓存,且在不改原来已有代码的情况下进行扩展。

首 先,我们需要提供一个 CacheManager 接口的实现,这个接口告诉 spring 有哪些 cache 实例,spring 会根据 cache 的名字查找 cache 的实例。另外还需要自己实现 Cache 接口,Cache 接口负责实际的缓存逻辑,例如增加键值对、存储、查询和清空等。利用 Cache 接口,我们可以对接任何第三方的缓存系统,例如 EHCache、OSCache,甚至一些内存数据库例如 memcache 或者 h2db 等。下面我举一个简单的例子说明如何做。

清单 23. MyCacheManager

package cacheOfAnno;

import java.util.Collection;

import org.springframework.cache.support.AbstractCacheManager;

public class MyCacheManager extends AbstractCacheManager {
private Collection<? extends MyCache> caches;

/**
* Specify the collection of Cache instances to use for this CacheManager.
*/
public void setCaches(Collection<? extends MyCache> caches) {
this.caches = caches;
}

@Override
protected Collection<? extends MyCache> loadCaches() {
return this.caches;
}

}

上面的自定义的 CacheManager 实际继承了 spring 内置的 AbstractCacheManager,实际上仅仅管理 MyCache 类的实例。
清单 24. MyCache

package cacheOfAnno;

import java.util.HashMap;
import java.util.Map;

import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;

public class MyCache implements Cache {
private String name;
private Map<String,Account> store = new HashMap<String,Account>();;

public MyCache() {
}

public MyCache(String name) {
this.name = name;
}

@Override
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public Object getNativeCache() {
return store;
}

@Override
public ValueWrapper get(Object key) {
ValueWrapper result = null;
Account thevalue = store.get(key);
if(thevalue!=null) {
thevalue.setPassword("from mycache:"+name);
result = new SimpleValueWrapper(thevalue);
}
return result;
}

@Override
public void put(Object key, Object value) {
Account thevalue = (Account)value;
store.put((String)key, thevalue);
}

@Override
public void evict(Object key) {
}

@Override
public void clear() {
}
}

上面的自定义缓存只实现了很简单的逻辑,但这是我们自己做的,也很令人激动是不是,主要看 get 和 put 方法,其中的 get 方法留了一个后门,即所有的从缓存查询返回的对象都将其 password 字段设置为一个特殊的值,这样我们等下就能演示“我们的缓存确实在起作用!”了。

这还不够,spring 还不知道我们写了这些东西,需要通过 spring*.xml 配置文件告诉它
清单 25. Spring-cache-anno.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/cache

http://www.springframework.org/schema/cache/spring-cache.xsd">

<cache:annotation-driven />

<bean id="accountServiceBean" class="cacheOfAnno.AccountService"/>

<!-- generic cache manager -->
<bean id="cacheManager" class="cacheOfAnno.MyCacheManager">
<property name="caches">
<set>
<bean
class="cacheOfAnno.MyCache"
p:name="accountCache" />
</set>
</property>
</bean>

</beans>

注意上面配置文件的黑体字,这些配置说明了我们的 cacheManager 和我们自己的 cache 实例。

好,什么都不说,测试!
清单 26. Main.java

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-cache-anno.xml");// 加载 spring 配置文件

AccountService s = (AccountService) context.getBean("accountServiceBean");

Account account = s.getAccountByName("someone");
System.out.println("passwd="+account.getPassword());
account = s.getAccountByName("someone");
System.out.println("passwd="+account.getPassword());
}

上面的测试代码主要是先调用 getAccountByName 进行一次查询,这会调用数据库查询,然后缓存到 mycache 中,然后我打印密码,应该是空的;下面我再次查询 someone 的账号,这个时候会从 mycache 中返回缓存的实例,记得上面的后门么?我们修改了密码,所以这个时候打印的密码应该是一个特殊的值
清单 27. 运行结果

real querying db...someone
passwd=null
passwd=from mycache:accountCache

结果符合预期,即第一次查询数据库,且密码为空,第二次打印了一个特殊的密码。说明我们的 myCache 起作用了。

注意和限制

基于 proxy 的 spring aop 带来的内部调用问题

上 面介绍过 spring cache 的原理,即它是基于动态生成的 proxy 代理机制来对方法的调用进行切面,这里关键点是对象的引用问题,如果对象的方法是内部调用(即 this 引用)而不是外部引用,则会导致 proxy 失效,那么我们的切面就失效,也就是说上面定义的各种注释包括 @Cacheable、@CachePut 和 @CacheEvict 都会失效,我们来演示一下。

清单 28. AccountService.java

public Account getAccountByName2(String userName) {
return this.getAccountByName(userName);
}

@Cacheable(value="accountCache")// 使用了一个缓存名叫 accountCache
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
return getFromDB(userName);
}

上面我们定义了一个新的方法 getAccountByName2,其自身调用了 getAccountByName 方法,这个时候,发生的是内部调用(this),所以没有走 proxy,导致 spring cache 失效
清单 29. Main.java

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-cache-anno.xml");// 加载 spring 配置文件

AccountService s = (AccountService) context.getBean("accountServiceBean");

s.getAccountByName2("someone");
s.getAccountByName2("someone");
s.getAccountByName2("someone");
}

清单 30. 运行结果

real querying db...someone
real querying db...someone
real querying db...someone

可见,结果是每次都查询数据库,缓存没起作用。要避免这个问题,就是要避免对缓存方法的内部调用,或者避免使用基于 proxy 的 AOP 模式,可以使用基于 aspectJ 的 AOP 模式来解决这个问题。

@CacheEvict 的可靠性问题

我们看到,@CacheEvict 注释有一个属性 beforeInvocation,缺省为 false,即缺省情况下,都是在实际的方法执行完成后,才对缓存进行清空操作。期间如果执行方法出现异常,则会导致缓存清空不被执行。我们演示一下
清单 31. AccountService.java

@CacheEvict(value="accountCache",allEntries=true)// 清空 accountCache 缓存
public void reload() {
throw new RuntimeException();
}

注意上面的代码,我们在 reload 的时候抛出了运行期异常,这会导致清空缓存失败。
清单 32. Main.java

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-cache-anno.xml");// 加载 spring 配置文件

AccountService s = (AccountService) context.getBean("accountServiceBean");

s.getAccountByName("someone");
s.getAccountByName("someone");
try {
s.reload();
} catch (Exception e) {
}
s.getAccountByName("someone");
}

上面的测试代码先查询了两次,然后 reload,然后再查询一次,结果应该是只有第一次查询走了数据库,其他两次查询都从缓存,第三次也走缓存因为 reload 失败了。
清单 33. 运行结果

real querying db...someone

和预期一样。那么我们如何避免这个问题呢?我们可以用 @CacheEvict 注释提供的 beforeInvocation 属性,将其设置为 true,这样,在方法执行前我们的缓存就被清空了。可以确保缓存被清空。
清单 34. AccountService.java

@CacheEvict(value="accountCache",allEntries=true,beforeInvocation=true)
// 清空 accountCache 缓存
public void reload() {
throw new RuntimeException();
}

注意上面的代码,我们在 @CacheEvict 注释中加了 beforeInvocation 属性,确保缓存被清空。

执行相同的测试代码
清单 35. 运行结果

real querying db...someone
real querying db...someone

这样,第一次和第三次都从数据库取数据了,缓存清空有效。

非 public 方法问题

和内部调用问题类似,非 public 方法如果想实现基于注释的缓存,必须采用基于 AspectJ 的 AOP 机制,这里限于篇幅不再细述。

 

其他技巧

Dummy CacheManager 的配置和作用

有的时候,我们在代码迁移、调试或者部署的时候,恰好没有 cache 容器,比如 memcache 还不具备条件,h2db 还没有装好等,如果这个时候你想调试代码,岂不是要疯掉?这里有一个办法,在不具备缓存条件的时候,在不改代码的情况下,禁用缓存。

方法就是修改 spring*.xml 配置文件,设置一个找不到缓存就不做任何操作的标志位,如下

清单 36. Spring-cache-anno.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/cache

http://www.springframework.org/schema/cache/spring-cache.xsd">

<cache:annotation-driven />

<bean id="accountServiceBean" class="cacheOfAnno.AccountService"/>

<!-- generic cache manager -->
<bean id="simpleCacheManager"
class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="default" />
</set>
</property>
</bean>

<!-- dummy cacheManager  -->
<bean id="cacheManager"
class="org.springframework.cache.support.CompositeCacheManager">
<property name="cacheManagers">
<list>
<ref bean="simpleCacheManager" />
</list>
</property>
<property name="fallbackToNoOpCache" value="true" />
</bean>

</beans>

注意以前的 cacheManager 变为了 simpleCacheManager,且没有配置 accountCache 实例,后面的 cacheManager 的实例是一个 CompositeCacheManager,他利用了前面的 simpleCacheManager 进行查询,如果查询不到,则根据标志位 fallbackToNoOpCache 来判断是否不做任何缓存操作。
清单 37. 运行结果

real querying db...someone
real querying db...someone
real querying db...someone

可以看出,缓存失效。每次都查询数据库。因为我们没有配置它需要的 accountCache 实例。

如果将上面 xml 配置文件的 fallbackToNoOpCache 设置为 false,再次运行,则会得到
清单 38. 运行结果

Exception in thread "main" java.lang.IllegalArgumentException:
Cannot find cache named [accountCache] for CacheableOperation
[public cacheOfAnno.Account
cacheOfAnno.AccountService.getAccountByName(java.lang.String)]
caches=[accountCache] | condition='' | key=''

可见,在找不到 accountCache,且没有将 fallbackToNoOpCache 设置为 true 的情况下,系统会抛出异常。


小结

总 之,注释驱动的 spring cache 能够极大的减少我们编写常见缓存的代码量,通过少量的注释标签和配置文件,即可达到使代码具备缓存的能力。且具备很好的灵活性和扩展性。但是我们也应该看 到,spring cache 由于急于 spring AOP 技术,尤其是动态的 proxy 技术,导致其不能很好的支持方法的内部调用或者非 public 方法的缓存设置,当然这都是可以解决的问题,通过学习这个技术,我们能够认识到,AOP 技术的应用还是很广泛的,如果有兴趣,我相信你也能基于 AOP 实现自己的缓存方案。

参考资料

学习

讨论

5大WordPress论坛插件比较

wordpress已经是一个很强大的博客程序,可是如果再加上一个论坛插件,有时候不是更好。。。呵呵

这里介绍5个比较强大流行的WordPress论坛插件。因为介绍的只是插件,不是单独的论坛程序。。。先来个对比。

我就装上了simple press。大家可以看看我的/forum/看效果。。。

Tal.ki ForumsWP-ForumbbPressSimple: PressZingiri
Facebook & Twitter IntegrationYesNoNoNoNo
Email NotificationsYesNoYesYesYes
Embed Media & VideosYesNoWith
Add-On
YesYes
Search Engine Optimized URLSYesNoYesYesNo
Last ReleaseMay ‘10Sep ‘08Jul ‘09Feb ‘10May ‘10

1. Tal.ki Embeddable Forums

talki forum

该 Tal.ki论坛插件 为WP为简单安装和配置,因为它会自动检测你的WordPress主题和风格本身相应。Tal.ki真正闪耀着它的社会网络集成,允许成员发布求职主题的 Facebook和Twitter的。塔尔。Ki是唯一从其他的插件,同样可以嵌入到论坛的多个网站,而不是只使用WordPress。

2. WP-Forum

wp-forum

最流行的论坛插件安装,拥有超过57,000的用户的插件。论坛支持不同的皮肤和管理员可以选择把客人可选的张贴或关闭。紧密整合,使这个WordPress的插件易于使用和管理。有一点要考虑的是,虽然流行,论坛更新于2008年,并没有因为这样存在安全问题而还没有发布错误补丁。

3. bbPress Bulletin Boards

bbpress forums

虽然它不是一个常规安装插件, bbPress的 是由 Automattic,后面WordPress的人。bbPress的是从WordPress的单独的软件,需要多一点经验成立,但它紧密集成了一个WordPress的博客和它的成员。

4. Simple:Press

simplepress forum

Simple:Press论坛是一个功能丰富的WordPress插件。 它是完全可定制的和图标配备以及若干外观。通过它的强大和灵活的权限系统,适合那些谁希望民办,邀请委员专家论坛上,但同样能够容纳开放,允许访客公众论坛,张贴。

5. Zingiri Forum

zingiri forum

Zingiri 是一个独特的论坛插件,整合现有软件, MyBB的WordPress的,成。 Zingiri提供的胶水,就可以MyBB的,易于使用,功能强大,多语种,功能丰富,软件与WordPress免费论坛。 MyBB的还包括一个信誉系统,多报价,并且能够创建定制的BBCode(称为MyCodes)。

来源:http://www.ylsnuha.com/5-top-wordpress-forum-plugins/

Hadoop应用构建企业级的安全解决方案

理解Hadoop未为企业级应用提供的安全措施

本书的第10章节讨论了Hadoop安全以及Hadoop内部的安全控制机制。在构建企业安全解决方案(这类方案会包括很多与Hadoop数据相关 的应用以及企业服务)的时候,保证Hadoop自身的安全只是其中一个方面。组织机构正努力利用不同安全策略来为从异构数据源提取的数据提供一致的安全机 制。当组织机构从外部不同的数据源中提取数据,并且进行转换,装载到Hadoop中时候,在这些数据要被导入到企业应用的时候过程中,安全方面的挑战就变 的更加复杂。例如,Hadoop 工作组合多个数据集并产生新的组合数据集,对于原始数据集你采用何种安全访问控制策略。

要命的是,许多企业发现Hadoop自身提供的安全级别并不能满足他们所有的需求,他们必须要补充Hadoop的安全模型。例如,一些机构被要求加 密静态数据以满足他们的需求,这样的功能,是Hadoop不具备的。另外,数据科学家进行分析查询的时候,一些企业需要为Hadoop 查询提供细粒度的基于属性的访问控制。虽然这些需求目前已经在Hadoop路线图上面,或者会出现未来的Hadoop 安全策略中(如第10章所述)。这类的功能大大超出了Hadoop 本身能够提供的功能。

因为这些挑战,企业级的解决方案必然要按照一个整体的安全方式去开发,而不是以Hadoop为中心。当然,你可以使用Hadoop自身的安全机制去 满足你的一部分安全需求,但是在许多企业中,你会发现Hadoop的安全机制并不能满足所有的需求。使用Hadoop的企业级的应用必须要结合企业大局, 集成其他的安全机制来计划,设计,以及实施。

在第10章的讨论中,Hadoop的设计和开发是没有考虑安全的,而且在很长一段时间内,几乎没有为Hadoop定制的安全机制。早期社区内的设想 是,Hadoop集群会在可信赖环境中由可信赖用户使用的协同,可信赖的机器组成。在之后,Hadoop社区采用了一个新的安全架构,该架构包括了新的安 全控制(如第10章讨论),但是对于那些有很强的访问控制限制,保密制度,隐私要求,以及合规要求的企业或者组织来说,仍然不能利用Hadoop生态系统 中基本的工具来满足他们的需求。他们想要利用Hadoop的功能,那么他们就必须要把安全也构建到其他工具中,或者另外设计一套使用Hadoop的安全解 决方案。

本章是为想要利用Hadoop,却同样要回应安全问题的企业级应用的开发者而写的。企业级应用需要支持一个企业的安全要求,理解这一点很重要。这可 能需要集成身份访问管理基础设施、以及其他的网络基础设施,这同样意味着要采用其他的独立于Hadoop的安全控制。本章提供了满足这些需求的一种方法论 以及一些可能的解决方案。

本章首先会介绍开发基于Hadoop的企业级应用的安全考量的简要概述。然后,会讨论Hadoop安全不提供的功能,以及一系列构建集成 Hadoop企业安全解决方案的方法,包括现实的例子。最后,本章会简要的涉及一下另外一种安全工具Apache Accumulo,你能够将之应用到Hadoop分布式集群中,Apache Accumulo是一种高级安全存储和检索系统,它由美国国家安全局(NSA)为了精细审计而在Hadoop基础上面构建的。

企业级应用的安全考量

在构建Hadoop解决方案的时候,不仅是考虑Hadoop自身的安全性(如第10章所讨论的那样)很重要,另外要理解全局安全策略以及以数据为中心,这两点也很重要。

如图12-1所示,你需要理解Hadoop数据的生命周期。在构建Hadoop解决方案的时候,数据的生命周期包括:从信息源提取数据,装载数据到 Hadoop Cluster中,运行查询,以及分析,到最终利用结果数据。当数据到达的时候,安全框架的目标就是确保整个数据的生命周期中所有的安全策略都能被强制执 行。

\

不管使用到的工具,数据可以从很多数据源中提取数据,转换为通用的数据格式,然后被装载到Hadoop集群中的HDFS上面去,这个过程通常会被称 为:抽取,转换以及装载的过程(ETL)。数据分析师可以接着运行一些列的MapReduce 工作,以及执行能够产生解决集的查询,这也就是企业级应用所典型的利用Hadoop的方式。

这里的挑战是多方面的,你必须能够在数据生命周期之间的迁移过程中,保证数据的安全性,要坚持最初的安全策略。数据被抽取、装载、和集群中其他机器上面的数据组合、以及产生最终会被其他应用使用的结果集,整个过程都是一个挑战。

图12-1给出关于安全考量点的一个很好的一览,这些安全考量会在本章中被检测。要建立牵涉Hadoop的安全的企业级应用,架构师们需要理解信息 安全的基础,以及他们是如何被应用的。利用Hadoop的解决方案中很多安全目标需要解释,要明白最优方法依赖于理解这些安全考量以及相关术语的含义。这 里的讨论没有提供一份详尽的安全考量点的清单或者是关于每一个安全考量的详细解释,只是提供了作为企业架构师需要知道的为了达到安全目标的简略的信息安全 词汇列表。对于每一个术语定义的目标,你会学习在Hadoop环境中,认识到为什么是重要的。

认证

认证就是识别一个主体的身份。一个主体可以是一个用户,一个应用,一个任务,或者系统中其他的角色。就如第10章中所讨论的那样,Hadoop可以 被配置使用Kerberos来认证用户,服务,以及Hadoop集群中服务器。认证确保了用户和服务正如他们的所表明的身份那样是可靠的用户和服务,阻止 了来自于其他恶意系统的模仿的用户、服务、任务。

需要提及的是,并不是所有的企业在Hadoop系统之外都部署了一套企业级的Kerberos系统来作为认证系统。企业级的应用可能需要把其他的身份访问管理基础设施集成进入他们的应用。

授权

授权就是决定一个主体能够拥有什么样的权限,在主体通过了认证之后,系统必须确定主体的特许凭证,并且需要将主体的特许凭证和一个授权策略做比较, 以给主体提供对请求的资源的访问许可。如第10章所述,目前Hadoop提供一种访问控制级别,这种访问控制利用访问控制列表(ACLs)来表述访问控制 策略,非常类似于UNIX的用户-组的用户权限。

除了Hadoop自身提供的授权机制,大多数企业机构都采用了另外的授权机制。例如,一个企业机构可能拥有以下一种,或者多种授权机制:
?轻量级目录访问控制(简称OLAP)目录、或者活动目录(AD)实例,这两种方式都保存了主体的组、角色以及权限。

?属性服务,这种方式使用属性作为主体的特许凭证

?安全令牌服务(Security Token Services简称STS),这种授权机制是分配与主体的特许凭证对应的令牌,并分配授权决策事务。

?策略服务(Policy Services),该机制是用可扩展访问控制标记语言(eXtensibleAccess Control Markup Language 简称XACML),或者安全断言标记语言(Security Assertion Markup Language简称SAML)来表述对资源访问控制策略,并为主体提供访问控制决策。

使用Hadoop的企业应用需要根据他们自身组织的访问控制策略来控制对数据的访问,也就是说要利用其它的机制来补充Hadoop自身的授权控制。

需要记住的是,在数据的整个生命周期中实施授权控制的一致性是很重要的。如果你的初始数据源自身就拥有对于他们的数据的访问控制策略,为查询数据的 数据科学家提供同样的访问控制策略很重要。同样,对于之后产生的需要被导入到企业应用中的结果集来说,采用恰当合适的访问控制更加重要。这确实是一个挑 战。

机密性

机密性就是限制敏感数据,用户只能看到被授权了的允许看到的部分数据。当敏感数据在网络上传输的时候,这就需要在数据传输过程中,数据不能被偷听者 读取。这可以用过网络加密来实现。一些企业需要磁盘加密,或者是静态数据加密,静态数据加密就是数据只在被存储地方被加密,减少了未受保护数据的被盗窃的 风险。

在第10章中,我们学习到Hadoop提供了网络加密的功能和机制。然而,Hadoop没有提供对静态数据的加密功能。在本章的后面的章节,你可以学习到达到静态数据加密这一目标的集中策略。

完整性

完整性确保了数据在传输过程中或者是落地之后(成为静态数据)不能够被更改。完整性可以通过加密完成,该加密可以利用信息摘要,哈希码,或者是数字签名的副影响。当Hadoop被配置成采用网络加密的时候,Hadoop会在传输过程中保证数据的完整性。

静态数据就是另外一种情况,幸运的是,Hadoop保证了静态数据的完整性,他通过采用副本形式保证数据可靠性。数据的完整性以及可靠性是 Hadoop系统的健壮性,以及他的分布式架构的一个有意的附加影响。由于HDFS被设计成运行在标准化商用硬件上面,用在不同节点上面拷贝数据的方式来 达到容错的目的,正是由于数据副本的数目,校验和检查机制,冲突检测机制,Hadoop为保存在HDFS上面的数据提供了一套保证数据完整性的健壮的机 制。

然而,安全架构师有时会提议一种担心,这就是如果Hadoop当中的一个节点缺乏抵抗能力,一个恶意的用户可能会修改数据,那么这就会歪解数据分析 的结果,这种情况完全可能发生,但是使用Hadoop的安全机制(服务器/服务 认证,数据完整性确认等等),配合深度防御策略,企业级解决方案架构师能够减少这个风险。

审计

大多数的公司都会采用安全审计来保证合规事项的保密性,以及识别潜在的安全缺口。Hadoop当然可以被配置成记录所有的访问日志 -NameNode上面保存一份本地日志,一个审计日志记录器可以被配置将访问信息写入一个特定的安全卷上面以保证日志的完整性。企业当然也可以在授权和 认证方面拥有更多的需求。

注意:尽管关于经典安全目标的大多数的描述都关注机密性,完整性,以及可使用性,这里的描述却关注认证、授权、机密性、完整性以及审计,这是由于这 些都是企业应用的安全的关键方面。可使用性(确保能够访问Hadoop)当然也是很重要的,这点在Hadoop设计的时候就已经被关注了。Hadoop是 高度可靠的,对于可使用性,Hadoop包含一条重要的跟踪记录,Hadoop同时能够部署你企业内部的其他的安全机制(比如入侵检测系统),来保护系统 避免受到Dos攻击。这些都不会在本章讨论,因为这个讨论主要面向于企业应用的开发者。

对于企业应用, Hadoop安全机制不提供什么?

在本章剩余的部分中的内容以及安全术语中,重要的一点是你需要理解企业安全的某些方面,而这些方面Hadoop自身也不能够提供。当然Hadoop 提供了一定级别的认证(Kerberos)、一定程度的授权(ACLs 以及 UNIX级别的文件权限)、以及支持网络加密和完整性的能力。然而,仅仅靠Hadoop,还有很多安全方面不能提供。

面向数据的访问控制

除了在HDFS上面的针对用户和组的ACLs(访问控制列表)以及基于POSIX的文件读写权限,Hadoop自身不会跟踪数据的访问控制策略。正 如你在本章学到那样,很多企业机构都会根据不同的策略来限制对数据的访问,而这些策略通常又非常复杂。可能会出现一些情况,Hadoop系统中数据集中的 一部分数据不能被数据分析师访问,这些数据分析师可能不能访问MapReduce工作的结果集以及查询的结果。

下面介绍了一些好的例子:

一个卫生保健机构可能要求医师只能访问和他的相关病人的数据,并且只能在正常的工作时间(早上9点以及下午5点)访问数据。这就意味着需要对病人 的数据提供访问控制,需要一个系统,该系统能够基于角色(医师),以及时间(比如正常的工作时间)来限制数据访问权限,同时还要判断该数据是否属于这个医 师的病人。

一份政府文件可能要根据一个用户的国籍,以及/或者一个安全检查的要求,该安全检查通常被称为强制访问控制。

某公司的金融咨询师不能访问该公司竞争方的计划以及建议。这种情况被称为“利益冲突”或者是“中国长城”策略。

一个大学可以搜集各个部门或者子机构的学生信息,可能涉及金融,医疗记录,校园警察。大学可能需要根据部门或者角色(医疗、警察、金融策略)来控制数据的访问。

这里面的每一个例子当中,Hadoop自身的安全机制不能够轻易的实行这些访问控制策略。一些挑战可能是架构方面的,这需要根据MapReduce 的算法。导入的数据可能在开始的时候和访问控制策略关联好(比如在数据用安全策略标记的例子当中)。然而,当数据分布在HDFS上面或者和其他数据集连接 的时候,这就可能会导致数据和安全策略之间产生裂缝。这可能导致新合成数据上面的访问控制策略就会不那么清晰。

对于需要这种级别的访问控制的机构来说这就是一个问题,本章的后面的内容中会检验该问题。

差异性隐私

对于无意泄露的信息的研究工作已经开展了将近40年,包括统计学数据库,到与数据挖掘有关的安全以及隐私关注。在数据科学当中,微软研究院的Cynthia Dwork博士使用差异性隐私(differential privacy)来定义这一领域。

差异性隐私关注于保护来自不同数据集或者数据库的信息,避免其被泄露。由于Hadoop以及其他数据分析平台能够利用大量计算资源来处理众多不同的 大数据集,包含了重要的隐私和法律含义的差异性隐私就变成了热门话题。在诸如美国健康保险携带和责任法案(Health Insurance Portability and Accountability Act 简称HIPAA)等隐私保护数字法当中,差异化隐私显得尤为重要。

即使一个Hadoop数据集的隐私保密信息已经被略去,而该数据集可能只包含(或者配合)其他看起来并不有害的信息,这些看似无害的信息仍然可能被 用来泄露个人的身份或者其他敏感信息,这就导致了违背隐私策略。可能会合成来自多个不同的Hadoop工作中提取的数据信息,这样,数据分析师或者 Hadoop用户就不能看到这部分公开的信息。然而,Hadoop自身是不提供这种差异性隐私的保护功能的。当然,Hadoop包含了为内部用户提供了访 问控制隐含式,对和其他组织共享统计信息以及数据的组织也提供了非常重要的隐含式。

因为被众多组织采用的Hadoop是强大的分析平台,它可以被用来发现你可能不能发现的信息。组织机构需要在向公众或者合作方发布数据之前都需要再 三思考。根据你的实际环境,对你的数据可能会有一些内部的控制,要明白,一些Hadoop用户在他们的分析查询当中可能不允许看到某些结果。这也是NSA 所关注的一点,NSA开发并发布了Accumulo,Accumulo项目是属于Apache的开源项目,他提供单元级别(cell-level)的安 全。

差异性隐私问题举例
最为广泛关注一个差异性隐私的例子发生在Netflix。在2006年,Net?ix开展了一个奖金为1百万美元的比赛,来给它的电影推荐系统提高 10%。为了给参加比赛的开发者在比赛当中有数据可用,Netflix发布了一些的被处理过的匿名训练数据集,其中包含了一百万订阅者的电影浏览历史信 息。该数据集中有Netflix用户观看电影的排名,但是没有个人身份信息。两个研究者,来自于奥斯汀的德克萨斯州大学的Arvind Narayanan博士以及Vitaly Shmatikov将Netflix的数据集和互联网电 影数据库(Internet Movie Database,简称IMDB)评论数据,利用新的反匿名话算法(de-anonymization algorithm)。他们发表了一份研究报告,该报告表示他们能够在数学上识别Netflix数据集中的很多用户。基于一个用户的某些电影在IMDB中 排名信息,研究者表示他们的算法能够识别在Netflix数据集中相同的用户,从而能够找到Netflix用户在2005之前的所有电影浏览历史,这就导 致能够获取用户的有关宗教信仰,性别以及政治主张等潜在的信息。结果,一个Netflix用户起诉Netflix发布的有关他们的数据违反了视频隐私保护 法案(Video Privacy Protection Act 简称VPPA),并暴露了她是一个同性恋。为解决这场官司,2010年Netflix支付了9百万美元。

就在同时,出于研究目的,AOL发布了一些“匿名的”搜索引擎日志。一个纽约时报记者利用电话薄对照这个数据集就能够识别一个用户。这份数据集当中 包含了AOL用户的三个月的搜索历史,其中一些信息相当令人相当尴尬。该事件直接导致了AOL的CTO的辞职,以及两个AOL雇员被结果,以及一起针对该 公司的集体诉讼。

还有其他无数需要注意的例子。比如,MIT的一个研究者能够从一个匿名的州保险数据库中结合公开可用的州选举登记记录识别她主管的医疗记录。

这些例子证明安全问题迫在眉睫,例子显示出数据集是如何能够被组合使用,从而违反隐私法律,法规,以及绕过用户的访问控制限制。通过使用这些相同的原理,如果你并没有进行恰当的控制,内部的Hadoop用户可能能够绕过安全限制。

加密静态数据

由于对存储在磁盘或者终端用户设备上面信息的机密性有太多的威胁,许多拥有敏感信息的的组织机构都需要对静态数据进行加密。这种需求的原因与来自恶 意软件的威胁有关、也与数据敏感度和机密性有关,或者是与法律条例有关。例如,HIPAA就包含了对静态加密数据的指导条例,这些静态加密数据与电子受保 护的健康信息(ElectronicProtected Health Information简称EPHI)或者其他受法律保护的个人身份信息(Personally Identi?able Information简称PII)。

一些组织机构正在努力对存储于HDFS上面的静态数据进行加密,而这种加密是Hadoop自身并不提供的。然而,第三方库以及其他的产品能够配合Hadoop一起来满足这些需求,Rhino项目(在第10章讨论那样)正致力于解决Hadoop当中的这个问题。

企业安全集成

大多数的企业在他们内部都有各式各样的安全基础设施,包括进行身份认证的公钥基础设施(Public Key Infrastructure 简称PKI)组件、活动目录(Active Directory)、安全令牌服务(Security Token Service)、属性服务、用户认证的策略服务器,策略服务器提供了授权特许凭证,并且制定和执行访问控制决策。Hadoop自身的安全功能并不能总是 能够嵌入或者集成每一个组织的安全基础设施。当安全需求要求企业应用要和一个组织的安全基础设施进行集成的时候,安全架构师的的职责就是设计出一套解决方 案能够利用其它工具将Hadoop与这些安全基础设置进行集成起来。

保护使用Hadoop的企业应用的方法

近来,大量的项目,包括Hadoop附属项目,或者是专有的Hadoop发行版都许诺要强化Hadoop的安全。Hortonwork的Knox Gateway项目,Intel的安全加强版Hadoop的发行版,以及一些诸如Rhino等开源项目,Rhino已经发布了并且承诺帮助企业应用开发者 来达到他们安全要求。不管怎样,每一个企业应用都不一样,每一个应用部署所需要的安全要求也不一样,认识到这一点非常重要。

在进入到具体细节之前,你必须理解为使用Hadoop的企业应用而提供的一些通用的基本要素和指导方针。对于这些基本要素和指导方针,很多项目在关注某些安全机制的时候出现了偏移,他们并不按照那些适用于任何项目的通用的指导方针。

确定你的安全需求 -- 理解你的安全需求非常重要。对于认证、访问控制、审计、加密、完整性、以及保密性的需求都取决于你的组织。相对于Hadoop运行时自身的安全 (Hadoop自身的安全提供了针对执行查询或者运行工作的应用以及用户的访问控制),围绕Hadoop MapReduce工作的结果数据集的安全,你可能会提出这样问题。要和合适决策制定者交谈,以理解什么是需要的,这样你才能制定相应的计划。

从开始就围绕安全而设计 -- 这些项目的一个共同的重大的问题是最终还是试图改进安全性--这种做法导致了架构变得短期化并且脆弱,而且使得项目最终注定失败。如果你项目中存在类似于 本章讨论的安全性需求,而你认为你只关注数据分析而在后来才去担心去保护你的方案的时候,那么你就会面临着非常大的风险。重点开发初期的高级安全架构的, 这些架构可以和一些合适的权威专家一起讨论,以获得概念上的赞同。

不要保护你不需要保护的东西 -- 如果你为了达到本章讨论的某些目的而不需要安全性需求是,那么就不要采取安全性保护措施。不要让不必要的需求的加重复杂性,或者是负载的性能。

使用深度防御方法 -- 不要设想通过一个单独的安全性措施或者机制来阻挠或者阻止一次攻击或者是违反安全策略的行为。深度防御方法涉及到过个层次的防御。

记住大局 -- 理解你的数据的生命周期,如图12-1所示的那样。要明白提供安全性保护可能意味着在整个数据生命周期(从原始数据源的数据,到被转载到Hadoop集群中的数据,指导最终的结果数据)当中都要控制访问,以及维护和执行安全策略。

下面的部分就会深入研究满足某些安全性需求以及补充Hadoop自身安全性功能的方法。讨论会关注于三个主要的方法,这些方法配合Hadoop自身的机制构建企业安全的保护墙,在下一个部分当中,将讨论Apache Accumulo。

利用Accumulo的访问控制保护

Apache Accumulo是一个稀疏的、分布式的、分类 、多维的键/值存储方案,他具有单元级别的精细访问控制的特性。他是由NSA在2008年根据Google的BigTable的论文开发的,在2011年 的时候Accumulo被发布到Apache开源社区里面。目前Accumulo已经成为Apache的顶级项目。Accumulo是一个高度可扩展的 NoSQL数据库,他建立在Hadoop和Zookeeper之上。Accumulo被开发的一部分原因是要解决大数据的安全性问题。

Accumulo扩展了BigTable的数据模型,但是添加了一个元素以提供单元级别的强制基于属性的访问控制(Attribute-Based Access Control,简称ABAC)。所有被导入到Accumulo的数据都可以通过可视化的控制来标记,基于访问控制策略当中的可视化控制,数据分析师查询 数据的时候,他们只能查看到被允许看到的那部分。Accumulo的API提供了可供你自由实现客户端,自定义的客户端程序可以认证用户、和企业属性服务 集成,企业属性服务可以拉取用户授权凭证以提供一定程度的访问控制。Accumulo同样提供了存储用户和授权信息的功能。

如图12-2所示,Accumulo是一个key/value的存储方案,Accumulo的key包含了5个元组。Accumulo中的Key是 由RowID、Column Family、Column Quali?er、Column Visibility、Timestamp组合而成,这个5个元素的key关联一个value。

\

这个5元组的Key提供了原子性、局部性、独特性、访问控制和版本控制。需要注意的是Key当中的Timestamp提供了同一数据包含多个版本的 功能,Timestamp根据不同的时间和日期。在很大程度上,Accumulo借鉴了BigTable的大部分数据模型,但是加入了可视化元素以提供面 向数据的安全性--这就会只返回那些可视化标签满足于执行查询的用户或者应用的凭证的单元。

Accumulo以及HBase当中数据级安全性差异
HBase和Accumulo相似--都是运行在Hadoop上面的BigTable的实现的Apache项目。HBase和Accumulo都提供了相 似的数据级安全性,但是是以不同的方式。正如第十章所讨论的,HBase提供了对每个表或者每个列的数据访问控制。在这点上面,HBase不提供单元安全 性,而这正是Accumulo提供的。但是,目前在Intel的发行版本Rhino项目中已经在进行类似的工作,在不久Hbase也会支持基于单元的安 全。HBase能够很容易的集成Hadoop安全模型(使用Kerberos),而这种方式和Hadoop生态系统的其他项目保持了一致。另外一方 面,Accumulo是Apache新的顶级项目,他包含了一个内置的访问控制数据库,而他没有采用和Hadoop生态系统的其他项目一样的方式。

HBase和Accumulo都很流行。由于Accumulo的单元级的安全性,在一些高级安全环境中,Accumulo更加普遍,Accumulo许诺提供和强制执行访问控制(Mandatory Access Control

简称MAC)以及其他不同的安全性相关的解决方案。

大多数的开发者都比较熟悉关系型数据库,这些数据库的表结构如表12-1所示,示例的表是一个二维模型。

表12-1:关系型数据库模型示例

\

在Accumulo结构中,相同的数据如表12-2所示,Accumulo中数据按照比较细的粒度存储,包括可见度(visibility)以及一个timestamp,其中timestamp可以使你跟踪超时的数据的改变。

表12-2:Accumulo数据模型中的数据

\

需要注意的是,在表12-2中,可见度被用安全标记进行了标记。这些安全标签能够被添加,使用AND/OR的布尔逻辑。例如,你可以提出如表12-3所示的授权策略,表12-3中授权凭证可以被新建以及和用户关联,以限制表中每一个单元的访问。

表12-3:安全策略以及对应的安全标签示例

\

在Accumulo的安全模型中,需要用户访问信赖的客户端(作为一个开发者,你可以编写这样的客户端)进行认证,客户端要负责认证用户,并且向 Accumulo发送合适的授权信息。你可以选择集成你自己的认证和授权系统,或者你可以使用Accumulo内部的认证/授权的组件,该组件当中存储了 用户和用户的授权凭证。本节当中会提供每种选择的例子。

一个简单的例子

让我们用一个大学的数据库作为例子,该大学从不同的部分和子机构当中搜集学生的信息。在例子当中,你会看到来自校园医疗中心、财务中心、大学管理、 体育设施、大学警备人员的数据。很大范围内的数据都被收集了,并且能够被所有的机构查询,你可能被要求保护这些数据,这些要求包括如下几种:

学生的医疗检查记录只能被医疗人员或者是管理人员浏览。

大学体育课当中学生的与体育记录相关的记录只能对他们的教练、有时一些医疗人员可见

付款记录,成绩,如社会保险号码等敏感信息只能对大学管理人员所见

关于学生的校园警备记录只能对校园警察或者大学管理人员可见。

对于这个例子,让我们加载一些示样数据到Accumulo数据库中,如表12-4所示,在这个简单的例子中,有一个名叫Kirk Rest的学生,他有各种各样的信息,这些信息来自该大学的众多数据源。

表12-4 大学数据示例的Key/Value(待续)

\

表12-4 大学数据示例的Key/Value(续)

\

假设装载这部分信息的Accumulo存储是建立在一个测试Hadoop实例上面的。为此,你需要使用Accumulo的shell,该shell 是Accumulo一个简单的客户端,能够被用来创建和修改表,同样能够被用来创建用户以及给用户指定授权凭证。因为本章将关注与安全性而不是 Accumulo的细节,所以不会在本章提及装载数据的细节。

注意:由于不同版本的Accumulo使用了不同版本的Hadoop和Zookeeper,有时设置Accumulo是 很大的挑战。一个开始Accumulo的简单的方法就是使用一个已经配置完了的虚拟机(VM)。Sqrrl公司的网站提供了一个亚马逊单机实例 (Amazon Machine Instance 简称AMI),借助它可以快速的开始Apache Accumulo,Sqrrl公司也提供了一个Oracle VirtualBox的VM。具体细节,查看他们的网站http://www.sqrrl.com/

VM的网址:http://blog.sqrrl.com/post/40578606670/quick-accumulo-install/

Accumulo被优化成能够快速提取key的value的系统。为此,一个你开发的Accumulo客户端(在这里的情况中,客户端可以是 Accumulo的shell)可以创建scanner,scanner可以迭代value。如你看到Listing 12-1,scan命令使得你迭代查询一张你新建名叫universitydata的表的value值(root用户被授权可以访问表中所有的内容)

\ 为了证明单元可见性的例子,你创建如下几个用户:
一个医生(drhouse),你给他授予了MEDICAL 角色

一个管理员(denyseAccountant),你给他授予了ADMIN角色

一个教练(coachTark),你给他授予了COACH角色

一个警察局长(chiefDoug),你给他授予了Z喎�"http://www.it165.net/pro/pkqt/" target="_blank" class="keylink">QT0xJQ0W9x8mrPC9wPgo8cD7I50xpc3RpbmcgMTItMsv5yr6jrMTjyrnTw0FjY3VtdWxvIHNoZWxsuPjL+dPQ08O7p9a4tqi2wcihse21xMioz96jqMra0+jL+8PHw7+49sjLVGFibGUuUkVBRMioz96jqaOsyLu688q508NzZXRhdXRoc8P8we7Oqsv7w8fWuLaovcfJq6Os1eLSu7K9ysfOqsHLyei2qL/JvPu2yKGjPC9wPgo8cD4gPGltZyBzcmM9"http://www.it165.net/uploadfile/files/2014/0218/20140218191649452.jpg" alt="\">

最后,为了显示Accumulo将会保护表中的数据,你用Accumulo sheel以每一个用户进行登录。你可以scan(或者迭代)整个universitydata表。Listing 12-3,显示了你可能如何用用户coachTark,drHouse,denyseAccountant和chiefDoug,并进行表迭代,表根据每 一个用户的权限进行访问控制。 \

从Listing 12-3可以看到,Accumulo可以根据每一个用户授权控制来限制访问,这一点可以通过使用Accumulo shell来证明。现在,让我们来使用JAVA建造一个Accumulo Client,来证明本例中同样级别的访问控制,该客户端同时还会和企业内的身份识别和访问管理基础设施集成。

因为Accumulo适合于集成企业的基础设施,Accumulo拥有一个灵活的模型。如前面的提及的那样,Accumulo Client的责任就是认证用户并提取用户的授权凭证,发送凭证给Accumulo以进行处理。只要Accumulo的表中数据的可见性属性被用与你企业 属性存储系统中相同的属性和角色标记了,数据的可见度就会工作良好。如果不是,你会需要在Client端中对这些从属性存储系统中拉取的属性进行一下处 理,以保证他们具有相同的特征。

为了证明这点,让我们通过一个简单的例子,该例子描述了如何编写一个简单的Client来认证用户,认证可以通过你选择的认证机制来完成,以及从由你选择的属性服务或者LDAP目录拉取授权凭证。

Listing 12-4 显示了一个例子,该例子使用JAVA类来连接Accumulo。为了连接,你必须使用Connector来建立一个Accumulo连接。为此,你需要首 先连接到Zookeeper实例上,该Zookeeper实例使用ZookeeperInstance类来跟踪 Accumulo,ZookeeperInstance会返回一个connector。

\

一旦你建立了一个连接,你想要认证一个用户。在这种情况下,对于这个简单的例子来说,你从命令行获取用户,并且将之传递给一个外部类叫做 CustomAuthenticator,该类简单的显示了你可以使用另外一个在Accumulo外部的认证机制以及授权机制。在 CustomAuthenticator类中,你把从命令行扫描到的用户名和密码传递到authenticate()方法中。如果一个用户成功通过认证, 然后你就可以从外部的存储系统中拉取用户的授权凭证,凭证在Accumulo的类 org.apache.accumulo.core.security.Authorizations类被返回。最后,如在之前的例子中所示的那样,你创 建一个scanner来迭代在同一个表中value值,然后答应结果。

Listing 12-5 显示了命令行的结果。在这个例子中,你设置了一个外部的LDAP目录,该目录中有一个用户名为joeUser,他的角色为ADMIN。

\ 本例中被认证的用户,joeAdmin,并不像之前的那些例子那样被存储在Accumulo当中。如这里表示的那样,你可以编写一个Client来认证一个用户,从企业的存储系统中拉取授权凭证,以及查询Accumulo的数据。

关于Apache Accumulo还有很多东西--很多不能在本章中覆盖到。然而,重穴ky"http://www.it165.net/qq/" target="_blank" class="keylink">qq1xMrHzqrBy8r9vt2wssir0NTKudPDQWNjdW11bG+1xNfp1q+7+rm5o6zSqsjPyra1vUFjY3VtdWxv1rvKx8bz0rWwssirveK+9re9sLjW0LXE0ru49re9w+aho8bz0rWwssir0OjSqsnutsi3wNP5o6yx2NDruLK4x7W9yv2+3bXE1fu49sn6w/zW3MbawO/D5qOstviyu732vfbU2khhZG9vcLWx1tC1xMr9vt2hozwvcD4KPGgzPrzTw9y+ssysyv2+3TwvaDM+CjxwPrzTw9xIYWRvb3DW0L6yzKzK/b7d0tG+rcrHuty24M/uxL/W0LXEyMjDxbuwzOLByy0t1eLQqc/uxL/W0NK70KnKx7+q1LS1xKOs0rvQqcrHyczStbXEoaNIYWRvb3DX1MntsqKyu8zhuanV4tH5tcS808PcuabE3KGjxL/HsKOsuty24Lmry77V/dTasaO7pM6709qyu82st6LQ0LDmtcRIYWRvb3DW0LXEw/S40Mr9vt0ssru99r32ysexo7ukw/S40NDFz6Is0rLTrbrPyOdISVBBQdXi0fm1xLeowsm78tXfxuTL+7CyyKu3qLnmLrrctuDX6davu/q5uc/r0qrA+9PDvNPD3L6yzKzK/b7dwLSxo7uksdzD4rbx0uLTw7unu/HIobbURGF0YU5vZGW1xM60vq3K2siotcS3w87KoaM8L3A+CjxwPsS/x7DS0b6tzOGz9rXE0rvQqb3ivva3vbC41tCjrLD8wKhHYXp6YW5nIHpOY3J5cHSjrLjDt72wuM6qQ2xvdWRlcmG1xLei0NCw5szhuanBy8r9vt2wssir0NSxo7ukoaPT2tTaMjAxM9Tnxtq3orK8tcRJbnRlbLXESGFkb29wt6LQ0LDmo6zS0b6tvq25/dPFu6/AtLzTw9y+ssysyv2+3aOs1eLQ6NKq1NrKudPDuMO5q8u+tcRYZW9utKbA7cb3oaO6w8/xo6zDv8zstrzT0NDCtcS94r72t72wuLP2z9YtLbWryse1vcS/x7DOqta5o6zL+dPQtcS94r72t72wuNKqw7S+zcrHy73T0LXEo6zSqsO00OjSqr2rxOOw87ao1NrEs9K7SGFkb29wtcS3otDQsObW0KGjPC9wPgo8cD7I57XaMTDVwtbQzOG1vbXExMfR+aOsUmhpbm/P7sS/o6jT1kludGVsubHP17j4QXBhY2hlyefH+KOpsPy6rMHL1PbHv7mmxNyjrLD8wKi31rK8yr1LZXm53MDto6y6zb6yzKzK/b7dvNPD3LmmxNyho0hhZG9vcL+qt6LJ58f4xL/HsNX91Nq/vMLHvavV4rmmxNzSsrfF1NrOtMC0tcRIYWRvb3Cw5rG+1tChozwvcD4KPHA+sru53MTj08PKssO0u/rWxsC0zqpIYWRvb3C808PcvrLMrMr9vt2jrMTjsdjQ68P3sNe+ssysvNPD3LmmxNy1xLfHucrS4rXE07DP7KOs1eK147rc1tjSqqGjyOe5+8Tj0OjSqtK71ta808PcvrLMrMr9vt21xL3ivva3vbC4o6y8x9ehvNPD3Lvh07DP7LW9xOO1xNDUxNyho8jnufvE48jPzqrE47XETWFwUmVkdWNluaTX97/JxNyxyMS/x7DUpM/rtcTSqsL9o6zP68/zvNPD3LvhuPjQ1MTctPjAtMqyw7TR+bXE07DP7KGjSW50ZWy1xEhhZG9vcLei0NCw5s2ouf3TxbuvwLTX9rzTw9y6zb3iw9yjrNXi1tbTxbuvysfNqLn9yrnTw8zYtqi0psDtxve1xNOyvP7AtM3qs8m1xKGjvMfXoda709BJbnRlbLXESGFkb29wt6LQ0LDmyse94brPy/vX1Ly6tcTTsrz+vNPL2cb3oaPG89K1vNy5ucqm0OjSqrriwb+808PcvrLMrMr9vt24+NOm08O0+MC0tcTTsM/sLS3I57n7xOPIt8q10OjSqtXi1ta5psTco6zEx8O00qq4+b7d0NTE3MC0vfjQ0Lnmu66hozwvcD4KPHA+xL/HsKOsxOO/ycTcu+HTydPatuC49tSt0vLP67Hcw+LKtcqpvrLMrMr9vt2808Pco6yz/bfHxOO8zND40qrKtc/WoaPK18/Io6y+ssysyv2+3bzTw9zB7NPyyse3x7OjuLTU07XEo6zS8s6qt9ayvLXEyv2+3brNa2V5udzA7aGjwe3N4qOsUmhpbm/P7sS/uPjV4rj2wezT8rT4wLS1xMzhuN+8tL2rtb3AtKOstavKx9TatMvWrsewo6zE47/JxNy74bDztqjU2sSz0rvM2LaotcS3otDQsOa78tXfs6fJzMnPw+aho9fuuvOjrMjnzOG1vbXExMfR+aOstObU2r6yzKzK/b7dvNPD3MC0tPjAtLXE0NTE3MnPw+a1xLrzufuho8jnufvE47XEyv2+3crHyOe0y7XEw/S40NLU1sHT2sTj1f3U2sy9y/e808PcvrLMrMr9vt21xL/JxNzQ1KOsz8LD5rXEsr+31r/JxNy74crH0ru49r3ivva3vbC4oaM8L3A+CjxoMz7N+MLnuPTA67rNt9bA67e9t6g8L2gzPgo8cD7I58eww+bM4bW9tcTEx9H5o6zTtdPQsaPD3LrNw/S40LXEyv2+3bXE1+nWr7v6ubm/ycTc0rvWsbvh08PN+MLnuPTA67XEt723qMC0uPTA68v7w8e1xEhhZG9vcLyvyLqjrNPD1eLW1re9t6jAtML61+PL+8PHtcSwssir0OjH86Gj1eLQqbv6ubnNqLOju+G7+dPa08O7p7y2tcTK2siowLS/2NbGttTEs7j2taW2wLyvyLq1xLfDzsqjrNXi1tbTw7unvLa1xMrayKi7+tbGzaizo8q508PO78DtsLLIq8C01/fOqrGju6S7+tbGoaPG5Mv70rvQqbv6ubm74cq508O1zc/e1sa1xLe9t6ijrLHIyOe31sDrzfjC56OstavKx9TK0O3K3NDFyM63/s7xxve6zbmk1/fVvsTcubvU2sG9uPbN+MLnwO/D5r340NC0q8rkoaM8L3A+CjxwPtXi0Km3vbeoyNTIu8rH0ru49r/J0NC1xNGh1PGjrNSt0vKw/MCoo7o8L3A+CjxwPj88c3Ryb25nPrCyyKvQ1Lyvs8m1xLi01NPQ1Dwvc3Ryb25nPiAtLSDI57n7xOO1xLCyyKuy38LUt8ezo9HPJiMyNjY4NDujrMTjtcTK/b7dt8ezo8P0uNCjrNLU1sHT2sTjsru1w7K70OjSqryvs8m088G/SGFkb29w19TJ7c60zOG5qbXEsLLIq9DUv9jWxrW9SGFkb29wtbHW0KOsv7zCx8q508PN+MLnuPTA66OsvavL/LauxuTL+834wufW0LfWwOuz9sC0o6yyosfSz97Wxta71MrQ7bG7ytrIqLXE08O7p7fDzsqho8jnufvE49f2wcujrMTHw7TE49a70OjSqrWj0MRIYWRvb3C94bn7yv2+3byvtcS3orK8o6y2+LK7ysdIYWRvb3DUy9DQyrG1xLCyyKvQ1KGj1eK+zdfu0KG7r8HLxOO1xNX7zOW358/Vo6yyosfSxNy5u7z1ydm088G/tcS5pNf3oaM8L3A+CjxwPj88c3Ryb25nPtDUxNw8L3N0cm9uZz4gLS0gobCwssirysfQ1MTctcS10MjLobHV4r7ku7C+rbOjsbvM4bywoaPU2tK7uPa94r72t72wuNbQo6zE47zTyOvBy9S9tuC1xLCyyKvQ1Lv61sa1xMqxuvKjrL3ivva3vbC4vs274bHktcPUvcL9oaPU2rGju6RIYWRvb3C1xMqxuvKjrNXitePIt8q1yOe0y6OszNix8MrHyOe5+8Tjv7zCx8q508O12sj9t721xLmkvt/AtLzTw9y6zb3iw9zOu9PaSERGU8nPw+a1xMr9vt21xMqxuvKho9DttuDIy7a8u+HKudPDzfjC57j0wOu1xLe9yr3AtMfh0te1xLHcw+LBy9DUxNzL8LrEoaM8L3A+CjxwPj88c3Ryb25nPsr9vt28r7XEsu7S7LuvsLLIq9DUPC9zdHJvbmc+IC0tIMTj1+nWr7XE0rvQqcr9vt2/ycTc1rvE3Leisry4+MSz0KnM2LaoyMvIuqGj1NrV4tbWx+m/9qOsSGFkb29wuaTX97XEveG5+7yv0rLNrNH5w/S40KGjy+TIu9K70Km6zUhhZG9vcM/gudi1xLmkvt+jqLHIyOdIQmFzZaGiQWNjdW11bG+jqcTcubvM4bmp0rvW1tTawdC8tqOoSEJhc2WjqbrNtaXUqry2o6hBY2N1bXVsb6Opuf3Cy7fDzsq1xLe9yr2jrMbky/vKudPDSGFkb29wtcS5pL7fsru74czhuanV4tbWsLLIq7y2sfCho8jnufvE49X91NrUy9DQSmF2YSBNYXBSZWR1Y2W5pNf30tS9qNTsxLPQqb3hufu8r6OssqLH0sq508PBy7Tzwb+1xLK7zai1xLmkvt+jrNKy0O2/vMLHuPm+3b/Jt8POyrXEyMvIusC0t9bA67yvyLrKx9K7uPaxyL3PtM/D97XE1/a3qKGjPC9wPgo8cD4/PHN0cm9uZz5IYWRvb3Cwssir0NTAts28tcTR3b34PC9zdHJvbmc+IC0tINXrttRIYWRvb3C1xNDttuDQwrXEsvrGt6Ost6KyvMa3oaK3otDQsOaxvra8zOG5qcHL0MK1xLCyyKvM2NX3oaPI57XaMTDVwszhvLC1xKOs1eu21EhhZG9vcLXEsLLIq9DUtcTU9se/1Nq9/Ly4xOq8tL2rtb3AtKGjvLS9q7b4wLS1xLjEseS74dOwz+y1vcq508NIYWRvb3DG89K1tcTTptPDo6zU2sP3sNfV4tCpuMSx5LT4wLS1xLrzufvWrsewo6zT0LrctuDX6davu/q5udX91NrRodTxzfjC57j0wOu1xMSj0M2hozwvcD4KPHA+PzxzdHJvbmc+1Nq8r7PJ1q7HsL340NDK/b7dsLLIq8bAyfM8L3N0cm9uZz4gLS0gINPJ09rN+MLnuPTA67XEt73KvbK7xNy21M6709rG5Mv7zfjC58nPw+bG89K106bTw7XEyv2+3b340NDKtcqxt8POyqOs1NrG89K106bTw8q508PXysHP1q7HsKOszfjC57j0wOvUytDtyfOy6brNuf3Cy9Xi0KnXysHPo6zV4rz10KHBy7/JxNy1xLGjw9zQ1LrN0v7LvbXEx9a3uKGjo6i21NPayrXKsUhhZG9vcMC0y7XN+MLnuPTA67/PtqjKx9K7sNHLq8jQvaOjrLKix9LOqsHLt6KyvMbz0rXTptPDyrnTw7XEyv2+3byvo6y7udDo0qrSu7aotcS0psDto6mhozwvcD4KPHA+xvPStdOm08Owssir1tCjrM34wue49MDrxNzU2rrctuC3vcq9yrXP1qOszbwxMi0zIM/Uyr7By9K71ta3vcq9oaPSu7j21+nWr7v6ubm0tL2owcvSu7j2obBEYXRhIEFuYWx5dGljc6GxzfjC56Osy/vT69fp1q+1xMbz0rXN+LfWzai5/dK7uPbO78DttcShsGFpciBnYXChscDrv6rAtKOs0tTX6Na5yv2+3dTawb249s34wufWrrzkvfjQ0LSryuSho9TaobBEYXRhQW5hbHl0aWNzobHN+MLn1tCjrNPDu6fHobWxt8POyr/Y1sa1xMr9vt231s72yqbU2khhZG9vcLyvyLrW0L340NCy6dGvus1NYXBSZWR1Y2Wy2df3o6y21NPa1eK49s34wue1xLfDzsq/2NbG09bO78DtsLLIq8nosbi78tXfzai5/bbU1rTQ0LLp0a+1xL/Nu6e2y7v6xve9+NDQyM/WpKGjPC9wPgo8cD4gPGltZyBzcmM9"http://www.it165.net/uploadfile/files/2014/0218/20140218191650456.jpg" alt="\">

图 12-3 “Airgap”网络隔离内的导入导出工作流

为了支持使用这些结果数据集的企业应用,需要开发和利用一些非常重要的工作处理:

首先,任何需要被分析的数据都必须从在企业网中数据库和应用中提取到,写入,并带入到这个隔离的网络。一旦数据准备好被分析了,并且也被装载到集群 中,Hadoop MapReduce工作就能运行,直到结果能被检阅。基于最终的结果,结果集必须被用授权策略进行标记,该授权策略也能够被外部的企业应用控制,或者结果 集必须被过滤,保证敏感、机密或者保密的数据被移除掉。一旦数据集被过滤或者被用访问控制策略标记过了,数据集就被写入到介质中,导入到企业应用中。对于许多机构,由于要牵涉到两个网络,这就需要一个笨重的处理,就需要一个从原网络导出数据,并导入到数据分析网络,进行分析,过滤,然后为了企业应用准备结果集。即使这个过程非常笨重,然而,在数据非常敏感的时候,许多组织机构都会采用这种方式。

因为牵涉的复杂性,许多组织都转向一个类似的模型,该模型限制了企业网内的可信赖主机到数据分析网络的流量,如图12-4所示,ETL处理过程可以跨网络进行,移除了之前描述的第一步。

图 12-4 单向传输的网络隔离

有些机构优先选择Apache的Accumulo来对数据进行访问控制,他们把结果集进行数据过滤之后,发布到企业网中--比如,创建一个“企业网 用户”,该用户拥有的凭证等于该网络最低级别的授权。根据该用户过滤数据,通常会使结果集能够很容易的传送,而不用担心信息的非故意的泄露。

网络隔离可以通过其他很多种方式来实现--这只是其中的一些。其他涉及到Hadoop集群网络隔离方式需要根据数据的类型,一些低限制的方式需要允许连接到企业网络中机器或者从企业网络中的机器连接出来,这需要使用访问控制列表(ACLs)来对可信赖的主机进行限制。

你的解决方案中对于企业安全性的每一部分都依赖于你的组织机构的安全需求--两个机构很少一样,无论如何,本章中示例和指导意见应该能够帮助你构建你企业的安全解决方案。

总结

本章从安全策略以及以数据为中心的观点出发,为你提供了关注于数据的生命周期的企业安全视图。安全架构师在能够利用Hadoop和其他补充安全工具来解决企业安全性的不同方面,要理解这个大局,这点很重要。

本章开头介绍了开发使用Hadoop的企业应用的安全关注点的一个总的简图。你学习了Hadoop自身没有解决的一些安全挑战点--包括数据集安 全,差异化隐私,静态数据加密。你看到很多结合Hadoop构建企业安全的解决方案的方法,并且学习了一些指导意见和示例。你学习了如何使用Apache Accumulo来实现单元级的安全,同时了解了静态数据加密和一些可用的选择。你同时还学习了网络隔离的一些方法,这些方法被许多关心敏感数据暴露的机 构所采用。

安全问题目前正成为Hadoop中不管进化的话题,在接下来的几年时间内Hadoop将会成长的领域。第13章关注于其他一些即将到来的Hadoop的增强功能,其讨论趋势正涌现出来,并且将会在未来不断成长。

 

来源:http://www.it165.net/admin/html/201402/2360.html

Web 2.0 SOA

来源:http://www.ibm.com/developerworks/cn/webservices/redbooks/soa-case/11.html

本红皮书中案例研究的重点是 Web 2.0 SOA 场景,以及 JKHLE 如何使用该场景的 3 个实现来改进公司的旅行代理业务。 本红皮书具体介绍以下 Web 2.0 实现:REST 式服务创建、呈现和使用 REST 式服务、用户界面 (UI) 组合和通信。

Web 2.0 技术概述

Web 2.0 代表一个不断发展的互联网平台。Web 1.0 是指人与计算机的交互以及提高计算机效率的技术。Web 2.0 是指人通过计算机进行交流以及提高使人类生产力的技术(参见图 1)。

图 1. Web 1.0 和 Web 2.0

图 1. Web 1.0 和 Web 2.0Web 2.0 改变了业务与其客户之间的交互方式。注意关于 Web 2.0 的以下信息:

  • 它是可消费服务、富互联网应用和简化的编程模型。
  • 它构建了环境关系并促进了知识共享。
  • 它与人及其协作方式有关。

代表性状态传输 (REST)

代表性状态传输 (REST) 是互联网构建的体系架构模型。REST 之所以流行是因为其简单性和易用性。

REST 提供了以下原则:

  • REST 提供了一种资源导向型服务方式(相对于 RPC 导向型服务方式)。
  • 所有资源都可以通过相对 URL 进行寻址,例如:/JKHLE/employees/JKHLE/employees/sandy
  • REST 通常使用 HTTP 作为传输协议并支持 HTTP GET、POST、PUT 和 DELETE。
  • 可以使用在 Web 浏览器(或任何其他客户端或服务器)中运行的代码轻松访问 REST 式服务,并且可以轻松保留 REST 式服务并利用现有内容。
  • REST 中的领先实践源于 Web 的使用,因为它是为使用而构建的。

RESTful SOA

RESTful SOA(有时称为 WOA 或 ROA)是 SOA 的一个实例,它使用来自 Web 的概念作为主服务架构:

  • 更易实现 SOA 的有限选项
  • 主要使用 REST 表示和访问服务
  • 将数据编码为 JSON 或 XML(包括 XML 模式,比如 ATOM)
  • 在适当的时候可以使用其他方法,比如 JSON-RPC
  • 支持使用 AJAX 构建的 富用户界面

REST 风格的架构保留 SOA 原则。它支持以组件为中心的模型,在该模型中各种服务器端和客户端组件可以以一种可伸缩且简单的方式重用。

案例研究简介

如前所述,JKHL Enterprises (JKHLE) 是一个虚拟公司,它期望能扩展其旅行代理部门。在本节中,我们将介绍 JKHLE 公司的业务和技术挑战,并检查其当前的基础设施(当前架构)。

从事旅游业的 JKHL 公司

JKHLE 拥有一个旅行代理部门,该部门使用一个在线旅行网站提供所有旅游资源的一站式访问。该旅行代理公司的主要业务包括:

  • 航班预订
  • 酒店预订
  • 游艇预订
  • 天气状况
  • 交通状况
  • 城市信息(比如方向和感兴趣的领域)

JKHLE 想开发一种最好的旅行网站。该网站应该能够为客户提供易用的服务,并简化公司的基础业务流程。

JKHL 公司的业务挑战

JKHLE 有以下一些急需解决的业务挑战:

  • 提供一种快速且简单的架构,以快速地向客户和合作伙伴交付新产品。
  • 使用第三方的新界面,如天气和交通报告。
  • 通过利用 SOA 基础设施和可消费的服务端消除业务孤立。
  • 持续快速地提供简单的新业务数据和服务。
  • 降低呼叫中心成本。
  • 以可消费的形式向公共网络公开业务数据

当前架构和技术挑战

KHLE 使用的当前架构如图 2 所示。

图 2. 当前架构

图 2. 当前架构该架构提出了以下技术挑战:

  • 业务策略被嵌入到业务流程中。业务策略的更改需要更新业务流程,但是更新业务流程非常缓慢、昂贵且容易出错。 随着 JKHLE 的持续扩展,此限制将变得尤为突出。
  • 每个业务系统都孤立运行,当试图为客户实现多渠道策略时,这就会造成重大问题。
  • 很难将第三方服务集成到整体解决方案中。但是包含这些第三方服务很重要,因为它们有助于将 JKHLE 的旅行产品与其竞争者区分开来。
  • 类似地,第三方公司在与 JKHLE 的服务集成时也会遇到困难,这就限制了外部各方使用 JKHLE 的旅行服务。

使用 Web 2.0 SOA 场景实现

Web 2.0 SOA 场景可用于帮助 JKHLE 解决其业务和技术挑战。该场景定义了 3 种实现:

  • RESTful Service 创建实现

描述映射到数据源的基于资源的模式。

  • Rendering and Consuming RESTful Services 实现

描述呈现数据的格式,以及基于 RESTful 服务架构的数据使用。

  • UI Composition and Communication 实现

描述从 REST 式服务到用户收集的数据展示。

每个实现都可以组合或单独使用以解决业务解决方案。

将要使用的架构

为了解决其业务和技术挑战,JKHLE 构建了一个新架构,该架构使用 Web 2.0 SOA 场景的原则。图 3 展示了此架构,以及使用实现的位置。

图 3. 将要使用的架构

图 3. 将要使用的架构新的架构具有以下优势:

  • 业务逻辑和业务策略现在是独立的实体,这就支持对业务流程变量的快速、简单且不间断的补充。
  • 简化的开发界面使 JKHLE 业务流程能够更容易地调用第三方服务,并使得第三方服务能够更方便地调用 JKHLE 的服务。
  • 新服务和新渠道可以快速集成到架构中。

客户端和 RESTful 服务器的解决方案

JKHLE 要采用的架构将充分利用客户端和 RESTful 服务器之间的设计模式,如图 4 所示。

图 4. 客户端和 RESTful 服务器的解决方案模式

图 4. 客户端和 RESTful 服务器的解决方案模式该解决方案模式的使用如下所示:

  • 客户端(通常是 RIA)向 RESTful 服务器发出一个基于资源的调用。
  • 服务器将 JSON、XML、RSS 或 ATOM 的负载返回到客户端。通过 RIA 或调用服务来使用返回的负载。
  • 客户端对 XMLHttpRequest (XHR) 调用使用 GET、POST、PUT 或 DELETE 方法,以映射到 RESTful 实体行为。

产品映射

图 5 展示了 JKHLE 用于实现将要使用的参考架构的产品。

图 5. 产品映射

图 5. 产品映射在本红皮书的其他部分,我们将更详细地介绍每个实现,以及 JKHLE 用于实现每个实现的产品。

RESTful Service 创建实现

JKHLE 使用 RESTful Service 创建实现来解决互联网服务域和服务集成域之间的交互,如图 6 所示。

图 6. JKHLE 使用 RESTful Service 创建实现的位置

图 6. JKHLE 使用 RESTful Service 创建实现的位置以下架构考虑因素与 RESTful Service 创建实现有关:

  • 创建 REST 式服务的数据源(比如 Web 服务、数据库和屏幕抓取)
  • Java™ 对象(比如 Java beans、EJB™ 3 和 JPA)
  • 映射到后端实体的资源模型
  • 安全性
  • 治理

设计模式

本节将介绍 REST 式服务创建实现的 2 种设计模式。第一种设计模式如图 7 所示。

图 7. 设计模式

图 7. 设计模式该设计模式描述了以下信息:

  • 现有遗留数据被转换为 REST 式服务。
  • 每个资源有一个 URI/URL,并且这些资源使用 REST 实体模式进行公开。
  • 可以使用简化的实体命名规则。例如:GetOrderServices?ordernumber=12345 可更改为 /rest/orders/12345
  • REST 式服务提供的每种操作都是一种 HTTP 方法。例如,URI 为 /JKHLE/hotel/reserve 的资源能够使用 GET /JKHLE/hotel/reserve 进行调用。
  • REST 服务负责调用应用程序服务。该应用程序服务可以通过一个适配器与另一个后端服务的企业服务总线进行连接。

第二种设计模式如图 8 所示。

图 8. 设计模式

图 8. 设计模式这个设计模式描述了模式的构建方式:

  • 构建 Java EE 工件,比如 EJBs、servlets 或重用现有 Java EE 工件。
  • 使用 WebSphere® Application Server Feature Pack for Web 2.0 公开这些工件,如通过 HTTP 的 JSON,或通过 HTTP 实体的 XML。
  • 必要时使用 WebSphere DataPower® in the DMZ 添加更多 Web 2.0 安全性服务和转换。
  • 根据客户端发送正确的 HTTP 内容。例如,将 JSON 发送到 Web 浏览器,将 XML、ATOM 和 RSS 发送到其他客户端。

Rendering and Consuming RESTful Services 实现

JKHLE 使用 Rendering and Consuming RESTful Services 实现来解决消费者渠道和互联网服务域之间的交互,如图 9 所示。

图 9. JKHLE 使用 Rendering and Consuming RESTful Services 实现的位置

图 9. JKHLE 使用 Rendering and Consuming RESTful Services 实现的位置以下架构考虑因素与 Rendering and Consuming RESTful Services 创建有关:

  • 响应负载(比如 XML、JSON、 ATOM 和 RSS)
  • REST 界面分组治理
  • 安全性(包括 HTTPS 和 SPNEGO)

运行时考虑因素

以下产品可用于实现 Rendering and Consuming RESTful Services 创建:

  • WebSphere Application Server Feature Pack for Web 2.0
  • 通过 REST 公开基于 Java EE 的工件。
  • WebSphere sMash
  • 为创建 Web 2.0 应用提供了开发和运行时环境。
  • 提供与 Dojo widgets 和 Groovy 或 PHP 脚本的紧密 REST 集成。
  • WebSphere DataPower
  • 提供了 Web 2.0 应用,支持 REST-SOAP 和 JS 检查。

本节将介绍这些产品的作用,以及在 Rendering and Consuming RESTful Services 实现中使用这些产品的设计模式。

WebSphere Application Server Feature Pack for Web 2.0

Web 2.0 Feature Pack 扩展了 WebSphere Application Server 的功能,它具有以下组件:

  • Web 2.0 到 SOA 的连接性

该组件用于支持从 Ajax 客户端到 SOA 服务和其他 Java EE 资产的连接性。该组件通过 Web 提要将企业数据扩展到客户和合作伙伴。

  • Ajax 消息传输

该组件用于将 Ajax 客户端连接到即时更新数据,比如股票报价或即时通信。

  • Ajax 开发工具集

该组件是 WebSphere Application Server 基于开源 JavaScript™ 运行环境的最佳 Ajax 开发工具集。

Web 2.0 Feature Pack 可以在设计模式中使用,如图 10 所示。

图 10. 设计模式

图 10. 设计模式该设计模型支持将 Ajax 客户端和混搭应用程序连接到外部 Web 服务、内部 SOA 服务和其他 Java EE 资产。它通过 Web feeds 向客户和合作伙伴扩展企业数据。

WebSphere sMash

WebSphere sMash 支持开发人员使用集成运行环境和工具包中的 PHP 脚本、REST 和 Dojo 快速构建并灵活地执行基于 Web 2.0 的应用程序。WebSphere sMash 侧重于快速面市、易于开发和部署,以及惯例优先原则。

WebSphere sMash 提供了干净且简单的 REST 界面。每个 REST 都由一个事件处理程序文件定义,并具有映射到 REST 动词的独特函数。例如,REST 动词 POST 映射到事件方法 onCreate(), 并拥有名为 /resources/people 的示例 URL。

WebSphere sMash 提供了将响应数据到 XML、JSON 和 ATOM 的自动化转换。

WebSphere DataPower

WebSphere DataPower 是一个特殊的网络应用集合,它有助于集成、简化和加速 SOA,并加强安全性。 WebSphere DataPower 可以多种方式支持 Rendering and Consuming RESTful Services 实现:

RESTful 资源聚合:

  • 场景:跨多个服务实现的资源请求。这些调用的结果需要聚合或组合成一个复杂的报告。
  • 解决方案:WebSphere DataPower 充当定义聚合资源 URI 的中介。该中介将请求传送给供应商并聚合响应。

媒体类型转换:

  • 场景:现有供应商实施一种媒体类型,而客户端需要其他媒体类型。
  • 解决方案:WebSphere DataPower 充当在请求信息中转换 Accept 头部和在响应信息中转换 Content-Type 头部的中介。该解决方案扩展了线速转换。

REST / SOAP 仲裁:

  • 场景:供应商支持 REST,但是现有客户端需要 SOAP。
  • 解决方案:WebSphere DataPower 充当公开 SOAP 的中介。它将请求信息从 SOAP 转换到 REST,并将响应信息从 REST 转换到 SOAP。

版本仲裁:

  • 场景:消费者和供应商各自发展。目标是最小化供应商实现的数量。
  • 解决方案:WebSphere DataPower 充当代理多个资源版本的中介。资源请求转换为新版本。还将执行任何需要的标题和实体转换、改进或筛选。

服务仲裁质量:

  • 场景:消费者签订使用资源配额的合同。必须监控该使用合同,并且根据请求率和请求数量执行。
  • 解决方案:必要时,WebSphere DataPower 充当监控和执行服务限制质量、调速和请求形成的中介。

UI Composition and Communication 实现

JKHLE 使用 UI Composition and Communication 实现,以提供业务域的更多功能,如图 11 所示。

Figure xxx. Requires a heading

Figure xxx. Requires a heading

图 11  JKHLE 使用 UI Composition and Communication 实现的位置

以下架构考虑因素与 UI Composition and Communication 实现有关:

  • 无状态实体
  • 框架(比如 Dojo 和 JSF)
  • 容器(比如 portlets、iWdigets 和 iViews)
  • 治理
  • 安全性(包括 HTTPS 和单点登录)

运行时和工具考虑因素

因为有很多客户端软件和技术可供选择,所以 IBM 意识到它必须支持从异构到客户端 SOA 这一范围内的所有领域。为了实现该目的,IBM 制定了以下策略:

  • 支持通过标准进行 UI 聚合。

这包括 Web 标准(比如 JSR 53 和 JSR 127)、portlet 应用(比如 JSR 286 和 JSR 168)、混搭(比如 OpenAjax 和 iWidget)和丰富的台式机 / 设备(比如 Eclipse 和 iView)。

  • 通过产品交付开源聚合。

IBM 提供并宣传应用内容的技术。这包括客户端容器的 W3C 开源 / Web 标准和开源框架(比如 Web 浏览器和 Lotus® Expeditor),以及台式和移动应用的 Eclipse 和 SWT。

  • 支持通过中间件进行客户端 UI 集成。

IBM 完全支持用户集成、边缘集成和 SOA 层之间的集成。

IBM 端到端软件客户端平台策略如图 12 所示。

图 12. IBM 端到端软件客户端平台策略

图 12. IBM 端到端软件客户端平台策略

Dojo

Dojo 是一个用 JavaScript 编写的开源 DHTML 工具集。Dojo 支持将动态功能轻松地构建到 Web 页面中。Dojo 提供了许多功能,并且由 3 个主要层构成:Dojo Core、Dijit 和 DojoX。

Dojo 工具集是一个 JavaScript 工具集,它具有丰富的用户界面,用于开发 Ajax 应用。它在大多数现代客户端容器上都能很好地工作,并且占用资源少、性能高。IBM 支持 Dojo 工具集; 在 Dojo 工具集中创建的 Ajax 应用可以被 WebSphere Application Server 和 WebSphere Portal 使用。

可从以下网址下载 Dojo 工具集:

设计模式

WebSphere Portal 和 Dojo 可用于支持 UI Composition and Communication 实现,如图 13 中的设计模式所示。

图 13. 设计模式

图 13. 设计模式该设计模式描述了以下信息:

  • WebSphere Portal 支持支持 Ajax 的 portlet,可以使用 IBM Rational® Application Developer 或 Portlet Factory 生成这些 portlet。

此应用允许选择部分更新。

  • Dojo Dijit 用于呈现 portlet 内部的小部件。
  • portlet 的目的是调用 REST 式服务。

结束语

JKHL 实现了基于 Web 2.0 SOA 场景的 3 种实现的解决方案:

  • RESTful Service 创建实现
  • Rendering and Consuming RESTful Services 实现
  • UI Composition and Communication 实现

这些实现使 JKHLE 能够构建更好的旅行代理网站。该网站提供的快速架构可以交付新产品、使用新服务、消除业务孤立,以及以可消费形式在 Internet 上公开业务数据。