月度归档:2015年08月

Memcached、Redis OR Tair

一、前言

非关系型数据库(NoSQL = Not Only SQL)的产品非常多,常见的有Memcached、Redis、MongoDB等优秀开源项目,相关概念和资料网上也非常丰富,不再重复描述,本文主要 引入Memcached和Redis与淘宝开源Tair分布式存储进行对比测试,由于各自适用场景不同,且每个产品的可配置参数繁多,涉及缓存策略、分布 算法、序列化方式、数据压缩技术、通信方式、并发、超时等诸多方面因素,都会对测试结果产生影响,单纯的性能对比存在非常多的局限性和不合理性,所以不能 作为任何评估依据,仅供参考,加深对各自产品的理解。以下是一些基本认识:

1、尽管 Memcached 和 Redis 都标识为Distribute,但从Server端本身而言它们并不提供分布式的解决方案,需要Client端实现一定的分布算法将数据存储到各个节点, 从而实现分布式存储,两者都提供了Replication功能(Master-Slave)保障可靠性。

2、Tair 则本身包含 Config Server 和 Data Server 采用一致性哈希算法分布数据存储,由ConfigSever来管理所有数据节点,理论上服务器端节点的维护对前端应用不会产生任何影响,同时数据能按指定 复制到不同的DataServer保障可靠性,从Cluster角度来看属于一个整体Solution,组件图参照上一篇博文( http://www.cnblogs.com/lengfo/p/4171655.html )。

基于此,本文设定了实验环境都使用同一台机器进行 Memcached、Redis 和 Tair 的单Server部署测试。

二、前置条件

1、虚拟机环境(OS :CentOS6.5,CPU:2 Core,Memory:4G)

2、软件环境

 Sever Client
MemcachedMemcached 1.4.21Xmemcached 2.0.0
RedisRedis 2.8.19Jedis 2.8.5
TairTair 2.3Tair Client 2.3.1

3、服务器配置,单一服务器通过配置尽可能让资源分配一致(由于各个产品服务器端的配置相对复杂,不再单独列出,以下仅描述内存、连接等基本配置)

 IP_Port Memory_Size Max_Connection 备注
Memcached10.129.221.70:120001024MB2048
Redis10.129.221.70:63791gb(1000000000byte)10000(默认)
Tair Config Server10.129.221.70:5198
Tair Data Server10.129.221.70:51911024MB使用mdb存储引擎

三、用例场景,分别使用单线程和多线程进行测试

1、从数据库读取一组数据缓存(SET)到每个缓存服务器,其中对于每个Server的写入数据是完全一致的,不设置过期时间,进行如下测试。

1)单线程进行1次写入

2)单线程进行500次写入

3)单线程进行2000次写入

4)并行500个线程,每个线程进行1次写入

5)并行500个线程,每个线程进行5次写入

6)并行2000个线程,每个线程进行1次写入

2、分别从每个缓存服务器读取(GET)数据,其中对于每个Server的读取数据大小是完全一致的,进行如下测试。

1)单线程进行1次读取

2)单线程进行500次读取

3)单线程进行2000次读取

4)并行500个线程,每个线程进行1次读取

5)并行500个线程,每个线程进行5次读取

6)并行2000个线程,每个线程进行1次读取

四、单线程测试

1、缓存Model对象(OrderInfo)的定义参照tbOrder表(包括单据号、制单日期、商品、数量等字段)

2、单线程的读写操作对于代码的要求相对较低,不需要考虑Pool,主要代码如下:

1)Memcached单线程读写,使用二进制方式序列化,不启用压缩。

 1 public static void putItems2Memcache(List<OrderInfo> orders) throws Exception {
 2         MemcachedClient memcachedClient = null;
 3         try {
 4             MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("10.129.221.70:12000"));
 5             builder.setCommandFactory(new BinaryCommandFactory());
 6             memcachedClient = builder.build();
 7 
 8             for (OrderInfo order : orders) {
 9                 boolean isSuccess = memcachedClient.set("order_" + order.BillNumber, 0, order);
10                 if (!isSuccess) {
11                     System.out.println("put: order_" + order.BillNumber + "  " + isSuccess);
12                 }
13             }
14         } catch (Exception ex) {
15             ex.printStackTrace();
16         } finally {
17             memcachedClient.shutdown();
18         }
19     }
20 
21     public static void getItemsFromMemcache(List<String> billNumbers) throws Exception {
22         MemcachedClient memcachedClient = null;
23         try {
24             MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("10.129.221.70:12000"));
25             builder.setCommandFactory(new BinaryCommandFactory());
26             memcachedClient = builder.build();
27 
28             for (String billnumber : billNumbers) {
29                 OrderInfo result = memcachedClient.get(billnumber);
30 
31                 if (result == null) {
32                     System.out.println(" get failed : " + billnumber + " not exist ");
33                 }
34             }
35         } catch (Exception ex) {
36             ex.printStackTrace();
37         } finally {
38             memcachedClient.shutdown();
39         }
40     }

View Code

2)Redis单线程读写,由于Jedis Client 不支持对象的序列化,需要自行实现对象序列化(本文使用二进制方式)。

 1 public static void putItems2Redis(List<OrderInfo> orders) {
 2         Jedis jedis = new Jedis("10.129.221.70", 6379);
 3 
 4         try {
 5             jedis.connect();
 6 
 7             for (OrderInfo order : orders) {
 8                 String StatusCode = jedis.set(("order_" + order.BillNumber).getBytes(), SerializeUtil.serialize(order));
 9                 if (!StatusCode.equals("OK")) {
10                     System.out.println("put: order_" + order.BillNumber + "  " + StatusCode);
11                 }
12             }
13         } catch (Exception ex) {
14             ex.printStackTrace();
15         } finally {
16             jedis.close();
17         }
18     }
19 
20     public static void getItemsFromRedis(List<String> billNumbers) {
21         Jedis jedis = new Jedis("10.129.221.70", 6379);
22 
23         try {
24             jedis.connect();
25 
26             for (String billnumber : billNumbers) {
27                 byte[] result = jedis.get(billnumber.getBytes());
28                 if (result.length > 0) {
29                     OrderInfo order = (OrderInfo) SerializeUtil.unserialize(result);
30                     if (order == null) {
31                         System.out.println(" unserialize failed : " + billnumber);
32                     }
33                 } else {
34                     System.out.println(" get failed : " + billnumber + " not exist ");
35                 }
36             }
37         } catch (Exception ex) {
38             ex.printStackTrace();
39         } finally {
40             jedis.close();
41         }
42     }

序列化代码

 1 package common;
 2 
 3 import java.io.ByteArrayInputStream;
 4 import java.io.ByteArrayOutputStream;
 5 import java.io.ObjectInputStream;
 6 import java.io.ObjectOutputStream;
 7 
 8 public class SerializeUtil {
 9 
10     /**
11      * 序列化
12      * @param object
13      * @return
14      */
15     public static byte[] serialize(Object object) {
16         ObjectOutputStream oos = null;
17         ByteArrayOutputStream baos = null;
18 
19         try { 
20             baos = new ByteArrayOutputStream();
21             oos = new ObjectOutputStream(baos);
22             oos.writeObject(object);
23             byte[] bytes = baos.toByteArray();
24             return bytes;
25         } catch (Exception e) {
26             e.printStackTrace();
27         }
28         return null;
29     }
30 
31     /**
32      * 反序列化
33      * @param bytes
34      * @return
35      */
36     public static Object unserialize(byte[] bytes) {
37         ByteArrayInputStream bais = null;
38         try {
39             bais = new ByteArrayInputStream(bytes);
40             ObjectInputStream ois = new ObjectInputStream(bais);
41             return ois.readObject();
42         } catch (Exception e) {
43             e.printStackTrace();
44         }
45 
46         return null;
47     }
48 }

3)Tair单线程读写,使用Java序列化,默认压缩阀值为8192字节,但本文测试的每个写入项都不会超过这个阀值,所以不受影响。

 1 public static void putItems2Tair(List<OrderInfo> orders) {
 2         try {
 3             List<String> confServers = new ArrayList<String>();
 4             confServers.add("10.129.221.70:5198");
 5             //confServers.add("10.129.221.70:5200");
 6 
 7             DefaultTairManager tairManager = new DefaultTairManager();
 8             tairManager.setConfigServerList(confServers);
 9             tairManager.setGroupName("group_1");
10             tairManager.init();
11 
12             for (OrderInfo order : orders) {
13                 ResultCode result = tairManager.put(0, "order_" + order.BillNumber, order);
14                 if (!result.isSuccess()) {
15                     System.out.println("put: order_" + order.BillNumber + "  " + result.isSuccess() + " code:" + result.getCode());
16                 }
17             }
18         } catch (Exception ex) {
19             ex.printStackTrace();
20         }
21     }
22 
23     public static void getItemsFromTair(List<String> billNumbers) {
24         try {
25             List<String> confServers = new ArrayList<String>();
26             confServers.add("10.129.221.70:5198");
27             //confServers.add("10.129.221.70:5200");
28 
29             DefaultTairManager tairManager = new DefaultTairManager();
30             tairManager.setConfigServerList(confServers);
31             tairManager.setGroupName("group_1");
32             tairManager.init();
33 
34             for (String billnumber : billNumbers) {
35                 Result<DataEntry> result = tairManager.get(0, billnumber);
36                 if (result.isSuccess()) {
37                     DataEntry entry = result.getValue();
38                     if (entry == null) {
39                         System.out.println(" get failed : " + billnumber + " not exist ");
40                     }
41                 } else {
42                     System.out.println(result.getRc().getMessage());
43                 }
44             }
45         } catch (Exception ex) {
46             ex.printStackTrace();
47         }
48     }

3、测试结果,每项重复测试取平均值

五、多线程测试

1、除了多线程相关代码外的公共代码和单线程基本一致,多线程测试主要增加了Client部分代码对ConnectionPool、TimeOut相关设置,池策略、大小都会对性能产生很大影响,为了达到更高的性能,不同的使用场景下都需要有科学合理的测算。

2、主要测试代码

1)每个读写测试线程任务完成后统一调用公共Callback,在每批测试任务完成后记录消耗时间

 1 package common;
 2 
 3 public class ThreadCallback {
 4 
 5     public static int CompleteCounter = 0;
 6     public static int failedCounter = 0;
 7 
 8     public static synchronized void OnException() {
 9         failedCounter++;
10     }
11 
12     public static synchronized void OnComplete(String msg, int totalThreadCount, long startMili) {
13         CompleteCounter++;
14         if (CompleteCounter == totalThreadCount) {
15             long endMili = System.currentTimeMillis();
16             System.out.println("(总共" + totalThreadCount + "个线程 ) " + msg + "  ,总耗时为:" + (endMili - startMili) + "毫秒 ,发生异常线程数:" + failedCounter);
17             CompleteCounter = 0;
18             failedCounter = 0;
19         }
20     }
21 }

View Code

2)Memcached多线程读写,使用XMemcached客户端连接池,主要设置连接池大小ConnectionPoolSize=5,连接超时时间ConnectTimeout=2000ms,测试结果要求没有超时异常线程。

测试方法

 1         /*-------------------Memcached(多线程初始化)--------------------*/
 2         MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("192.168.31.191:12000"));
 3         builder.setCommandFactory(new BinaryCommandFactory());
 4         builder.setConnectionPoolSize(5);
 5         builder.setConnectTimeout(2000);
 6         MemcachedClient memcachedClient = builder.build();
 7         memcachedClient.setOpTimeout(2000);
 8 
 9         /*-------------------Memcached(多线程写入)--------------------*/
10         orders = OrderBusiness.loadOrders(5);
11         startMili = System.currentTimeMillis();
12         totalThreadCount = 500;
13         for (int i = 1; i <= totalThreadCount; i++) {
14             MemcachePutter putter = new MemcachePutter();
15             putter.OrderList = orders;
16             putter.Namesapce = i;
17             putter.startMili = startMili;
18             putter.TotalThreadCount = totalThreadCount;
19             putter.memcachedClient = memcachedClient;
20 
21             Thread th = new Thread(putter);
22             th.start();
23         }
24 
25                 //读取代码基本一致

线程任务类

 1 public class MemcachePutter implements Runnable {
 2     public List<OrderInfo> OrderList;
 3     public int Namesapce;
 4     public int TotalThreadCount;
 5     public long startMili;
 6     public MemcachedClient memcachedClient = null; // 线程安全的?
 7 
 8     @Override
 9     public void run() {
10         try {
11             for (OrderInfo order : OrderList) {
12                 boolean isSuccess = memcachedClient.set("order_" + order.BillNumber, 0, order);
13                  if (!isSuccess) {
14                 System.out.println("put: order_" + order.BillNumber + "  " + isSuccess);
15                 }
16             }
17         } catch (Exception ex) {
18             ex.printStackTrace();
19             ThreadCallback.OnException();
20         } finally {
21             ThreadCallback.OnComplete("Memcached 每个线程进行" + OrderList.size() + "次 [写入] ", TotalThreadCount, startMili);
22         }
23     }
24 }
25 
26 
27 
28 public class MemcacheGetter implements Runnable {
29 
30     public List<String> billnumbers;
31     public long startMili;
32     public int TotalThreadCount;
33     public MemcachedClient memcachedClient = null; // 线程安全的?
34 
35     @Override
36     public void run() {
37         try {
38             for (String billnumber : billnumbers) {
39                 OrderInfo result = memcachedClient.get(billnumber);
40                 if (result == null) {
41                     System.out.println(" get failed : " + billnumber + " not exist ");
42                 }
43             }
44         } catch (Exception ex) {
45             ex.printStackTrace();
46             ThreadCallback.OnException();
47         } finally {
48             ThreadCallback.OnComplete("Memcached 每个线程进行" + billnumbers.size() + "次 [读取] ", TotalThreadCount, startMili);
49         }
50     }
51 }

3)Redis多线程读写,使用Jedis客户端连接池,从源码可以看出依赖与Apache.Common.Pool2,主要设置连接池MaxTotal=5,连接超时时间Timeout=2000ms,测试结果要求没有超时异常线程。

测试方法

 1         /*-------------------Redis(多线程初始化)--------------------*/
 2         GenericObjectPoolConfig config = new GenericObjectPoolConfig();
 3         config.setMaxTotal(5);
 4         JedisPool jpool = new JedisPool(config, "192.168.31.191", 6379, 2000);
 5 
 6         /*-------------------Redis(多线程写入)--------------------*/
 7         totalThreadCount = 2000;
 8         orders = OrderBusiness.loadOrders(1);
 9         startMili = System.currentTimeMillis();
10         for (int i = 1; i <= totalThreadCount; i++) {
11             RedisPutter putter = new RedisPutter();
12             putter.OrderList = orders;
13             putter.Namesapce = i;
14             putter.startMili = startMili;
15             putter.TotalThreadCount = totalThreadCount;
16             putter.jpool = jpool;
17 
18             Thread th = new Thread(putter);
19             th.start();
20         }

线程任务类

 1 public class RedisPutter implements Runnable {
 2 
 3     public List<OrderInfo> OrderList;
 4     public int Namesapce;
 5     public int TotalThreadCount;
 6     public long startMili;
 7     public JedisPool jpool;
 8 
 9     @Override
10     public void run() {
11         Jedis jedis = jpool.getResource();
12 
13         try {
14             jedis.connect();
15 
16             for (OrderInfo order : OrderList) {
17                 String StatusCode = jedis.set(("order_" + order.BillNumber).getBytes(), SerializeUtil.serialize(order));
18                 if (!StatusCode.equals("OK")) {
19                     System.out.println("put: order_" + order.BillNumber + "  " + StatusCode);
20                 }
21             }
22         } catch (Exception ex) {
23             // ex.printStackTrace();
24             jpool.returnBrokenResource(jedis);
25             ThreadCallback.OnException();
26         } finally {
27             jpool.returnResource(jedis);
28             ThreadCallback.OnComplete("Redis 每个线程进行" + OrderList.size() + "次 [写入] ", TotalThreadCount, startMili);
29         }
30     }
31 }
32 
33 
34 
35 public class RedisGetter implements Runnable {
36     public List<String> billnumbers;
37     public long startMili;
38     public int TotalThreadCount;
39     public JedisPool jpool;
40 
41     @Override
42     public void run() {
43         Jedis jedis = jpool.getResource();
44 
45         try {
46             jedis.connect();
47             for (String billnumber : billnumbers) {
48                 byte[] result = jedis.get(billnumber.getBytes());
49                 if (result.length > 0) {
50                     OrderInfo order = (OrderInfo) SerializeUtil.unserialize(result);
51                     if (order == null) {
52                         System.out.println(" unserialize failed : " + billnumber);
53                     }
54                 } else {
55                     System.out.println(" get failed : " + billnumber + " not exist ");
56                 }
57             }
58         } catch (Exception ex) {
59             // ex.printStackTrace();
60             jpool.returnBrokenResource(jedis);
61             ThreadCallback.OnException();
62         } finally {
63             jpool.returnResource(jedis);
64             ThreadCallback.OnComplete("Redis 每个线程进行" + billnumbers.size() + "次 [读取] ", TotalThreadCount, startMili);
65         }
66     }
67 }

View Code

4)Tair多线程读写,使用官方Tair-Client,可设置参数MaxWaitThread主要指最大等待线程数,当超过这个数量的线程在 等待时,新的请求将直接返回超时,本文测试设置MaxWaitThread=100,连接超时时间Timeout=2000ms,测试结果要求没有超时异 常线程。

测试方法

 1      /*-------------------Tair(多线程初始化tairManager)--------------------*/
 2         List<String> confServers = new ArrayList<String>();
 3         confServers.add("192.168.31.191:5198");
 4         DefaultTairManager tairManager = new DefaultTairManager();
 5         tairManager.setConfigServerList(confServers);
 6         tairManager.setGroupName("group_1");
 7         tairManager.setMaxWaitThread(100);// 最大等待线程数,当超过这个数量的线程在等待时,新的请求将直接返回超时
 8         tairManager.setTimeout(2000);// 请求的超时时间,单位为毫秒
 9         tairManager.init();
10 
11         /*-------------------Tair(多线程写入)--------------------*/
12         orders = OrderBusiness.loadOrders(5);
13         startMili = System.currentTimeMillis();
14         totalThreadCount = 500;
15         for (int i = 1; i <= totalThreadCount; i++) {
16             TairPutter putter = new TairPutter();
17             putter.OrderList = orders;
18             putter.Namesapce = i;
19             putter.startMili = startMili;
20             putter.TotalThreadCount = totalThreadCount;
21             putter.tairManager = tairManager;
22 
23             Thread th = new Thread(putter);
24             th.start();
25         }
26      /*-------------------Tair(多线程读取)--------------------*/
27         //读取代码基本一致

线程任务类

 1 public class TairGetter implements Runnable {
 2     public List<String> billnumbers;
 3     public long startMili;
 4     public int TotalThreadCount;
 5     public DefaultTairManager tairManager;
 6 
 7     @Override
 8     public void run() {
 9         try {
10             for (String billnumber : billnumbers) {
11                 Result<DataEntry> result = tairManager.get(0, billnumber);
12                 if (result.isSuccess()) {
13                     DataEntry entry = result.getValue();
14                     if (entry == null) {
15                         System.out.println(" get failed : " + billnumber + " not exist ");
16                     }
17                 } else {
18                     System.out.println(result.getRc().getMessage());
19                 }
20             }
21         } catch (Exception ex) {
22             // ex.printStackTrace();
23             ThreadCallback.OnException();
24         } finally {
25             ThreadCallback.OnComplete("Tair 每个线程进行" + billnumbers.size() + "次 [读取] ", TotalThreadCount, startMili);
26         }
27     }
28 }
29 
30 
31 
32 public class TairPutter implements Runnable {
33 
34     public List<OrderInfo> OrderList;
35     public int Namesapce;
36     public int TotalThreadCount;
37     public long startMili;
38     public DefaultTairManager tairManager;
39 
40     @Override
41     public void run() {
42         try {
43             for (OrderInfo order : OrderList) {
44                 ResultCode result = tairManager.put(0, "order_" + order.BillNumber, order);
45                 if (!result.isSuccess()) {
46                     System.out.println("put: order_" + order.BillNumber + "  " + result.isSuccess() + " code:" + result.getCode());
47                 }
48             }
49         } catch (Exception ex) {
50             // ex.printStackTrace();
51             ThreadCallback.OnException();
52         } finally {
53             ThreadCallback.OnComplete("Tair 每个线程进行" + OrderList.size() + "次 [写入] ", TotalThreadCount, startMili);
54         }
55     }
56 }

3、测试结果,每项重复测试取平均值

六、Memcached、Redis、Tair 都非常优秀

Redis在单线程环境下的性能表现非常突出,但在并行环境下则没有很大的优势,是JedisPool或者CommonPool的性能瓶颈还是我测试代码的问题请麻烦告之,过程中修改setMaxTotal,setMaxIdle都没有太大的改观。

Tair由于需要在服务器端实现数据分布等相关算法,所以在测试对比中性能有所损耗应该也很好理解。

如之前所言,每个技术本身的原理、策略、适用场景各不相同,尽管以上测试方法已经考虑了很多影响因素,但任然可能存在不足之处,所以类似的对比缺乏合理 性,Tair还有2种存储引擎没有测试,而且以上都基于单机环境测试,在Cluster环境下可能也会有差别,所以结果仅供参考,不作任何评估依据。

 

来源:http://www.tuicool.com/articles/mmEz6zq

Dubbo与Zookeeper、SpringMVC整合和使用

互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,Dubbo是一个分布式服务框架,在这种情况下诞生的。现在核心业务抽取出来,作为独立的服务,使前端应用能更快速和稳定的响应。

第一:介绍Dubbo背景

大规模服务化之前,应用可能只是通过RMI或Hessian等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过F5等硬件进行负载均衡。

(1) 当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。

此时需要一个服务注册中心,动态的注册和发现服务,使服务的位置透明。

并通过在消费方获取服务提供方地址列表,实现软负载均衡和Failover,降低对F5硬件负载均衡器的依赖,也能减少部分成本。

(2) 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。

这时,需要自动画出应用间的依赖关系图,以帮助架构师理清理关系。

(3) 接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?

为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。

其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阀值,记录此时的访问量,再以此访问量乘以机器数反推总容量。

第二:Dubbo的简介

Dubbo是一个分布式服务框架,解决了上面的所面对的问题,Dubbo的架构如图所示:


节点角色说明:

Provider: 暴露服务的服务提供方。

Consumer: 调用远程服务的服务消费方。

Registry: 服务注册与发现的注册中心。

Monitor: 统计服务的调用次调和调用时间的监控中心。

Container: 服务运行容器。

调用关系说明:

0. 服务容器负责启动,加载,运行服务提供者。

1. 服务提供者在启动时,向注册中心注册自己提供的服务。

2. 服务消费者在启动时,向注册中心订阅自己所需的服务。

3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Dubbo提供了很多协议,Dubbo协议、RMI协议、Hessian协议,我们查看Dubbo源代码,有各种协议的实现,如图所示:


我们之前没用Dubbo之前时,大部分都使用Hessian来使用我们服务的暴露和调用,利用HessianProxyFactory调用远程接口。

上面是参考了Dubbo官方网介绍,接下来我们来介绍SpringMVC、Dubbo、Zookeeper整合使用。

第三:Dubbo与Zookeeper、SpringMVC整合使用  第一步:在Linux上安装Zookeeper

Zookeeper作为Dubbo服务的注册中心,Dubbo原先基于数据库的注册中心,没采用Zookeeper,Zookeeper一个分布式的服务框架,是树型的目录服务的数据存储,能做到集群管理数据 ,这里能很好的作为Dubbo服务的注册中心,Dubbo能与Zookeeper做到集群部署,当提供者出现断电等异常停机时,Zookeeper注册中心能自动删除提供者信息,当提供者重启时,能自动恢复注册数据,以及订阅请求。我们先在linux上安装Zookeeper,我们安装最简单的单点,集群比较麻烦。

(1)下载Zookeeper-3.4.6.tar.gz  地址http://www.apache.org/dist/zookeeper/

(2) 我们放到Linux下的一个文件夹,然后解压:

#tar zxvf zookeeper-3.4.6.tar.gz

(3)然后在对应的zookeeper-3.4.6/conf 下有一个文件zoo_sample.cfg的这个文件里面配置了监听客户端连接的端口等一些信息,Zookeeper 在启动时会找zoo.cfg这个文件作为默认配置文件,所以我们复制一个名称为zoo.cfg的文件,如图所示:

我们查看一下这个文件的里面的一些配置信息,如图所示:

说明:

clientPort:监听客户端连接的端口。

tickTime:基本事件单元,以毫秒为单位。它用来控制心跳和超时,默认情况下最小的会话超时时间为两倍的 tickTime。

我们可以对配置文件的端口等或者进行高级配置和集群配置例如:maxClientCnxns:限制连接到 ZooKeeper 的客户端的数量等

(4)启动Zookeeper 的服务,如图所示:

到这边Zookeeper的安装和配置完成

第二步:配置dubbo-admin的管理页面,方便我们管理页面

(1)下载dubbo-admin-2.4.1.war包,在Linux的tomcat部署,先把dubbo-admin-2.4.1放在tomcat的webapps/ROOT下,然后进行解压:

#jar -xvf dubbo-admin-2.4.1.war

(2)然后到webapps/ROOT/WEB-INF下,有一个dubbo.properties文件,里面指向Zookeeper ,使用的是Zookeeper 的注册中心,如图所示:

(3)然后启动tomcat服务,用户名和密码:root,并访问服务,显示登陆页面,说明dubbo-admin部署成功,如图所示:

 第三步:SpringMVC与Dubbo的整合,这边使用的Maven的管理项目

    第一:我们先开发服务注册的,就是提供服务,项目结构如图所示:

(1)test-maven-api项目加入了一个服务接口,代码如下:

public interface TestRegistryService {
   public String hello(String name);
}

(2)test-maven-console在pom.xml加入Dubbo和Zookeeper的jar包、引用test-maven-api的jar包,代码如下:

      <dependency>
  		<groupId>cn.test</groupId>
  		<artifactId>test-maven-api</artifactId>
  		<version>0.0.1-SNAPSHOT</version>
  	</dependency>
  
      <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.5.3</version>
        </dependency>
        
         <dependency>
            <groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.4.6</version>
        </dependency>
  
      <dependency>
     	 <groupId>com.github.sgroschupf</groupId>
		 <artifactId>zkclient</artifactId>
		 <version>0.1</version>
      </dependency>

(3)test-maven-console实现具体的服务,代码如下:

  @Service("testRegistryService")
public class TestRegistryServiceImpl implements TestRegistryService {
	public String hello(String name) {	
		return "hello"+name;
	}
}

(4)我们服务以及实现好了,这时要暴露服务,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:tx="http://www.springframework.org/schema/tx"
	<span style="color:#cc0000;">xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"</span>
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
	http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
	<span style="color:#990000;">http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd</span>
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"
	default-lazy-init="false" >
   <!-- 提供方应用名称信息,这个相当于起一个名字,我们dubbo管理页面比较清晰是哪个应用暴露出来的 -->
   <dubbo:application name="dubbo_provider"></dubbo:application>
   <!-- 使用zookeeper注册中心暴露服务地址 -->  
   <dubbo:registry address="zookeeper://127.0.0.1:2181" check="false" subscribe="false" register=""></dubbo:registry>
  <!-- 要暴露的服务接口 -->  
  <dubbo:service interface="cn.test.dubbo.registry.service.TestRegistryService" ref="testRegistryService" />  	
</beans>

说明:

dubbo:registry 标签一些属性的说明:

1)register是否向此注册中心注册服务,如果设为false,将只订阅,不注册。

2)check注册中心不存在时,是否报错。

3)subscribe是否向此注册中心订阅服务,如果设为false,将只注册,不订阅。

4)timeout注册中心请求超时时间(毫秒)。

5)address可以Zookeeper集群配置,地址可以多个以逗号隔开等。

dubbo:service标签的一些属性说明:

1)interface服务接口的路径

2)ref引用对应的实现类的Bean的ID

3)registry向指定注册中心注册,在多个注册中心时使用,值为<dubbo:registry>的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A

4)register 默认true ,该协议的服务是否注册到注册中心。

(5)启动项目,然后我们在Dubbo管理页面上显示,已经暴露的服务,但显示还没有消费者,因为我们还没实现消费者服务,如图所示:

第二:我们在开发服务消费者,就是调用服务,我们在新建一个新的消费者项目结构如图所示:

(1)test-maven-server-console的pom.xml引入Dubbo和Zookeeper的jar包、test-maven-api的jar包,因为引入test-maven-api的jar包,我们在项目中调用像在本地调用一样。代码如下:

 

      <dependency>
  		<groupId>cn.test</groupId>
  		<artifactId>test-maven-api</artifactId>
  		<version>0.0.1-SNAPSHOT</version>
  	</dependency>
  
      <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.5.3</version>
        </dependency>
        
         <dependency>
            <groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.4.6</version>
        </dependency>
  
      <dependency>
     	 <groupId>com.github.sgroschupf</groupId>
		 <artifactId>zkclient</artifactId>
		 <version>0.1</version>
      </dependency>

(2)test-maven-server-console项目的具体实现,代码如下:

@Controller
public class IndexController {
	
	@Autowired
	private TestRegistryService testRegistryService;
	
	@RequestMapping("/hello")
	public String index(Model model){
	     String name=testRegistryService.hello("zz");
	     System.out.println("xx=="+name);
		return "";
	}

}

(3)我们要引用的地址,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:tx="http://www.springframework.org/schema/tx"
	<span style="background-color: rgb(255, 255, 255);"><span style="color:#990000;">xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"</span></span>
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
	http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
	<span style="color:#990000;">http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd</span>
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"
	default-lazy-init="false" >

   <dubbo:application name="dubbo_consumer"></dubbo:application>
   <!-- 使用zookeeper注册中心暴露服务地址 -->  
   <dubbo:registry address="zookeeper://192.168.74.129:2181" check="false"></dubbo:registry> 
     <!-- 要引用的服务 -->  
   <dubbo:reference interface="cn.test.dubbo.registry.service.TestRegistryService" id="testRegistryService"></dubbo:reference>
</beans>

说明:

dubbo:reference 的一些属性的说明:

1)interface调用的服务接口

2)check 启动时检查提供者是否存在,true报错,false忽略

3)registry 从指定注册中心注册获取服务列表,在多个注册中心时使用,值为<dubbo:registry>的id属性,多个注册中心ID用逗号分隔

4)loadbalance 负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮循,最少活跃调用

 

(4)项目启动,Dubbo管理页面,能看到消费者,如图所示:

(5)然后访问消费者项目,Controller层能像调用本地一样调用服务的具体实现,如图所示:

Dubbo提供了多种容错方案,包括负载均衡这些,如图所示:

 

 

参考资料:

     http://dubbo.io/Home-zh.htm

 

HTTPS的七个误解

来源:http://www.ruanyifeng.com/blog/2011/02/seven_myths_about_https.html

开发网页的时候,往往需要观察HTTP通信。

我使用的工具主要有两个,在Firefox中是Firebug,在IE中是Fiddler。但是,一直听别人说,付费软件HttpWatch是这方面最好的工具。

前几天,HttpWatch的官方网志刊登了一篇好文章,澄清了一些HTTPS协议容易产生误解的地方。学习之后,我增长了不少网页加密通信的知识。

我觉得这篇文章很实用,值得留作参考,就翻译了出来。


误解七:HTTPS无法缓存

许多人以为,出于安全考虑,浏览器不会在本地保存HTTPS缓存。实际上,只要在HTTP头中使用特定命令,HTTPS是可以缓存的。

微软的IE项目经理Eric Lawrence写道:

"说来也许令人震惊,只要HTTP头允许这样做,所有版本的IE都缓存HTTPS内容。比如,如果头命令是Cache-Control: max-age=600,那么这个网页就将被IE缓存10分钟。IE的缓存策略,与是否使用HTTPS协议无关。(其他浏览器在这方面的行为不一致,取决 于你使用的版本,所以这里不加以讨论。)"

Firefox默认只在内存中缓存HTTPS。但是,只要头命令中有Cache-Control: Public,缓存就会被写到硬盘上。下面的图片显示,Firefox的硬盘缓存中有HTTPS内容,头命令正是Cache-Control:Public。

误解六:SSL证书很贵

如果你在网上搜一下,就会发现很多便宜的SSL证书,大概10美元一年,这和一个.com域名的年费差不多。而且事实上,还能找到免费的SSL证书。

在效力上,便宜的证书当然会比大机构颁发的证书差一点,但是几乎所有的主流浏览器都接受这些证书。

误解五:HTTPS站点必须有独享的IP地址

由于IPv4将要分配完毕,所以很多人关心这个问题。每个IP地址只能安装一张SSL证书,这是毫无疑问的。但是,如果你使用子域名通配符SSL证 书(wildcard SSL certificate,价格大约是每年125美元),就能在一个IP地址上部署多个HTTPS子域名。比 如,https://www.httpwatch.com和https://store.httpwatch.com,就共享同一个IP地址。

另外,UCC(统一通信证书,Unified Communications Certificate)支持一张证书同时匹配多个站点,可以是完全不同的域名。SNI(服 务器名称指示,Server Name Indication)允许一个IP地址上多个域名安装多张证书。服务器端,Apache和Nginx支持该技术,IIS不支持;客户端,IE 7+、Firefox 2.0+、Chrome 6+、Safari 2.1+和Opera 8.0+支持。

误解四:转移服务器时要购买新证书

部署SSL证书,需要这样几步:

  1. 在你的服务器上,生成一个CSR文件(SSL证书请求文件,SSL Certificate Signing Request)。

2. 使用CSR文件,购买SSL证书。

3. 安装SSL证书。

这些步骤都经过精心设计,保证传输的安全,防止有人截取或非法获得证书。结果就是,你在第二步得到的证书不能用在另一台服务器上。如果你需要这样做,就必须以其他格式输出证书。

比如,IIS的做法是生成一个可以转移的.pfx文件,并加以密码保护。

将这个文件传入其他服务器,将可以继续使用原来的SSL证书了。

误解三:HTTPS太慢

使用HTTPS不会使你的网站变得更快(实际上有可能,请看下文),但是有一些技巧可以大大减少额外开销。

首先,只要压缩文本内容,就会降低解码耗用的CPU资源。不过,对于当代CPU来说,这点开销不值一提。

其次,建立HTTPS连接,要求额外的TCP往返,因此会新增一些发送和接收的字节。但是,从下图可以看到,新增的字节是很少的。

第一次打开网页的时候,HTTPS协议会比HTTP协议慢一点,这是因为读取和验证SSL证书的时间。下面是一张HTTP网页打开时间的瀑布图。

同一张网页使用HTTPS协议之后,打开时间变长了。

建立连接的部分,大约慢了10%。但是,一旦有效的HTTPS连接建立起来,再刷新网页,两种协议几乎没有区别。先是HTTP协议的刷新表现:

然后是HTTPS协议:

某些用户可能发现,HTTPS比HTTP更快一点。这会发生在一些大公司的内部局域网,因为通常情况下,公司的网关会截取并分析所有的网络通信。但 是,当它遇到HTTPS连接时,它就只能直接放行,因为HTTPS无法被解读。正是因为少了这个解读的过程,所以HTTPS变得比较快。

误解二:有了HTTPS,Cookie和查询字符串就安全了

虽然无法直接从HTTPS数据中读取Cookie和查询字符串,但是你仍然需要使它们的值变得难以预测。

比如,曾经有一家英国银行,直接使用顺序排列的数值表示session id:

黑客可以先注册一个账户,找到这个cookie,看到这个值的表示方法。然后,改动cookie,从而劫持其他人的session id。至于查询字符串,也可以通过类似方式泄漏。

误解一:只有注册登录页,才需要HTTPS

这种想法很普遍。人们觉得,HTTPS可以保护用户的密码,此外就不需要了。Firefox浏览器新插件Firesheep,证明了这种想法是错的。我们可以看到,在Twitter和Facebook上,劫持其他人的session是非常容易的。

咖啡馆的免费WiFi,就是一个很理想的劫持环境,因为两个原因:

  1. 这种WiFi通常不会加密,所以很容易监控所有流量。

2. WiFi通常使用NAT进行外网和内网的地址转换,所有内网客户端都共享一个外网地址。这意味着,被劫持的session,看上去很像来自原来的登录者。

以Twitter为例,它的登录页使用了HTTPS,但是登录以后,其他页面就变成了HTTP。这时,它的cookie里的session值就暴露了。

也就是说,这些cookie是在HTTPS环境下建立的,但是却在HTTP环境下传输。如果有人劫持到这些cookie,那他就能以你的身份在Twitter上发言了。

Hue安装配置实践

来源:http://shiyanjun.cn/archives/1002.html

Hue是一个开源的Apache Hadoop UI系统,最早是由Cloudera Desktop演化而来,由Cloudera贡献给开源社区,它是基于Python Web框架Django实现的。通过使用Hue我们可以在浏览器端的Web控制台上与Hadoop集群进行交互来分析处理数据,例如操作HDFS上的数 据,运行MapReduce Job等等。很早以前就听说过Hue的便利与强大,一直没能亲自尝试使用,下面先通过官网给出的特性,通过翻译原文简单了解一下Hue所支持的功能特性集 合:

  • 默认基于轻量级sqlite数据库管理会话数据,用户认证和授权,可以自定义为MySQL、Postgresql,以及Oracle
  • 基于文件浏览器(File Browser)访问HDFS
  • 基于Hive编辑器来开发和运行Hive查询
  • 支持基于Solr进行搜索的应用,并提供可视化的数据视图,以及仪表板(Dashboard)
  • 支持基于Impala的应用进行交互式查询
  • 支持Spark编辑器和仪表板(Dashboard)
  • 支持Pig编辑器,并能够提交脚本任务
  • 支持Oozie编辑器,可以通过仪表板提交和监控Workflow、Coordinator和Bundle
  • 支持HBase浏览器,能够可视化数据、查询数据、修改HBase表
  • 支持Metastore浏览器,可以访问Hive的元数据,以及HCatalog
  • 支持Job浏览器,能够访问MapReduce Job(MR1/MR2-YARN)
  • 支持Job设计器,能够创建MapReduce/Streaming/Java Job
  • 支持Sqoop 2编辑器和仪表板(Dashboard)
  • 支持ZooKeeper浏览器和编辑器
  • 支持MySql、PostGresql、Sqlite和Oracle数据库查询编辑器

下面,我们通过实际安装来验证Hue的一些功能。

环境准备

这里,我所基于的基本环境及其配置情况,如下所示:

  • CentOS-6.6 (Final)
  • JDK-1.7.0_25
  • Maven-3.2.1
  • Git-1.7.1
  • Hue-3.7.0(branch-3.7.1)
  • Hadoop-2.2.0
  • Hive-0.14
  • Python-2.6.6

基于上面的软件工具,要保证正确安装和配置。需要说明的是,我们通过Hue来执行Hive查询,需要启动HiveServer2服务:
cd /usr/local/hive
bin/hiveserver2 &

否则通过Hue Web控制无法执行Hive查询。
安装配置

我新建了一个hadoop用户,以hadoop用户,首先使用yum工具来安装Hue相关的依赖软件:
sudo yum install krb5-devel cyrus-sasl-gssapi cyrus-sasl-deve libxml2-devel libxslt-devel mysql mysql-devel openldap-devel python-devel python-simplejson sqlite-devel

然后,执行如下命令进行Hue软件包的下载构建:
cd /usr/local/
sudo git clone https://github.com/cloudera/hue.git branch-3.7.1
sudo chown -R hadoop:hadoop branch-3.7.1/
cd branch-3.7.1/
make apps

上述过程如果没有任何问题,我们就已经安装好Hue。Hue的配置文件为/usr/local/branch-3.7.1/desktop /conf/pseudo-distributed.ini,默认的配置文件不能正常运行Hue,所以需要修改其中的内容,与我们对应的Hadoop集群 配置相对应。该配置文件根据整合不同的软件,将配置分成多个段,每个段下面还有子段,便于管理配置,如下所示(省略子段名称):

  • desktop
  • libsaml
  • libopenid
  • liboauth
  • librdbms
  • hadoop
  • filebrowser
  • liboozie
  • oozie
  • beeswax
  • impala
  • pig
  • sqoop
  • proxy
  • hbase
  • search
  • indexer
  • jobsub
  • jobbrowser
  • zookeeper
  • spark
  • useradmin
  • libsentry

我们很容易根据需要来配置自己需要的内容。我们修改配置文件的情况,如下表所示:

Hue配置段Hue配置项Hue配置值说明
desktopdefault_hdfs_superuserhadoopHDFS管理用户
desktophttp_host10.10.4.125Hue Web Server所在主机/IP
desktophttp_port8000Hue Web Server服务端口
desktopserver_userhadoop运行Hue Web Server的进程用户
desktopserver_grouphadoop运行Hue Web Server的进程用户组
desktopdefault_useryanjunHue管理员
hadoop/hdfs_clustersfs_defaultfshdfs://hadoop6:8020对应core-site.xml配置项fs.defaultFS
hadoop/hdfs_clustershadoop_conf_dir/usr/local/hadoop/etc/hadoopHadoop配置文件目录
hadoop/yarn_clustersresourcemanager_hosthadoop6对应yarn-site.xml配置项yarn.resourcemanager.hostname
hadoop/yarn_clustersresourcemanager_port8032ResourceManager服务端口号
hadoop/yarn_clustersresourcemanager_api_urlhttp://hadoop6:8088对应于yarn-site.xml配置项yarn.resourcemanager.webapp.address
hadoop/yarn_clustersproxy_api_urlhttp://hadoop6:8888对应yarn-site.xml配置项yarn.web-proxy.address
hadoop/yarn_clustershistory_server_api_urlhttp://hadoo6:19888对应mapred-site.xml配置项mapreduce.jobhistory.webapp.address
beeswaxhive_server_host10.10.4.125Hive所在节点主机名/IP
beeswaxhive_server_port10000HiveServer2服务端口号
beeswaxhive_conf_dir/usr/local/hive/confHive配置文件目录

上面主要配置了Hadoop集群相关的内容,以及Hive(beeswax段配置的是Hive,通过HIveServer2与Hive交互)。
最后,启动Hue服务,执行如下命令:

1cd /usr/local/branch-3.7.1/
2build/env/bin/supervisor &

Hue功能验证

我们主要通过在Hue Web控制台上执行Hive查询,所以需要准备Hive相关的表和数据。

  • Hive准备

我们首先在Hive中创建一个数据库(如果没有权限则授权):
GRANT ALL TO USER hadoop;
CREATE DATABASE user_db;

这里,hadoop用户是Hive的管理用户,可以将全部权限赋给该用户。
创建示例表,建表DDL如下所示:
CREATE TABLE user_db.daily_user_info (
device_type int,
version string,
channel string,
udid string)
PARTITIONED BY (
stat_date string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat';

准备的数据文件格式,示例如下所示:
0     3.2.1     C-gbnpk     b01b8178b86cebb9fddc035bb238876d
0     3.0.7     A-wanglouko     e2b7a3d8713d51c0215c3a4affacbc95
0     1.2.7     H-follower     766e7b2d2eedba2996498605fa03ed33
0     1.2.7     A-shiry     d2924e24d9dbc887c3bea5a1682204d9
0     1.5.1     Z-wammer     f880af48ba2567de0f3f9a6bb70fa962
0     1.2.7     H-clouda     aa051d9e2accbae74004d761ec747110
0     2.2.13     H-clouda     02a32fd61c60dd2c5d9ed8a826c53be4
0     2.5.9     B-ywsy     04cc447ad65dcea5a131d5a993268edf

各个字段之间使用TAB分隔,每个字段含义与上面表user_db.daily_user_info的字段对应,然后我们将测试数据加载到示例表的各个分区之中:
LOAD DATA LOCAL INPATH '/home/hadoop/u2014-12-05.log' OVERWRITE INTO TABLE user_db.daily_user_info PARTITION (stat_date='2014-12-05');
LOAD DATA LOCAL INPATH '/home/hadoop/u2014-12-06.log' OVERWRITE INTO TABLE user_db.daily_user_info PARTITION (stat_date='2014-12-06');
LOAD DATA LOCAL INPATH '/home/hadoop/u2014-12-07.log' OVERWRITE INTO TABLE user_db.daily_user_info PARTITION (stat_date='2014-12-07');
LOAD DATA LOCAL INPATH '/home/hadoop/u2014-12-08.log' OVERWRITE INTO TABLE user_db.daily_user_info PARTITION (stat_date='2014-12-08');
LOAD DATA LOCAL INPATH '/home/hadoop/u2014-12-09.log' OVERWRITE INTO TABLE user_db.daily_user_info PARTITION (stat_date='2014-12-09');
LOAD DATA LOCAL INPATH '/home/hadoop/u2014-12-10.log' OVERWRITE INTO TABLE user_db.daily_user_info PARTITION (stat_date='2014-12-10');
LOAD DATA LOCAL INPATH '/home/hadoop/u2014-12-11.log' OVERWRITE INTO TABLE user_db.daily_user_info PARTITION (stat_date='2014-12-11');

可以通过Hive CLI接口登录,查看表中数据:
SELECT COUNT(1) FROM daily_user_info;

我这里有241709545条记录作为测试数据。

  • Hue登录页面

Hue服务启动成功后,可以直接通过浏览器打开连接http://10.10.4.125:8000/,就可以登录。第一次打开,需要输入默认用户和口令,然后就可以登录进去,如下图所示:
hue-login
首次登录,选择使用的用户即为Hue管理员用户,权限很大,可以添加用户并管理用户及其用户组的操作权限。

  • Hue用户首页

登录成功以后,进入Hue Web控制台首页,如下图所示:
hue-index
登录成功后,首先会执行一些基本环境的配置检查工作,它与我们实际修改配置时都指定了哪些应用有关系。

  • Hive查询编辑器页面

用户登录成功后,选择Query Editors下面的Hive菜单项,如图所示:
hue-hive-query
在提交查询的时候,由于该查询执行时间较长,可以等待查询执行,最后结果显示在的现房的Results标签页上,也可以在执行过程中查看Hive后台执行情况。

  • Job浏览器页面

通过Job浏览器(Job Browser)页面http://10.10.4.125:8000/jobbrowser/,可以查看运行在Hadoop集群上各种状态的Job,包括Succeeded、Running、Failed、Killed这4种状态,如图所示:
hue-job-browser
如果想要看到Job具体执行状态信息,需要正确配置并启动Hadoop集群的JobHistoryServer和WebAppProxyServer服务,可以通过Web页面看到相关数据,我们的示例,如图所示:
hue-job-browser-detail
如果想看某个Job对应的MapTask或者ReduceTask执行情况,可以点击对应链接进去,和通过Hadoop YARN的Job Web管理界面类似,监控起来非常方便。

  • 用户管理和授权认证

以授权管理员用户登录成功后,可以通过点击右上角用户(我这里是yanjun),下拉列表中有“Manage Users”菜单项,在这里面可以创建新用户,并指定访问权限,如下图所示:
hue-manage-users
上面,我创建了几个用户,并指定用户所属的组(Groups,支持组管理)。实际上,我们可以将不同的Hue应用设置为不同的组,然后将新建的用户分配到 该相关组,通过这种方式可以控制用户访问Hue应用的权限。上面创建并分配权限的用户可以通过设置的用户名和口令登录Hue Web管理系统,与各种Hadoop相关的应用(不仅仅限于此,如MySQL、Spark等)进行交互。

总结

通过上面的了解,以及安装配置过程所遇到的问题,做一个总结:

  • 如果基于CentOS环境安装配置Hue,可能相对复杂一点,不一定能够很容易的配置成功。我开始基于CentOS-5.11(Final)进行 配置,没有配置成功,可能是使用的Hue的版本太高(branch-3.0和branch-3.7.1我都试过),或者可能是CentOS依赖的一些软件 包无法安装等问题导致的。建议最好使用较新版本的CentOS,我这里使用的是CentOS-6.6 (Final),Hue使用的branch-3.7.1源码编译,并且Python版本需要2.6+。
  • 使用Hue,我们可能会对用户管理及其权限分配也很感兴趣,所以数据存储,可以根据需要使用我们熟悉的其他关系数据库,如MySQL等,并做好备 份,以防使用Hue应用的相关用户数据丢失,造成无法访问Hadoop集群等问题。需要修改Hue的配置文件,将默认存储方式sqlite3改成我们熟悉 的关系数据库,目前支持MySQL、Postgresql,以及Oracle。
  • 如果有必要,可能结合Hadoop集群底层的访问控制机制,如Kerberos,或者Hadoop SLA,配合Hue的用户管理和授权认证功能,更好地进行访问权限的约束和控制。
  • 根据前面我们提到的Hue特性,我们可以根据自己实际的应用场景,来选择不同的Hue应用,通过这种插件式的配置来启动应用,通过Hue与其交互,如Oozie、Pig、Spark、HBase等等。
  • 使用更低版本的Hive,如0.12,可能在验证过程中会遇到问题,可以根据Hive的版本来选择兼容版本的Hue来安装配置。
  • 由于本次安装配置实践,并没有使用Cloudera发行的CDH软件包,如果使用CDH可能会更加顺利一些。

参考链接

自由软件 ─ FreeMind思维导图

来源:互联网

课程大纲

壹、 前言

一、 何谓心智图(思维导图)

心智图,又称脑图、思维导图、灵感触发图、概念地图或思维地图,是一种利用图像式思考辅助工具来表达思维的工具。

心智图由英国的托尼·布詹(Tony Buzan)于20世纪70年代提出的一种辅助思考工具。心智图通过在平面上的一个主题出发画出相关联的对象,像一个心脏及其周边的血管图,故称为「心智图」。由于这种表现方式和人思考时的空间想象比单纯的文本更加接近,已经越来越为大家用于创造性思维过程中。心智图在头脑风暴、创意、文档规划和记录笔记等场合中广为应用。Mindjet公司的MindManager是专业的心智图工具,微软的Visio 2002及以上版本提供了画心智图的功能。

二、 何谓FreeMind

FreeMind是一套非常容易上手的简易绘图软件,由Java语言编写并以一个高效率的树形图软件为出发点。这套软件和商业软件MindManager有着意曲同工之妙,但两者的差别在于Mindjet所推出的MindManager是一款付费软件,而FreeMind则是符合GPL的自由软件,以及FreeMind比MindManage多了一些特色,如实时按键缩放资料夹和连结的跟进等。

贰、 FreeMind安装

FreeMind 在安装时必须有Java 1.4 或更新的版本来支援。所以利用计算机→设定→控制台→新增或移除软件,来查看是否已经安装。

如果没有安装,可以到下列网址下载JRE - Java Runtime Environment 的程序(http://java.sun.com/j2se/1.5.0/download.jsp) 。

透过 FreeMind 的 Wiki 网站(http://freemind.sourceforge.net/wiki)下载软件。目前最新的版本是0.8.0版,它已经具有中文化的界面。

下载后直接点选二下,即进行安装动作。

叁、 功能表架构

肆、 FreeMind 基本的操作

一、 开启FreeMind

二、 新增或删除节点

1. 建立新的「子」节点,按Insert。

2. 建立「平行」的节点,在节点之前按 Enter,之后按Shift + Enter。

3. 删除节点,按delete。

4. 一般常用鼠标右键来编辑。

三、 编辑节点文字

四、 节点格式

1. 外观 ─ 底线或气泡框。

底线式气泡式

2. 字型

3. 颜色

4. 线条(颜色、样式、长度)

五、 样式

六、 使用云图重点标示

如果要取消云状,再做一次就可以了!

七、 超连结

1. 档案

2. 网址

八、 加入图示

至于「移除最后Icon」或「移除所有Icon」是以单一节点来处理,换言之,如果某一节点上有许多个Icon,可以最后加入的Icon被移除,或者是全部的Icon被移除。

九、 加入图形化连结(节点关连)

如果要对所产生的关连线进行移除、变更颜色、箭头类型或方向等等,可在关连在线方按鼠标右键即可进行编辑。

十、 汇出

1. 网页

将心智图汇出为具树状结构的网页:

l 汇出HTML

l 汇成XHTML格式(Javascript version)

2. 图形

三种基本图形格式:

l SVG

l PNG

l JPG

伍、 FreeMind进阶操作

一、 汇入档案结构

选择功能表的档案→汇入→汇入档案结构,然后选定计算机中的目录,FreeMind即将档案的结构汇入,并做好超连结,可以以此开启相关的档案。

二、 汇入我的最爱

要将浏览器上的「我的最爱」变为freemind,可用档案→汇入→汇入我的最爱方式来产生,唯一要注意的是要找到「我的最爱」资料的存放位置,一般来说会在下列档案位置里:

C:\Documents and Settings\<user>\Favorites

三、 汇入其他的FreeMind图

在freemind 中,可以利用此种方式将各个freemind的作品合并为一个freemind档案。

陆、 在线浏览FreeMind

之前所介绍的汇出网页方式,只是将freemind转换为类似树状的文字结构,如果希望呈现freemind 的图形,又兼具类似freemind分支缩放的动作,可使用freemind官方网站所提供的java applet 功能来做到。

首先,要到官方网站下载 freemind-browser 的压缩档(目前最新版本是0.7.1),里面包含二个档案网页档 freemindbrowser.html 及java applet档 freemindbrowser.jar。

解开压缩后,将你要搭配的freemind档(例如freemindbrowser.mm)放在同一目录(也可以指定目录),然后利用一般网页编辑器打开freemindbrowser.html ,将搭配的freemind档名编辑进入即可。例如:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">

<html>

<!-- This launcher works fine with Explorer (with Javascript or without) as

well as with Mozilla on Windows -->

<head>

<title>Mind Map</title>

<!-- ^ Put the name of your mind map here -->

</head>

<body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">

<APPLET CODE="freemind.main.FreeMindApplet.class"

ARCHIVE="freemindbrowser.jar" WIDTH="100%" HEIGHT="100%">

<PARAM NAME="type" VALUE="application/x-java-applet;version=1.4">

<PARAM NAME="scriptable" VALUE="false">

<PARAM NAME="modes" VALUE="freemind.modes.browsemode.BrowseMode">

<PARAM NAME="browsemode_initial_map"

VALUE="./freemind.mm"> ←搭配的freemind档名

<!-- ^ Put the path to your map here -->

<param NAME="initial_mode" VALUE="Browse">

<param NAME="selection_method" VALUE="selection_method_direct">

</applet>

</body>

</html>

柒、 心智图在教学上的应用

(资料来源:http://www.yourblog.org/Data/20046/90069.html)

FreeMind软件教学

FreeMind 是一款免费的自由软件,这个软件是利用Java程序所写成的,让你用图形的方式轻松的绘制出心智图,来帮助你整理思绪!

行前概念
l 自由软件的概念

l 父节点与子节点概念

安装FreeMind
1. 下载安装J2RE 点我下载

2. 下载安装FreeMind 点我下载

使用FreeMind
1. 新增档案

2. 点一下根节点,修改根节点名称

3. 新增子结点

(1) 于父节点上按右键

(2) 点选新增子节点,并输入节点名称

(3) 右上方工具列可修改字号

4. 改变节点的层级和位置

(1) 按住该节点

(2) 直接拖曳此节点即可

5. 云状的使用:云能将所属的子节点框住,增加辨析度

(1) 将游标移至某节点上方,节点变成灰色代表已选取

(2) 点选上方工具列的云状按钮

6. 节点的格式

(1) 在该节点上按右键选择格式

(2) 改变节点外观:底线或气泡框

(3) 改变节点颜色(节点文字颜色)与节点背景颜色

(4) 改变云的颜色

(5) 改变连结线颜色

7. 新增连结

(1) 于节点上按右键选择新增

(2) 选择加入连结(文字结(可使用URL)),连结至网页

(3) 选择加入连结(档案),连结至某档案

(4) 选择加入图档,插入小图片

(5) 将2个节点加上另外的连结线

8. 标注小图示

(1) 选取节点

(2) 点选左方工具列的小图示即可

(3) 要删除图示,点选左上方的按钮即可

9. 存档与汇出档案

(1) 存档:档案à另存新档

(2) 汇出:选择JPEG档

10. FreeMind影音教学网 http://ds.k12.edu.tw/1000315492/FreeMind/list.htm