Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

hbase性能调优

$
0
0

一、服务端调优

 1、参数配置

   1)、hbase.regionserver.handler.count:该设置决定了处理RPC的线程数量,默认值是10,通常可以调大,比如:150,当请求内容很大(上MB,比如大的put、使用缓存的scans)的时候,如果该值设置过大则会占用过多的内存,导致频繁的GC,或者出现OutOfMemory,因此该值不是越大越好。

 

  2)、hbase.hregion.max.filesize 配置region大小,0.94.12版本默认是10G,region的大小与集群支持的总数据量有关系,如果总数据量小,则单个region太大,不利于并行的数据处理,如果集群需支持的总数据量比较大,region太小,则会导致region的个数过多,导致region的管理等成本过高,如果一个RS配置的磁盘总量为3T*12=36T数据量,数据复制3份,则一台RS服务器可以存储10T的数据,如果每个region最大为10G,则最多1000个region,如此看,94.12的这个默认配置还是比较合适的,不过如果要自己管理split,则应该调大该值,并且在建表时规划好region数量和rowkey设计,进行region预建,做到一定时间内,每个region的数据大小在一定的数据量之下,当发现有大的region,或者需要对整个表进行region扩充时再进行split操作,一般提供在线服务的hbase集群均会弃用hbase的自动split,转而自己管理split。

 

  3)、hbase.hregion.majorcompaction:配置major合并的间隔时间,默认为1天,可设置为0,禁止自动的major合并,可手动或者通过脚本定期进行major合并,有两种compact:minor和major,minor通常会把数个小的相邻的storeFile合并成一个大的storeFile,minor不会删除标示为删除的数据和过期的数据,major会删除需删除的数据,major合并之后,一个store只有一个storeFile文件,会对store的所有数据进行重写,有较大的性能消耗。

 

  4)、hbase.hstore.compactionThreshold:HStore的storeFile数量>= compactionThreshold配置的值,则可能会进行compact,默认值为3,可以调大,比如设置为6,在定期的major compact中进行剩下文件的合并。

  5)、 hbase.hstore.blockingStoreFiles:HStore的storeFile的文件数大于配置值,则在flush memstore前先进行split或者compact,除非超过hbase.hstore.blockingWaitTime配置的时间,默认为7,可调大,比如:100,避免memstore不及时flush,当写入量大时,触发memstore的block,从而阻塞写操作。

 

  6)、hbase.regionserver.global.memstore.upperLimit:默认值0.4,RS所有memstore占用内存在总内存中的upper比例,当达到该值,则会从整个RS中找出最需要flush的region进行flush,直到总内存比例降至该数限制以下,并且在降至限制比例以下前将阻塞所有的写memstore的操作,在以写为主的集群中,可以调大该配置项,不建议太大,因为block cache和memstore cache的总大小不会超过0.8,而且不建议这两个cache的大小总和达到或者接近0.8,避免OOM,在偏向写的业务时,可配置为0.45,memstore.lowerLimit保持0.35不变,在偏向读的业务中,可调低为0.35,同时memstore.lowerLimit调低为0.3,或者再向下0.05个点,不能太低,除非只有很小的写入操作,如果是兼顾读写,则采用默认值即可。

 

  7)、hbase.regionserver.global.memstore.lowerLimit:默认值0.35,RS的所有memstore占用内存在总内存中的lower比例,当达到该值,则会从整个RS中找出最需要flush的region进行flush,配置时需结合memstore.upperLimit和block cache的配置。

 

  8)、file.block.cache.size:RS的block cache的内存大小限制,默认值0.25,在偏向读的业务中,可以适当调大该值,具体配置时需试hbase集群服务的业务特征,结合memstore的内存占比进行综合考虑。

 

  9)、hbase.hregion.memstore.flush.size:默认值128M,单位字节,超过将被flush到hdfs,该值比较适中,一般不需要调整。

 

  10)、hbase.hregion.memstore.block.multiplier:默认值2,如果memstore的内存大小已经超过了hbase.hregion.memstore.flush.size的2倍,则会阻塞memstore的写操作,直到降至该值以下,为避免发生阻塞,最好调大该值,比如:4,不可太大,如果太大,则会增大导致整个RS的memstore内存超过memstore.upperLimit限制的可能性,进而增大阻塞整个RS的写的几率。如果region发生了阻塞会导致大量的线程被阻塞在到该region上,从而其它region的线程数会下降,影响整体的RS服务能力,例如:

开始阻塞:


 解开阻塞:

 从10分11秒开始阻塞到10分20秒解开,总耗时9秒,在这9秒中无法写入,并且这期间可能会占用大量的RS handler线程,用于其它region或者操作的线程数会逐渐减少,从而影响到整体的性能,也可以通过异步写,并限制写的速度,避免出现阻塞。

 

  11)、hfile.block.index.cacheonwrite:在index写入的时候允许put无根(non-root)的多级索引块到block cache里,默认是false,设置为true,或许读性能更好,但是是否有副作用还需调查。

 

  12)、io.storefile.bloom.cacheonwrite:默认为false,需调查其作用。

 

  13)、hbase.regionserver.regionSplitLimit:控制最大的region数量,超过则不可以进行split操作,默认是Integer.MAX,可设置为1,禁止自动的split,通过人工,或者写脚本在集群空闲时执行。如果不禁止自动的split,则当region大小超过hbase.hregion.max.filesize时会触发split操作(具体的split有一定的策略,不仅仅通过该参数控制,前期的split会考虑region数据量和memstore大小),每次flush或者compact之后,regionserver都会检查是否需要Split,split会先下线老region再上线split后的region,该过程会很快,但是会存在两个问题:1、老region下线后,新region上线前client访问会失败,在重试过程中会成功但是如果是提供实时服务的系统则响应时长会增加;2、split后的compact是一个比较耗资源的动作。

 

  14)、Jvm调整:

       a、内存大小:master默认为1G,可增加到2G,regionserver默认1G,可调大到10G,或者更大,zk并不耗资源,可以不用调整;

   b、垃圾回收:待研究。

 

2、其它调优

  1)、列族、rowkey要尽量短,每个cell值均会存储一次列族名称和rowkey,甚至列名称也要尽量短,以下截图是表test2的数据和存入hdfs后的文件内容:

 

 由上图可见:短的列族名称、rowkey、列名称对最终的文件内容大小影响很大。

 

  2)、RS的region数量:一般每个RegionServer不要过1000,过多的region会导致产生较多的小文件,从而导致更多的compact,当有大量的超过5G的region并且RS总region数达到1000时,应该考虑扩容。

 

  3)、建表时:

                   a、如果不需要多版本,则应设置version=1;

                   b、 开启lzo或者snappy压缩,压缩会消耗一定的CPU,但是,磁盘IO和网络IO将获得极大的改善,大致可以压缩4~5倍;

                  c、合理的设计rowkey,在设计rowkey时需充分的理解现有业务并合理预见未来业务,不合理的rowkey设计将导致极差的hbase操作性能;

                 d、合理的规划数据量,进行预分区,避免在表使用过程中的不断split,并把数据的读写分散到不同的RS,充分的发挥集群的作用;

                 e、列族名称尽量短,比如:“f”,并且尽量只有一个列族;

                 f、视场景开启bloomfilter,优化读性能。

 

二、Client端调优

  1、hbase.client.write.buffer:写缓存大小,默认为2M,推荐设置为6M,单位是字节,当然不是越大越好,如果太大,则占用的内存太多;

 

  2、hbase.client.scanner.caching:scan缓存,默认为1,太小,可根据具体的业务特征进行配置,原则上不可太大,避免占用过多的client和rs的内存,一般最大几百,如果一条数据太大,则应该设置一个较小的值,通常是设置业务需求的一次查询的数据条数,比如:业务特点决定了一次最多100条,则可以设置为100

 

  3、设置合理的超时时间和重试次数,具体的内容会在后续的blog中详细讲解。

 

  4、client应用读写分离

    读和写分离,位于不同的tomcat实例,数据先写入redis队列,再异步写入hbase,如果写失败再回存redis队列,先读redis缓存的数据(如果有缓存,需要注意这里的redis缓存不是redis队列),如果没有读到再读hbase。

    当hbase集群不可用,或者某个RS不可用时,因为HBase的重试次数和超时时间均比较大(为保证正常的业务访问,不可能调整到比较小的值,如果一个RS挂了,一次读或者写,经过若干重试和超时可能会持续几十秒,或者几分钟),所以一次操作可能会持续很长时间,导致tomcat线程被一个请求长时间占用,tomcat的线程数有限,会被快速占完,导致没有空余线程做其它操作,读写分离后,写由于采用先写redis队列,再异步写hbase,因此不会出现tomcat线程被占满的问题, 应用还可以提供写服务,如果是充值等业务,则不会损失收入,并且读服务出现tomcat线程被占满的时间也会变长一些,如果运维介入及时,则读服务影响也比较有限。

 

  5、如果把org.apache.hadoop.hbase.client.HBaseAdmin配置为spring的bean,则需配置为懒加载,避免在启动时链接hbase的Master失败导致启动失败,从而无法进行一些降级操作。

 

  6、Scan查询编程优化:

     1)、调整caching;

     2)、如果是类似全表扫描这种查询,或者定期的任务,则可以设置scan的setCacheBlocks为false,避免无用缓存;

    3)、关闭scanner,避免浪费客户端和服务器的内存;

    4)、限定扫描范围:指定列簇或者指定要查询的列;

  5)、如果只查询rowkey时,则使用KeyOnlyFilter可大量减少网络消耗;

 

作为hbase依赖的状态协调者ZK和数据的存储则HDFS,也需要调优:

 

ZK调优:

  1、zookeeper.session.timeout:默认值3分钟,不可配置太短,避免session超时,hbase停止服务,线上生产环境由于配置为1分钟,出现过2次该原因导致的hbase停止服务,也不可配置太长,如果太长,当rs挂掉,zk不能快速知道,从而导致master不能及时对region进行迁移。

 

  2、zookeeper数量:至少5个节点。给每个zookeeper 1G左右的内存,最好有独立的磁盘。 (独立磁盘可以确保zookeeper不受影响).如果集群负载很重,不要把Zookeeper和RegionServer运行在同一台机器上面。就像DataNodes 和 TaskTrackers一样,只有超过半数的zk存在才会提供服务,比如:共5台,则最多只运行挂2台,配置4台与3台一样,最多只运行挂1台。

 

  3、hbase.zookeeper.property.maxClientCnxns:zk的最大连接数,默认为300,可配置上千

 

hdf调优:

  1、dfs.name.dir: namenode的数据存放地址,可以配置多个,位于不同的磁盘并配置一个NFS远程文件系统,这样nn的数据可以有多个备份

  2、dfs.data.dir:dn数据存放地址,每个磁盘配置一个路径,这样可以大大提高并行读写的能力

  3、dfs.namenode.handler.count:nn节点RPC的处理线程数,默认为10,需提高,比如:60

  4、dfs.datanode.handler.count:dn节点RPC的处理线程数,默认为3,需提高,比如:20

  5、dfs.datanode.max.xcievers:dn同时处理文件的上限,默认为256,需提高,比如:8192

  6、dfs.block.size:dn数据块的大小,默认为64M,如果存储的文件均是比较大的文件则可以考虑调大,比如,在使用hbase时,可以设置为128M,注意单位是字节

  7、dfs.balance.bandwidthPerSec:在通过start-balancer.sh做负载均衡时控制传输文件的速度,默认为1M/s,可配置为几十M/s,比如:20M/s

  8、dfs.datanode.du.reserved:每块磁盘保留的空余空间,应预留一些给非hdfs文件使用,默认值为0

  9、dfs.datanode.failed.volumes.tolerated:在启动时会导致dn挂掉的坏磁盘数量,默认为0,即有一个磁盘坏了,就挂掉dn,可以不调整。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




activeMQ 推送之mqtt客户端

$
0
0

使用activeMQ进行android推送

activeMQ下载地址:http://activemq.apache.org/download.html

下载后是一个压缩包:apache-activemq-5.9.0-bin.zip

启动方式:

解压缩,进入apache-activemq-5.9.0-bin\apache-activemq-5.9.0\bin,双击activemq.bat,即可启动activeMQ服务

 启动之后:

 android客户端推送采用mqtt(paho-mqtt-client-1.0.1.jar),依赖包见附件

 

但是为了测试,我写了一个swing图形界面,充当手机客户端,依赖的jar包仍然是paho-mqtt-client-1.0.1.jar.界面如下:

使用方法:点击 [启动]按钮,开始接收推送消息
 对应的主类是:MqttSwing,用于接收推送消息.

 

我还写了一个发送推送消息的swing图形界面,充当推送后管系统,界面如下:

使用方法:点击[连接]按钮,才可以发送推送消息
 对应的主类:PusherApp,用于发送推送消息.

核心代码介绍如下.

客户端连接activeMQ,建立连接(只有建立连接,才能接收到推送消息)

方法名:connect,做了两件事:(1)建立连接;(2)订阅主题(topic)

 

/***
	 * 客户端和activeMQ服务器建立连接
	 * @param BROKER_URL
	 * @param clientId : 用于标识客户端,相当于ios中的device token
	 * @param TOPIC
	 * @param isCleanSession :false--可以接受离线消息;
	 * @return 是否启动成功 
	 */
	private boolean connect(String BROKER_URL,String clientId,String TOPIC,boolean isCleanSession){
		try {
			ComponentUtil.appendResult(resultTextPane, "connect time:"+TimeHWUtil.getCurrentMiniuteSecond(), true);
            mqttClient = new MqttClient(BROKER_URL, clientId, new MemoryPersistence());
            MqttConnectOptions options= new MqttConnectOptions();
            options.setCleanSession(isCleanSession);//mqtt receive offline message
            ComponentUtil.appendResult(resultTextPane, "isCleanSession:"+isCleanSession, true);
            options.setKeepAliveInterval(30);
            //推送回调类,在此类中处理消息,用于消息监听
            mqttClient.setCallback(new MyCallBack(MqttSwing.this));
            boolean isSuccess=false;
            try {
				mqttClient.connect(options);//CLIENT ID CAN NOT BE SAME
				isSuccess=true;
			} catch (Exception e) {
				if(isPrintException){
					e.printStackTrace();
				}
			}
            if(!isSuccess){
            	String message="连接失败,请检查client id是否重复了 或者activeMQ是否启动";
            	ComponentUtil.appendResult(resultTextPane, message, true);
            	GUIUtil23.warningDialog(message);
            	return false;
            }else{
            //Subscribe to topics 
	            mqttClient.subscribe(new String[]{TOPIC,clientId});
	            System.out.println("topic:"+TOPIC+",  "+(clientId));
	            ComponentUtil.appendResult(resultTextPane, "TOPIC:"+TOPIC+",  "+(clientId), true);
            }

        } catch (MqttException e) {
        	if(isPrintException){
            e.printStackTrace();}
            GUIUtil23.errorDialog(e.getMessage());
            return false;
        }
		return true;
	}

 

 

推送消息到来时的回调类:MyCallBack

 

package com.mqtt.hw.callback;

import org.apache.commons.lang.StringEscapeUtils;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;

import com.mqtt.hw.MqttSwing;
import com.time.util.TimeHWUtil;

public class MyCallBack implements MqttCallback {
	private MqttSwing mqttSwing;
	
	
	
	public MyCallBack(MqttSwing mqttSwing) {
		super();
		this.mqttSwing = mqttSwing;
	}

	@Override
	public void connectionLost(Throwable cause) {
		
	}

	@Override
	public void messageArrived(MqttTopic topic, MqttMessage message)
			throws Exception {
		System.out.println("messageArrived...."+TimeHWUtil.getCurrentMiniuteSecond());
		String messageStr=StringEscapeUtils.unescapeHtml(new String(message.getPayload()));
		System.out.println("message:"+messageStr);
		this.mqttSwing.receiveMessage(messageStr);
		//使窗口处于激活状态

	}

	@Override
	public void deliveryComplete(MqttDeliveryToken token) {
		
	}

}

 

 

推送者与activeMQ建立连接:

 

/**
	 * 初始化connection和session
	 * 
	 * @throws Exception
	 */
	private void init(/* String mqIp,boolean transacted */) throws Exception {
		if (!DialogUtil.verifyTFEmpty(serverIpTextField, "服务器ip")) {
			return;
		}
		String transactedStr = transactedTextField.getText();
		boolean transacted = false;
		if (ValueWidget.isNullOrEmpty(transactedStr)) {
			transacted = false;
		} else {
			transacted = Boolean.parseBoolean(transactedStr);
		}
		String message = "transacted:" + transacted;
		ComponentUtil.appendResult(resultTextArea, message, true);
		System.out.println(message);
		String brokerUrl = String.format(BROKER_URL,
				serverIpTextField.getText());
		// 创建链接工厂
		TopicConnectionFactory factory = new ActiveMQConnectionFactory(
				ActiveMQConnection.DEFAULT_USER,
				ActiveMQConnection.DEFAULT_PASSWORD, brokerUrl);
		ComponentUtil.appendResult(resultTextArea, "url:" + brokerUrl, true);
		// 通过工厂创建一个连接
		
		connection = factory.createTopicConnection();
		// 启动连接
		connection.start();
		ComponentUtil.appendResult(resultTextArea, "启动connection 成功", true);
		// 创建一个session会话 transacted
		session = connection.createTopicSession(
				transacted /* Boolean.FALSE */, Session.AUTO_ACKNOWLEDGE);

	}

 

 

项目源代码见附件 mqtt_swing.zip

手机android客户端(测试推送)见附件 android-mqtt-push-master.zip

也可以从  https://github.com/tokudu/AndroidPushNotificationsDemo

 

  下载

详细配置参阅附件 mqtt推送详解.zip

 





已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



java线程取消方式

$
0
0
JAVA任务取消方式一般有2种
第一种设置某个取消标志,任务定期查看该标志,这种方式存在任务取消并不能保证任务立即取消,更糟糕的是有可能任务永远不会结束
第二种是利用的中断机制,JVM并不能保证阻塞方法检测到中断的速度,但是实际中还是非常快的。
实例一(使用取消标志,会发现取消的任务还执行中,只有执行完再次检测标志任务才取消)

任务不断获取下一个素数,在每次获取素数利用sleep 保证获取素数时间大约10s
package org.thread;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

public class PrimeGenerator extends Thread {

	private final List<BigInteger> primes=new ArrayList<BigInteger>();
	private volatile boolean cancelled;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		BigInteger p=BigInteger.ONE;
		while(!cancelled){
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			p=p.nextProbablePrime();
			synchronized (this) {
				primes.add(p);
			}
		}
	}
	public void cancell(){cancelled=true;};
	public synchronized List<BigInteger> get(){
		return new ArrayList<BigInteger>(primes);
	}

}

测试程序中在启动获取素数的线程后,1s后取消获取素数线程,结果可以看出,线程真正结束是在9s后,结论利用取消标志不能保证线程立即结束或结束。
package org.thread;

public class InterruptTest {

	public static void main(String[] args) {
		PrimeGenerator t=new PrimeGenerator();
//		PrimeGenerator1 t=new PrimeGenerator1();
		t.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t.cancell();
		long start=System.currentTimeMillis();
		long end=0;
		while(t.isAlive()){
			 end=System.currentTimeMillis();
		}
		System.out.println(end-start);
	}

}

实例 二(使用中断取消,会发现取消的任务很快就结束)

任务不断获取下一个素数,在每次获取素数利用sleep 保证获取素数时间大约10s
package org.thread;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

public class PrimeGenerator1 extends Thread{

	private final List<BigInteger> primes=new ArrayList<BigInteger>();
	@Override
	public void run() {
		// TODO Auto-generated method stub
		BigInteger p=BigInteger.ONE;
		while(!Thread.currentThread().isInterrupted()){
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				Thread.currentThread().interrupt();
				e.printStackTrace();
			}
			p=p.nextProbablePrime();
			synchronized (this) {
				primes.add(p);
			}
		}
	}
	public void cancell(){interrupt();};
	public synchronized List<BigInteger> get(){
		return new ArrayList<BigInteger>(primes);
	}

}

测试程序中在启动获取素数的线程后,1s后取消获取素数线程,结果可以看出,线程可以立即结束。
package org.thread;

public class InterruptTest {

	public static void main(String[] args) {
//		PrimeGenerator t=new PrimeGenerator();
		PrimeGenerator1 t=new PrimeGenerator1();
		t.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t.cancell();
		long start=System.currentTimeMillis();
		long end=0;
		while(t.isAlive()){
			 end=System.currentTimeMillis();
		}
		System.out.println(end-start);
	}

}


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Selenium2.41.0—获取动态资源

$
0
0

编写不易,转载请注明(http://shihlei.iteye.com/blog/2067716)!

一概述

    获取动态资源,可以使用HtmlUnit,但是其对JS的支持还是不够完善。相对与HtmlUnit还有一种驱动浏览器的下载还原工具Selenium。可以打开浏览器,获取网页,下载解析,支持dom,js,解析效果更好,但是打开浏览器速度方面有一定损失。个人实验,禁用CSS,图片下载,速度还尚可。

    Selenium也是自动化测试工具,支持驱动不同的浏览器,Firefox,IE,Chrome等,也包含HtmlUnit提供的驱动实现。

    本文描述Selenium驱动Firefox,请求响应,设置cookies,驱动JS等方法,用他登录某主流weibo还是不错的。

 

二 版本

 

<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>2.41.0</version></dependency>

 

三 典型应用

1)打开Google,搜索baidu

可以看到Firefox从打开,填写表单,到提交打开新页面过程。

 

/**
 * 打开google搜索百度
 * 
 * @param queryStr
 */
public static void demo() {
	String url = "http://www.google.com.hk";

	WebDriver webDriver = new FirefoxDriver();
	// 打开google
	webDriver.get(url);

	// 使用Selenium的dom模型获取form
	WebElement webElement = webDriver.findElement(By.name("q"));
	webElement.sendKeys("baidu");
	webElement.submit();

	// 通过判断 title 内容等待搜索页面加载完毕,Timeout 设置10秒

	(new WebDriverWait(webDriver, 100)).until(new ExpectedCondition<Boolean>() {
		public Boolean apply(WebDriver d) {
			return d.getTitle().toLowerCase().indexOf("baidu") != -1;
		}
	});

	String responseBody = webDriver.getPageSource();
	System.out.println("Response : " + responseBody);

	// 关闭浏览器
	webDriver.close();
}

 

2)获取动态网页

与下面的请求响应一样,打开页面等待加载完毕即可,JS填充页面,AJAX都OK。

 

四 样例

1)请求响应

 

public static void main(String[] args) throws Exception {
	String url = "http://www.google.com";
	WebDriver webDriver = new FirefoxDriver();
	// 打开google
	webDriver.get(url);
        String responseBody = webDriver.getPageSource();  
        System.out.println("Response : " + responseBody);  
	webDriver.quit();
}

 

(2)配置不加载资源

 

/**
 * 获得不加载 css,图片,flash的浏览器
 * @return
 */
public WebDriver getNoResouceWebDriver(){
	FirefoxProfile firefoxProfile = new FirefoxProfile();
	// 去掉css
	firefoxProfile.setPreference("permissions.default.stylesheet", 2);
	// 去掉图片
	firefoxProfile.setPreference("permissions.default.image", 2);
	// 去掉flash
	firefoxProfile.setPreference("dom.ipc.plugins.enabled.libflashplayer.so", false);
	return new FirefoxDriver(firefoxProfile);
}

 

(3)配置Firefox路径

启动报找不到Firefox的时候可以使用:System.setProperty("webdriver.firefox.bin", firefoxPath)

 

(4)Cookies

1)设置Cookies

 

public void setCookies(WebDriver,webDriver,Map<String, String> cookies) {
	if (cookies != null && cookies.size() > 0) {
		for (Entry<String, String> c : cookies.entrySet()) {
			Cookie cookie = new Cookie(c.getKey(), c.getValue());
			webDriver.manage().addCookie(cookie);

			System.out.println("Set Cookies : " + c.getKey() + " | " + c.getValue());
		}
	}
}

 

2)获取响应Cookies

 

public Map<String,String> getCookies(WebDriver webDriver){
	Set<Cookie> cookies = webDriver.manage().getCookies();
	Map<String, String> responseCookies = new HashMap<String,String>();
	for (Cookie c : cookies) {
		responseCookies.put(c.getName(), c.getValue());
	}
	return responseCookies;
}

 

3)清理Cookies

 

/**
 * 清除所有cookie
 */
public void clearCookies(WebDriver webDriver) {
	webDriver.manage().deleteAllCookies();
}

 

(5)驱动JS

Selenium 的Dom对不可见的Element(html有但是CSS属性为不显示等)找不到,这时候使用JS操作和提交是个不错的选择:

 

public void doWeb(WebDriver webDriver) {
	StringBuilder js = new StringBuilder();
	js.append("document.getElementsByName('username')[1].value='").append(WeiboAccount.USERNAME)
			.append("';");
	js.append("document.getElementsByName('password')[1].value='").append(WeiboAccount.PASSWORD)
			.append("';");
	js.append("document.getElementsByClassName('W_btn_g')[1].click();");
	((JavascriptExecutor) webDriver).executeScript(js.toString());

	(new WebDriverWait(webDriver, 100)).until(new ExpectedCondition<Boolean>() {
		public Boolean apply(WebDriver d) {
			return d.getTitle().indexOf("我的首页") != -1;
		}
	});
}

 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



IP统计发现数十万小米用户用相同地址注册

$
0
0
小米论坛用户数据库泄露,涉及800多万用户,泄露的数据包括了用户名、哈希密码(MD5+salt)、电子邮件、注册IP地址等。小米已证实了此事,发表声明称受影响的是2012年8月前注册的论坛账号,2012年8月后注册的用户不受影响。它将安全漏洞归罪于第三方开源程序(也就是公开源代码的Discuz!),声称“在创业初期,我们的论坛及依附论坛产生的账号体系都使用了第三方开源程序。2012年8月,基于安全考虑,旧论坛账号体系不再使用,小米将所有服务(包括小米云服务、米币等)切换到全新的账号安全体系,采用业界最新安全实践方案,对所有存储数据均进行了最严格的安全加密。”有开发者根据泄露的数据库统计了用户的注册IP地址,发现大量用户用相同IP地址注册,有几个人或几十人几百人使用相同IP註冊不算奇怪,但小米论坛是动辄几十万:其中58.68.235.85和58.68.235.84各有超过27万用户使用,218.87.5.249和115.175.27.131都有超过10万用户,前两个都是北京的IP地址,都指向小米。有人怀疑这是小米服务器的反向代理的IP,更多人怀疑是“水军”。






易富贤:应立即停止计划生育政策以缓解就业压力

$
0
0

易富贤:应立即停止计划生育政策以缓解就业压力

2014年03月25日13:45 | 中国发展门户网 www.chinagate.cn |


中国网/中国发展门户网3月25日电(记者焦梦)当前中国同时面临“就业难”和“用工荒”并存的问题, 一方面2014年全国高校毕业生总数将达到727万人,比被称为“史上最难就业季”的2013年再增加28万人,创下历史新高;另一方面,春节后“用工荒”浪潮持续,许多制造和服务业企业招聘不到所需数量的员工。

那么造成这种两难局面的原因是什么,如何避免“就业难”和“用工荒”的进一步蔓延?美国威斯康星大学学者易富贤在接受记者专访时表示,这种结构性失业与计划生育政策密切相关,他认为应立即停止计划生育政策以缓解就业压力。

他认为,“一个正常的社会,有人口才有消费,有消费才有需求,有需求才有生产,有生产才有服务,才能提供就业机会,如果人口减少,消费就会减少导致需求减少,那么就业机会就会减少。”

根据易富贤的计算,每两个人口的消费约能提供一个就业机会。美国3.1亿人口提供 1.6亿个就业机会,日本1.27亿人口提供6700多万就业岗位,巴西2亿人口可以提供1亿就业机会,而中国80年代以来的计划生育少出生一亿多人口,这意味着中国减少一亿多消费者,而中国13亿人口目前只能提供6.5亿个就业机会,但是却拥有8亿劳动力。

他认为,国内市场不够,使得中国经济必然针对国际市场,因此中国经济呈现高度外向性特征,只有通过出口才能保障国内就业。而印度虽然经济状况落后,但是就业情况比中国好得多,原因是印度仅靠内需市场就可以解决就业,而中国靠内需市场根本不可能解决中国的就业。

他认为中国的“就业难”和“用工荒”是结构性问题,一方面年轻劳动力持续减少,中国20-39岁劳动力从2003年后开始下降;另一方面中老年劳动力供应富足,但是年轻人能够从事的行业,老人不一定能够从事,因此许多中老年劳动者还是找不到工作。

易富贤认为更严重的是,总劳动力数量一旦下降,经济就会衰退,企业问题就会更加严峻。日本自1995年起15-64岁劳动力数量开始下降,总失业率和青年失业率开始急剧提高;德国也是从1998年开始15-64岁劳动力数量呈现下降趋势,总失业率和青年失业率进而开始上升。他因此推断,中国在2014年15-64岁劳动力数量开始下降, 将导致总失业率上升,尤其是青年失业率上升。

“当务之急是尽快废除计划生育政策,生育更多的未来的消费者,”他呼吁。



有意思的加密通信

$
0
0

互联网就是个公开演讲的广场。意思是任何人说任何话,其实别人都是能听见的。这个认为是互联网的天性了,那么在这个前提下如果我和我一个朋友说点机密的事情,同时不想让别人偷听到,这个就成了一个有点挑战性的技术问题,也就是今天我们要讨论的加密通信。

mim

对称加密

几千年来,我们人类都在用一种“对称性加密“的形式来进行秘密信息的传递,”对称“指的是加密用一个密码,那解密是用的还是这个密码。但是“对称性加密”为啥不能用在互联网上的加密通信中呢?说说这个过程,我有一个文件,用一定的加密方式来进行加密(比如用一些文件压缩程序),然后可以把加密后的这个包,传递给我的朋友,到目前为止一切都好。但是接下来的问题是,我朋友如何才能解密我给他的文件呢,显然我必须安全的把我加密时输入的密码安全的传递给他,这样才能完成这个加密通信过程。所以这就有了一个鸡和蛋的问题。最终我可能就不得不借助互联网以外的工具,例如打电话,来把密码给我朋友了。所以,对称性加密是不适合用于互联网上的加密通信的。

dui_chen

非对称加密的原理

那我们就可以想一下,是不是有一种“非对称加密”的方法能解决鸡和蛋的问题呢?恭喜你,答对了!非对称加密用一句话来描述就是,加密是用一个密码 key,而解密则会用另外一个密码 key。安装好相应地程序之后,任何人都可以在自己的计算机上来生产这一对密码。其中用于加密的这个叫公钥,是可以公开给别人的,所以叫”密码“这个不严密,还是英文好,就叫 key。另外一个是绝对自己要保密好的,叫”私钥“,这个是用来解密的,真的是”密码“。

这两个 key 是一对,意思是他们在数学角度是有着联系的,但是如果想要从公钥计算出私钥也是不可能的。加密通信的过程是这样的,收信人,比如说我,先生成这一对 key,然后我想跟我的朋友秘密通话,那我就把我的公钥传给他,这个反正有人得到也没关系。我朋友拿到公钥之后,就把他想要跟我说的话进行加密,然后把加密后的信息通过互联网传给我,整个互联网上,即使有其他人得到了这些加密数据也没用,因为只有我拥有跟那个公钥配对的这个私钥,所以也就只有我,才可以解密这个信息。

fei_duicheng

非对称加密的类比和应用

我们再来打个比方。我家门口有个信箱,那么凡是知道我家地址的人就可以给我发信了,所以我家的地址就相当于公钥,但是由于只有我自己有信箱的钥匙,所以也只有我能看到信。那这个信箱的钥匙当然就相当于私钥了。

打另一个比方。我和朋友要秘密的通信。首先朋友寄给我一把她的打开的锁,这样我把信放到一个盒子里,锁上,给她寄过去。拿到盒子之后,朋友拿出自己的钥匙就可以打开箱子了。反过来,如果我想从她那里得到回信,那我就给她寄我的锁就行了。这种通信方式的好处是,任何一把锁的钥匙都没有邮寄过。

letter

最后说实际的一个应用。我在北京,如果我有一台服务器在美国,那么我如何让信息从我的服务器上安全的传递到我这里呢?首先,我先生成一对 key,然后我就把公钥传到我的服务器上。这样,我的服务器就可以把信息加密,然后传递给我了,听起来可能比较麻烦,但是实际中有 ssh 这样的程序帮忙,其实加密和解密过程都是自动完成的。 下面是生产秘钥的具体操作过程:

$> ssh-keygen

Generating public/private rsa key pair.

$> ls .ssh/

id_rsa id_rsa.pub

好,加密通信就说这么多,以后有机会我们会谈一个相关的话题:数字签名。

 

参考资料:

http://en.wikipedia.org/wiki/Public-key_cryptography

http://blog.agupieware.com/2014/05/online-learning-bachelors-level.html

有意思的加密通信,首发于 极客范 - GeekFan.net

simple – 基于 Github 的极简博客系统

$
0
0

simple是简单的静态博客生成器,基于 Github Pages,静态页面,完全在线操作,不需要服务器,只需一个 Github 账号即可。

o

传统的独立博客玩法,需要域名、服务器、程序等等一系列服务才玩得转,当然功能也丰富的多。

simple需要 GitHub 账号,然后创建一个 username.github.io 的 project,注意要勾选生成 README。(支持绑定自己的域名)。

然后访问 http://isnowfy.github.io/simple/并使用 Github 账号登录,根据提示就可以完成设置并自动生成页面了。

最终的效果与作者的 介绍页面相同,详细阅读后就知道怎么做的,注意每次打开页面后记得点一次 New Post 再开始写作,URL 里记得加 .html,支持标签。

那么,你还在写博客么?

simple – 基于 Github 的极简博客系统,首发于 极客范 - GeekFan.net


浅谈如何获取用户对项目正式交付文档的签字

$
0
0

浅谈如何获取用户对项目正式交付文档的签字

       

       需要用户签字确认的项目正式交付文档主要是需求规格说明书(SRS),变更单(CR)和用户验收报告。然而要获取用户对于这些正式交付物却是非常的困难:
用户迟迟不愿意对需求签字,从而常常导致设计开发都进行一段时间了,而需求还没有最后确认,用户还在频繁的对需求进行修改,自然,这些修改可能都不算变更!
只要有缺陷,用户就不愿意在验收报告上签字,甚至试运行一段时间后都因为一些缺陷而得不到签字。
为啥会出现这样的情况?签字确认意味着将来出现状况时需要承担责任,所以所有人基于安全的角度,会尽力地拖延签字。
我们以甲方和乙方为例来描述,一旦甲方在需求上签字,那么就意味着以后所有的对需求的调整都会成为“变更”,甲方会额外支付这些变更。而对于乙方来说,一旦得到这些签字确认,那么就可以光明正大得要求甲方付款。
用户们拖延签字的理由就更是林林总总:
    1. 交付物还存在问题/不确认性;(呵呵,找出一些小问题还是很容易的,双方你来我往的讨论,很快几周时间就混过去了)
    2. 签字责任人出差;(现在通信技术这么发达,出差也能成为不签字的理由?项目那么重要,难道比不上出差吗?这个明显是个理由)
    3. 工作太忙,没有时间看;(这个就是明明白白的“拖”字诀了)
    4. 需要内部人员评审;(一看就知道,这是在拿所谓的内部流程在踢皮球)

所以不及时签字是项目进行过程中一个十分痛苦的事情。有什么解决方案吗?国内项目的实践很简单:让销售去搞定。。。所谓搞定,无外乎请客,交易,拉关系,或者请自己领导出面协调、承诺
。在国内项目管理越来越规范的大趋势下,这样的方法就略显不足了,需要有新的方法。

方法一:签字文档化整为零。(方法有效性:90%)
一个需求文档至少几十页到上百页,让用户一下或者一段时间完全签字确认整份文档十分困难。通过系统的方法把文档分为较小的部分(比如用例,模块或者子系统),用户只需要对确定的部分签字,而不确定的部分等澄清后再确认。
文档如何化整为零是需要技术的,整个框架需要有明确的逻辑性和合理性。


方法二:至下而上的签字流程。(方法有效性:80%)
签字责任人常常是负责的经理或领导,对于他们来说,很多细节可能不会特别深入或者了解,强求他们马上签字有些难度,但是如果他们看到自己的手下的签字,那么他会放心得多,也就容易签字。

方法三:安全的集体决策。(方法有效性:70%)
举行正式的交付物评审,有与会的相关项目干系人是否“原则上”同意该项交付物。请注意“原则上同意”这个词很重要,它不会给参与评审的人员太大压力,同时又是一个定调调的决议,对于后续的推进具有关键性的作用。
注意:有把握再进行这样的评审,否则被攻击而被批为“漏洞百出”会很杯具。如果关键项目干系人找理由不参加这个会议,如果这样的事情发生,那么会更杯具了,它会极大阻碍后续的签字进程。

方法四:用户充分参与交付物的编写。(方法有效性:60%)
在重要交付物的编写过程中,充分调动用户的参与性,让他们能够充分了解并参与整个过程,基于这样的并肩战斗,用户会相当信任对方以及双方的共同产出物,自然也更容易签字了。
注:用户充分参与基本上属于可遇不可求。事实上,这里强调的是同用户融洽的个人/工作关系可以一定程度上帮助获得用户的签字。

方法五:带备注的签字确认。(方法有效性:100%)
一般的签字只包括两项内容:签字人名字和签字日期。而带备注的签字就更加的灵活,比如对于一项有不确定性的需求签字可能是这样的;
----
原则上确认本项需求,确认的前提是:
    A,B,C开放的问题仍需讨论并解决。(open issues)
    D,E,F可能需要调整。(risks)
    该需求基于G条件被满足的情况下。(assumption)
xxx(用户的名字)
日期
----
有了这样一份签字确认,那么可以极大的加快整体签字进程,同时这些备注也是非常重要的资料,让我们知道最近的工作重点以及将来可能的变更点。
如果是一份用户验收报告,那么部分签字的例子可能是这样的:
---
原则上验收本项需求,前提是:
   目前未解决的5个缺陷需在2010年6月30日前修改并测试正确。
   如果针对5个缺陷的版本导致其它功能错误,那么本项验收以及相关功能的验收确认无效,需完全重新进行验收测试。
---    
如何关闭这些备注呢?如果所有问题在后续过程中已解决,那么可以把调整后的部分请用户签字,或者再打印一份出来进行签字确认。

方法六:定期跟踪签字进程并在适当时候升级到更高级别管理层。(方法有效性:50%)
这个方法用于对付那么不讲道理能拖就拖的用户,定期跟踪签字进程实际是在每周的项目报告中把用户放到热锅上,每过一周温度升高一点,知道用户最后无法坚持而已。用户不得不在签字可能会承担责任/承担因为不签字而项目延迟的责任中间考虑。
注:这招只能在你方无过错,原因只是用户拖延的情况。

方法七:客户关系。(方法有效性:80%)
用户签字意味着承担责任,同时它也是一种权力,对于乙方来说,需要尊重这样的权力,必要的时候请销售/高管参与是必要的,特别是用户验收报告的签字,上述6种方法是不足够的,必须要相关人出面才能够搞定。
当然,必须得符合商业业务规则。

有了上述的7种方法,那么在寻求用户签字确认时,需要根据实际情况制定战略,并选择一个合适的组合,从而实现及时的、有效的签字确认。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



一文读懂BI商业智能与大数据应用的区别

$
0
0

BI11      之所以要区分大数据应用与BI(商业智能),是因为大数据应用与BI、数据挖掘等,并没有一个相对完整的认知。

BI(BusinessIntelligence)即商务智能,它是一套完整的解决方案,用来将企业中现有的数据进行有效的整合,快速准确的提供报表并提出决策依据,帮助企业做出明智的业务经营决策。

伴随着BI的发展,是ETL,数据集成平台等概念的提出。 ETL,Extraction Transformation Loading,数据提取、转换和加载,数据集成平台主要功能对各种业务数据进行抽取和相关转化,以此来满足BI、数据仓库对数据格式和内容挖掘的要求。

数据集成平台的基础工作与ETL有很大的相似性,其主要功能 是实现不同系统不同格式数据地抽取,并且按照目标需求转化成为相应的格式。数据集成开始是点对点的,慢慢地发现这种模式对于系统之间,不同所有权的企业数 据流向以及数据标准控制很难,为此,诞生了对统一企业数据平台的需求,来实现企业级之间的数据交互。

数据集成平台就像网络中Hub,可以连接所有应用系统,实现系统之间数据的互通有无。数据集成平台以BI、数据仓库需求而产生,现在已经跨越了最初的需求,上升到了一个更高的阶段。

如今大数据应用更多关注非结构化数据,更多谈论互联网,Twitter、Facebook、博客等非结构化数据,如此理解大数据应用,显然就有些走偏了。结构化数据也属于大数据,且呈现出相同的特点和特征,如数据量大,增长越来越快,对数据处理要求高等。

结构化数据是广义大数据中含金量或者价值密度最高的一部分数据,与之相比,非结构化数据含金量高但价值密度低。在Hadoop平台出现之前,没有人谈论大数据。数据应用主要是结构化数据,多采用IBM、HP等老牌厂商的小型机或服务器设备。

采用传统方法处理这些价值密度低的非结构化数据,被认为是不值得的,因为其产出实在是有限。Hadoop平台出现之后,提供了一种开放的、廉价的、基于普通商业硬件的平台,其核心是分布式大规模并行处理,从而为非结构化数据处理创造条件。

大数据应用的数据来源应该包括结构化数据,如各种数据库、各 种结构化文件、消息队列和应用系统数据等,其次才是非结构化数据,又可以进一步细分为两部分,一是社交媒体,如Twitter、Facebook、博客等 产生的数据,包括用户点击的习惯/特点,发表的评论,评论的特点,网民之间的关系等,这些都构成了大数据来源。另外一部分数据,也是数据量比较大的数据, 就是机器设备以及传感器所产生的数据。以电信行业为例,CDR、呼叫记录,这些数据都属于原始传感器数据,主要来自路由器或者基站。此外,手机的置传感 器,各种手持设备、门禁系统,摄像头、ATM机等,其数据量也非常巨大。

对于分析大数据的工具,目前所有的分析工具都侧重于结构化分析,例如针对社交媒体评论方向的分析,根据特定的词频或者语义,通过统计正面/负面评论的比例,来确定评论性质。如果有一个应用系统是接收结构化数据的,例如一个分析系统,接收这些语义就可以便于分析。

让大数据应用落地,其中的关键在于与行业应用的深度融合。

公安行业的视频影像处理是一个特定应用领域,传统BI、 ETL工具拿这些数据没有办法,采用分布式Hadoop进行处理能够带来很好的效益,因为Hadoop可以处理数据量足够大。公安行业实际上已采集了大量 视频影像数据,利用这些数据,可以追踪一个嫌疑犯的行踪,什么时间在全国哪些地区出现过。这些应用不可能单纯依靠人的力量,需要借助人脸识别、图像识别技 术、模式处理,数据压缩等技术,需要海量处理软件,抓出相关特征,帮助公安人员提高工作效率。

在电信行业,计费系统实际上是对各种数据进行整合后的结果, 是一个缩小的数据。借助大数据应用,运营商可以原始大数据进行分析,例如分析传感器数据是否有异常,从而判断设备异常等,这些都是一些用传统BI工具无法 实现的分析,其结果往往会出乎意料,帮助运营商提高服务水平以及用户的满意度。

在互联网行业,通过分析手机上网轨迹,可以分析了解客户群,了解用户的偏好,此外,获取地理位置的信息,也具有特定价值。

从这些行业大数据应用分析来看,一个是视频影像处理,一个是日志分析,另外一个是处理特定文件格式的分析处理,彼此之间显然没有任何通用性的特点,其共同点就是利用了廉价的大数据处理平台。

Via 互联网分析沙龙


(关注更多人人都是产品经理观点,参与微信互动(微信搜索“人人都是产品经理”或“woshipm”)

Chrome占据浏览器份额第一 火狐跌至第四

$
0
0

北京时间 5 月 18 日上午消息,社交发现和分享平台 Shareaholic 今天公布了最新的浏览器市场份额报告,公布了过去 8 个月中各大浏览器市场份额的变化情况。Chrome 仍在桌面和移动使用量方面处于领先,而最大的改变在于火狐的市场份额从第二下跌至第四,目前已落后于 Safari 和 IE。

从 2013 年 9 月至 2014 年 4 月,Chrome 的使用量市场份额基本持平,分别为 34.68% 和 34.65%。不过,Chrome 的使用量仍超过了火狐、IE 和 Opera 的总和。火狐的市场份额下降了 4.66%,Safari 下降了 0.96%,而 IE 则下降了 3.13%。

Shareaholic 此次报告的要点包括:

  • Safari (移动应用版)和 Android 浏览器是市场份额取得明显增长的两款浏览器。以这一增长速度来看,Android 浏览器将很快对火狐和 IE 形成挑战,进入所有浏览器排行榜的前 4 名。
  • 报告中的大部分浏览器市场份额出现下降,而最大的输家是 IE 和火狐。
  • Opera Mini 的市场份额出现上升,而 Opera 桌面浏览器的市场份额下降。

这一报告表明,移动浏览器在浏览器市场的地位越来越重要。因此,Mozilla 开发火狐 OS 操作系统是合理的。该公司已经意识到,如果没有预装浏览器的自主操作系统,那么将无法与谷歌、苹果公司和微软竞争。

根据另一家市场研究公司 Net Application 的数据,IE 仍是目前最流行的浏览器。需要指出,Shareaholic 跟踪了访问其发行商网络中 20 万家网站的 2.5 亿用户,而 Net Application 跟踪了每月访问约 4 万家网站的 1.6 亿独立用户。此外,Shareaholic 还跟踪了移动浏览器的数据,而 Net Application 针对桌面浏览器和移动浏览器分别制作报告。

最重要的是,Shareaholi 的数据统计了每种浏览器打开过多少个网页。Net Application 仅仅关注访问某一网页、来自不同浏览器的用户数是多少。因此,使用量市场份额和用户数市场份额之间仍存在明显差异。(李丽)

本文链接

dubbo的安装和使用

$
0
0

背景

( #)

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

  • 单一应用架构
    • 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。
    • 此时,用于简化增删改查工作量的  数据访问框架(ORM) 是关键。
  • 垂直应用架构
    • 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。
    • 此时,用于加速前端页面开发的  Web框架(MVC) 是关键。
  • 分布式服务架构
    • 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。
    • 此时,用于提高业务复用及整合的  分布式服务框架(RPC) 是关键。
  • 流动计算架构
    • 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。
    • 此时,用于提高机器利用率的  资源调度和治理中心(SOA) 是关键。

需求

( #)

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

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

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

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

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

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

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

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

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

以上是Dubbo最基本的几个需求,更多服务治理问题参见:

http://code.alibabatech.com/blog/experience_1402/service-governance-process.html

架构

( #)

节点角色说明:

  • Provider: 暴露服务的服务提供方。
  • Consumer: 调用远程服务的服务消费方。
  • Registry: 服务注册与发现的注册中心。
  • Monitor: 统计服务的调用次调和调用时间的监控中心。
  • Container: 服务运行容器。

调用关系说明:

  • 0. 服务容器负责启动,加载,运行服务提供者。
  • 1. 服务提供者在启动时,向注册中心注册自己提供的服务。
  • 2. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  • 3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  • 4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

(1) 连通性:

  • 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
  • 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
  • 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
  • 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
  • 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
  • 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
  • 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
  • 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者

(2) 健状性:

  • 监控中心宕掉不影响使用,只是丢失部分采样数据
  • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
  • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
  • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
  • 服务提供者无状态,任意一台宕掉后,不影响使用
  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

(3) 伸缩性:

  • 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
  • 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者

(4) 升级性:

  • 当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力:

  • Deployer: 自动部署服务的本地代理。
  • Repository: 仓库用于存储服务应用发布包。
  • Scheduler: 调度中心基于访问压力自动增减服务提供者。
  • Admin: 统一管理控制台。

安装

一、本地服务

  1、定义服务接口: (该接口需单独打包,在服务提供方和消费方共享)
public interface CustomerService {
	public String getName();
}
   2、在服务提供方实现接口:(对服务消费方隐藏实现)
  
public class CustomerServiceImpl implements CustomerService{
	@Override
	public String getName() {
		System.out.print("我打印");
		return "打印结果";
	}
}
  3、然后引入dubbo的几个包
     dubbo-2.5.3.jar
     log4j.jar
     netty-3.5.7.Final.jar
     slf4j.jar
     slf4j-log4j.jar
     zkclient.jar
     zookeeper.jar

  4、用Spring配置声明暴露服务:
   
      新建applicationProvider.xml,配置内容如下:
   
<?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:dubbo="http://code.alibabatech.com/schema/dubbo"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://code.alibabatech.com/schema/dubbo  
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">   <!-- 具体的实现bean -->  <bean id="demoService" class="com.jinbin.service.customer.CustomerServiceImpl" />  <!-- 提供方应用信息,用于计算依赖关系 -->  <dubbo:application name="xixi_provider"  />    <!-- 使用multicast广播注册中心暴露服务地址   
    <dubbo:registry address="multicast://localhost:1234" />-->   <!-- 使用zookeeper注册中心暴露服务地址 -->  <dubbo:registry address="zookeeper://192.168.1.3:2181" />     <!-- 用dubbo协议在20880端口暴露服务 -->  <dubbo:protocol name="dubbo" port="20880" />  <!-- 声明需要暴露的服务接口 -->  <dubbo:service interface="com.jinbin.service.customer.CustomerService" ref="demoService" />  </beans>  


我这里暴露服务器的地址交由zookeeper来管理的,使用者首先先要安装zookeeper应用才能使用此功能,相关安装步骤请参看相关博文
 
5、加载Spring配置,并调用远程服务:(也可以使用IoC注入)
public class DubooProvider {
	public static void main(String[] args) {
	    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(  
                new String[]{"applicationProvider.xml"});  
        context.start();  
        System.out.println("Press any key to exit.");  
        try {
			System.in.read();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
	}
}

并且启动,使其进入启动状态。

以上为服务器提供者的完整步骤,功能接口都已经写好,下面我们就开始怎么远程调用

二、服务消费者

 1、新建个配置文件applicationConsumer.xml,内容如下:

<?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:dubbo="http://code.alibabatech.com/schema/dubbo"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://code.alibabatech.com/schema/dubbo  
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">        <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->  <dubbo:application name="consumer-of-helloworld-app" />     <!-- 使用multicast广播注册中心暴露发现服务地址 -->  <dubbo:registry  protocol="zookeeper" address="zookeeper://192.168.1.3:2181" />       <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->  <dubbo:reference id="demoService" interface="com.jinbin.service.customer.CustomerService" />  </beans>  


为了在web中使用,我们在web.xml中配置在spring启动读取过程中
<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/application.xml /WEB-INF/applicationConsumer.xml</param-value></context-param>

2、接口调用

  调用过程很简单,先把接口文件打成jar包,然后在此工程中进行引用

 在springmvc调用程序如下:
@Autowired CustomerService demoService ;	 
  @RequestMapping(value="duboo1")
  public void duboo1(){
		   demoService.getName();
}

即可执行成功

三、dubbo-admin的使用


    下载dubbo-admin-2.5.3.war

    将其放到tomcat下面,配置dubbo.properties,
vi webapps/ROOT/WEB-INF/dubbo.properties
dubbo.properties
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest
      修改zookeeper的URL和端口
     

    启动:

./bin/startup.sh
   
   打开,直接访问首页如下:

服务提供者页面

服务消费者页面

服务应用页面

添加路由规则页面

添加动态配置页面

作者:songjinbin 发表于2014-5-18 20:14:31 原文链接
阅读:53 评论:0 查看评论

免费域名解析服务

$
0
0

  一般域名使用注册商提供的域名解析服务虽然方便,但功能大多有限,特别是目前国内还会针对某些DNS服务器进行屏蔽,造成网站无法解析的情况出现,因此,使用第三方域名解析服务也是中国网站的必要选择,这里就介绍一些常见的免费域名解析服务。

   域名注册商提供的免费服务

   Godaddy:不在Godaddy注册域名,也可以使用Godaddy的域名解析服务,使用方法很简单,登录Godaddy网站后,点击“Add Off-site DNS”即可添加用户的域名,之后将用户域名的DNS设置为Godaddy指定的地址,域名DNS生效后,即可点击添加的域名进行DNS解析设置。

   NameCheap:知名的域名注册商NameCheap也和Godaddy一样,提供免费DNS域名解析,点击“FreeDNS”后,即可添加用户域名,用户可以通过修改DNS或域名邮件来验证自己的域名,NameCheap的解析服务支持的功能有:网址转发(可隐藏原URL、支持301重定向)、邮件转发、A记录、CNAME别名记录、MX邮件记录、TXT文本记录、NS记录、AAAA记录(IPV6)、动态域名解析等等。

   国内免费域名解析服务

   DNSPod:DNSPod是国内运营较久的免费DNS解析服务,除了免费服务外,还提供多项收费服务。DNSPod的功能较多,支持电信、网通、教育网双线或者三线智能DNS解析,在中国国内解析速度较快,但对于国外的Google爬虫来说经常出现无法访问的情况。

   DNS.La:也是类似DNSPod的免费DNS解析服务,可以为同时有电信、联通、教育网服务器的网站提供免费智能DNS的解析。

   EDNS:易名中国提供的免费域名解析服务,非易名中国用户也可使用。

   国外免费域名解析服务

   ZoneEdit:美国著名的老牌免费域名DNS解析服务,成立于1999年。免费帐户最多可添加5个域名。解析类型:A记录、AAAA记录、 TXT文本记录、LOC记录、PTR记录、CNAME别名记录、MX邮件记录、网址转发、邮件转发等。界面超级简洁,速度快,稳定。

   HE.NET:是美国老牌IDC,成立于1994年,在技术领域比较强,尤其是IPV6应用。这个免费DNS解析服务最多可以添加50个域名,可以设置A记录、AAAA记录、CNAME别名记录、MX邮件记录、NS记录、TXT记录、SRV记录。

   FreeDNS:一家美国免费域名DNS解析服务网站,界面简洁,注册简单,支持添加任何后缀的域名,支持Google Apps服务。免费域名DNS解析服务有三种模式:简单模式可直接设置IP指向、转发模式可设置301永久重定向和302临时重定向、高级模式可设置A记录、AAAA记录、CNAME记录、MX记录、PTR记录、TXT记录。

   afraid:美国一家免费域名解析服务,运营了相当长的时间了,值得提醒的是按照官方规定六个月账号必须有一次登录,否则账号会被锁定 。

   CDN服务自带域名解析

   CloudFlare:虽然它的主营业务是CDN(Anycast),但丝毫不影响它作为免费DNS的声誉,况且这个免费DNS是真的全球分布(12个节点),且使用了CDN技术。

   百度加速乐:加速乐也主要是CDN业务,顺带提供DNS域名解析,提供抗CC攻击、免费DNS解析、免费分省解析、防黑客攻击、黑链暗链防护、页面篡改防护服务。

   360网站卫士:360网站卫士也主要是CDN业务,顺带提供DNS域名解析,提供了网站加速,智能高防DNS,防DDOS,防CC,防黑客和网站永久在线等服务。

评论《免费域名解析服务》的内容...

相关文章:


微博: 新浪微博 - 微信公众号:williamlonginfo
月光博客投稿信箱:williamlong.info(at)gmail.com
Created by William Long www.williamlong.info

天猫浏览型应用的CDN静态化架构演变

$
0
0

  在天猫双11活动中,商品详情、店铺等浏览型系统,通常会承受超出日常数倍甚至数十倍的流量冲击。随着历年来双11流量的大幅增加,每年这些浏览型系统都要面临容量评估、硬件扩容、性能优化等各类技术挑战。因此,架构方面的重点在于,如何能够利用合理成本应对瞬间飙高的峰值请求,并确保活动完整周期中系统容量的可伸缩性、用户响应时间的稳定性,以及外部依赖系统出现问题时的高可用性。此外,作为最主要的页面流量承载体系,架构方面还需考虑防爬攻击、流控容灾等安全、稳定的需求,并综合衡量网络带宽、硬件成本、缓存效率等各方面要素,找准平衡点,从而达到以不变应万变的理想效果。

   演进

  为此,自2011年起,以天猫商品详情系统为代表,天猫浏览型系统在架构上的主要工作之一就是通过静态化技术实现了动静态信息分离、利用缓存技术存放静态化内容、利用少量动态数据异步加载填充。整个过程历经单机静态化、统一缓存接入,到2013年双11前彻底CDN化三个阶段(如图1所示),有效解决了缓存命中率、流量自然分布、系统扩容简化、用户端响应速度等关键问题。

图1  CDN化的三个阶段

  目前,天猫浏览型系统最新使用的这套基于CDN的静态化架构,可以满足高可用持续伸缩的原始预期,并包含如下特性。

  • 动静分离:HTML静态化和热点分离。
  • 分布式缓存体系:利用CDN节点分布式缓存。
  • 多级缓存机制:CDN两级+应用一级。
  • 统一服务静态化集群。
  • 一致性维持:主动失效&自动失效缓存机制。
  • 动态内容填充:能支持多种时效性动态内容填充方式。
  • 监控预警机制:流量、失效、命中率等关键参数实时监控报警。

  本文将针对这一优化历程,就主要技术挑战、架构改造策略、最终优化成果做一个总览式的介绍,并重点对CDN化过程中整体架构的演进、缓存失效机制、动态内容填充等具体要点进行论述。

   第一阶段:系统静态化

  早期天猫浏览型系统大多采用简单架构,实现一层很薄的前台应用。以天猫商品详情系统为例,针对商品、用户等访问量较大的数据中心接口模式改造为应用Client端缓存前置,同时普遍使用页面高速缓存(PageCache)来降低后端系统压力,使得整体可支持应用水平扩展不受限制。这一阶段系统面临的主要问题和挑战包括以下几点:

  • 应用服务器瓶颈,页面渲染带来的CPU开销巨大。
  • 单纯基于Java端的缓存已基本覆盖,整体性能提升空间有限。
  • 水平扩容只能支持容量线性提升,难以满足大促井喷式流量增长,扩容成本高。

  从问题看,基于原有动态浏览型系统模式而优化的瓶颈很难规避,例如以下几点:

  • Java应用服务器端必要开销,包括:涉及页面内容的字符串查找、替换、拼接等;元数据获取的网络开销;Servlet本身的性能瓶颈。
  • Web服务器端,包括:模块过滤,例如访问日志、Cookie打点、繁简转换;大HTML页面本身的GZIP压缩等。
  • 突发流量的抵御,例如攻击、秒杀、大促,等等。
  • 已用优化手段达到了边界,包括:可使用缓存的地方已经使用;服务端CPU能力已优化完毕(模板解析、压缩)。

  总体来看,必须从架构着手彻底解决。架构优化的方向上,考虑以下3个方面:

  • 改变缓存方式,直接缓存HTTP响应结果。
  • 改变缓存位置,直接基于Web服务器,屏蔽业务逻辑。
  • 基本原则,缓存空间足够大、无单点、易于维护。

  为此,2012年起正式启动了动态浏览型系统的改造项目,通过静态化手段解决上述问题。即基于业务把原动态系统中的内容做动静分离,对浏览者无关部分做缓存,动态内容做CSI填充。具体考虑从三方面重点着手展开:动静信息分离、静态化缓存方式,以及缓存失效机制。图2为一期静态化整体架构。

图2  一期静态化整体架构

   动静分离

  将原页面内容按业务进行区分,从浏览用户、信息发布者、时间、地域、私有(Cookie等)信息等维度分析,抽取出页面中相对公共不依赖以上因素,且变化频度较低的内容作为基础,生成静态化内容。静态化后页面URL固定,不同URL表示不同内容,服务器返回的请求与URL相关,其他动态内容则通过异步接口调用,通过CSI方式填充。以商品详情系统为例,静态化后商品基本信息如标题、商品详情、销售属性组合等信息均直接进入缓存,其他如优惠、库存、物流、服务等动态信息则通过异步调用方式填充至静态化后的页面框架内。

   缓存方式

  整体可划分为应用服务器、Web服务器、CDN节点、客户端浏览器4层缓存体系(如图3所示),分别承载不同使命。

图3  缓存整体划分

  缓存系统方面从开发成本、稳定性、I/O性能各方面综合考虑,选择了阿里内部广泛使用的分布式key/value系统Tair,存取静态化后的页面。相对 Nginx本地硬盘缓存方式来说,本地Tair读写性能更优,且服务器响应时间和负载波动影响小,使用及维护成本低。整套体系详解如下:

  • 应用层缓存:减小后端应用服务器压力,减少远程调用量。
  • Web服务器缓存:减小后端应用服务器压力,抵挡瞬间峰值和/或针对少量定点内容的攻击。
  • CDN缓存:合理地利用CDN,内容缓存放置在离用户最近的地方,加快响应的速度。
  • 浏览器缓存:减少用户请求数量,降低系统压力,提升用户体验。

   缓存失效

  缓存失效主要包含“失效后台进行主动失效”和“缓存过期自动失效”两种机制。针对主动失效,主要技术难点包括以下3个方面:

  • 失效来源及监控范围:基于业务决定需要监听哪些数据源哪部分内容变更,通过变更消息接收执行缓存失效动作。
  • 每秒失效数据量级:单位时间内大量数据源(如商品、店铺装修)失效处理。
  • 要失效的缓存范围:支持批量(例如基于域名)和单个数据源缓存失效变更。

  以商品详情系统为例,失效来源主要为商品数据及店铺装修信息,后台用户修改导致对应内容发生变更时,通过消息机制通知失效后台。失效后台接收消息并保留待失效商品ID,通过调用本地Tair接口失效缓存,大致流程如图4所示。

图4  缓存失效流程

   改造效果

  依然以天猫商品详情系统为例,采取静态化架构后,2012年双11时,在性能方面,结合后期完成的店铺装修分离等优化工作,系统单机(实体机)在80%缓存命中率的情况下,安全QPS(每秒查询率)相较2011年同期单机性能提升7倍多,系统资源则不到原来的50%。与此同时,静态化还解决了单URL热点攻击问题,更重要的是,使得原动态架构下依赖的后端Java系统可以转变为弱依赖:一方面既通过静态化缓存层一定程度上保护了后端系统;另一方面在极限情况 下,当后端系统不可用时,可以通过缓存维持一部分访问量。

   第二阶段:统一Web缓存

  第一阶段以商品详情为主的静态化架构改造取得了良好的效果,除天猫商品详情系统率先完成改造外,店铺等浏览型业务系统也很快参照类似方案完成了架构调整。在过程中,逐渐确立了静态化技术规范,简化了接入步骤;同时,也发现在各自的系统中,尽管同样基于浏览型业务场景,但由于采用的缓存方案细节差异,存在一些涉及静态化缓存体系相关的共性问题,包括以下几点:

  • 单机缓存静态页面,受部署模式影响,缓存层无法水平扩展。
  • 单机模式下,缓存受限于服务器能力及内存容量,命中率受制约。
  • CSI模式填充动态内容,需要前端脚本配合,开发成本较高。

  因此,自然而然想到有必要统一Web缓存层接入,共享静态化集群以节省成本、提高稳定性和命中率。从运维角度看:

  • 统一接入层可以减少多个应用接入使用的成本,接入的应用只需维护自身Java系统,不用单独维护缓存;只要关心如何使用,统一的缓存框架也可更好地让更多流量型系统接入使用。
  • 统一接入层易于维护,并可统一加强全局监控、实现配置自动化,使集中维护升级更加便利。
  • 统一接入层可以共享内存,最大化利用内存,不同系统间的内存可以动态切换,有效应对攻击等类似突发情况。

  搭建统一接入层,需要针对各浏览型系统做局部改动。而整体需要重点解决的技术问题,从架构层次上看,主要涉及以下几大部分:

   缓存系统选择

  第一阶段各浏览型系统采用了单机缓存模式,基于成本、业务场景等各方面因素稍有不同。搭建统一接入层需要能够兼顾各浏览型系统的特殊要求,同时还需能支持共 同需要的ESI解析及ESI模式下GZIP压缩,完成静态页面局部动态内容服务端填充;性能方面,能够满足双11/双12流量压力下的QPS(每秒访问 率)要求;支持失效协议以及长连接,可执行批量失效。综合以上分析,并考虑未来静态化内容最终CDN化部署方式,统一接入层Cache最终软件层面可支持 以上所有功能,同时还包括快速失效和预热能力,支持CSS和JavaScript的脚本合并,长连接和批量失效,支持基于HTTP头的可编程配置等。

   统一失效机制

  与缓存软件变更对应,各接入统一缓存的浏览型系统需针对新的缓存体系及协议改造原有失效机制,使用公共协议标准来执行批量及单个对象的主动失效。同时,建立 了统一的失效中心和缓存校验层,所有接入应用的主动失效请求统一经由失效中心,通过Purge方式执行缓存失效。底层失效源方面,监控信息源数据变更。以商品为例,当商品编辑完毕,包括商品标题描述等更新后详情页面需要失效,基于实时监控和消息机制进行主动失效(如图5所示)。

图5  基于事实监控和消息机制主动失效

   Web服务器改造

  缓存层之前的Web服务层,需要能支持一致性Hash分组,并集成现有系统使用的Session框架,可支持基于域名虚拟主机的动态配置。为此,核心系统部门的同事自行开发了淘宝定制版本的Nginx服务器(Tengine),作为统一接入层之上的Web服务器层部署。

   网络流量支持

  统一接入缓存层后,由于集中了各系统缓存信息且访问集中,所以网络部署层次方面,可使用万兆网卡配置解决硬件瓶颈;同时评估集群需支撑的网络出口流量,确保机房内部及外部出口无瓶颈;在缓存不命中的情况下,需能支撑请求回源服务器端形成的内部流量。

   整体部署方案

  图6是整体部署方案,从中可以看出:

  • 统一接入层部署,包括前端Nginx服务器+缓存系统+后端Java应用部署结构。
  • Web服务器层做一致性Hash分组。
  • 统一缓存层支持ESI或CSI方式获取动态内容。
  • 统一失效中心机制失效缓存。

图6  整体部署方案

   改造效果

  统一接入层于2013年上半年改造完成并开始了商品详情等浏览型系统的接入工作,完成后,在原有单机缓存模式之上又增加了一层集中式缓存,解决了缓存层的水平扩展问题。万兆网卡的使用有效解决了缓存层的网络瓶颈。由于统一接入层与应用无关,因此可以多应用共用,使监控和维护成本大大降低,并提高了质量和效率。当然,这一改造也造成应用对缓存层的强依赖链路,同时这一层缓存也存在单点问题。从静态化单机缓存模式到统一接入层,路只走了一半,一切改造的终极目标,是利用CDN分布式、地域性特性及强大的流量容量体系,实现浏览型应用的CDN静态化。

   第三阶段:CDN静态化

  统一接入层解决了单机缓存内存使用率低的问题,摆脱了单机缓存受内存大小制约,在面对商品数量增加和商品热点分散的场景下,只能垂直扩展那些无法水平扩展的 问题,这提升了缓存系统的可维护性和扩展性。在完成系统从单机静态化缓存到统一接入层的架构改造之后,已经具备了将静态页面放置到CDN上的条件。CDN 提供了更强的服务能力,放置在离用户最近的节点上,是缓存系统单元化最理想的架构。同时,也为双11峰值流量和防攻击提供了更为可靠稳定的保障。

  CDN化涉及3个具体技术难点:

  • CDN分布式节点失效问题。方案:采用主动失效的方式,商品变更后主动发送请求给缓存校验层,由其通知失效中心,接收并分发处理节点失效任务,以确保秒级失效。
  • 命中率问题。方案:优化节点部署条件,CDN节点数量可控,避免失效请求量过大,靠近流量集中区域,且节点到主站网络稳定;控制节点数量,访问流量集中分布在这批节点;节点内部采用类似统一缓存层的一致性Hash规则,以达到类似命中率。
  • 局部区域动态内容定时切换。方案:价格、库存等动态信息走动态系统接口,通过异步方式获取;展现端定时切换活动Banner等内容,走ESI回源,并同样缓存回源的静态资源。

   整体架构

  基于以上思路,总体架构已经较为清晰,方案上从缓存体系、失效模式、动态内容填充几方面入手执行改造,整体架构如图7所示。

图7  静态化整体架构

   缓存体系

  统一接入层和CDN节点上都是用Web服务器+Cache方式。静态化应用对应的域名会被解析到CDN和统一接入层的虚拟IP上,CDN拿到请求后,先读取 本地缓存,缓存不命中则到统一缓存层获取。统一接入层按原有逻辑处理请求,缓存不命中则回源到服务器端获取数据。同时,统一接入层Web服务器需要能够识 别用户请求是CDN回源类型,还是正常请求,以免重复打点访问日志和GZIP压缩。

   缓存失效

  缓存失效原理与统一接入层类似。失效执行流程大致为,客户端请求经VIP被随机分配给失效中心某个节点,然后失效任务被发送至代理,经代理向缓存服务器发送失效命令并返回结果,如图8所示。

图8  缓存失效原理

   动态内容填充

  业务方面,因为存在定时切换页面局部内容的需求,整体架构中增加ESI和页面打点作为动态内容填充方式。ESI标签由Cache层负责解析回源,并且会对ESI请求做缓存,并且提供如下特性。

  • 需要定时做全站变更的页面模块用ESI的Include实现,时间判断则放在应用服务器处理回源请求的时候。
  • 回源以后,应用服务器设置失效时间。例如请求回源时应用服务器加上s-maxAge,这个页头的缓存在定点失效。
  • Cache系统提供合并回源,避免重复,防止失效后的高并发回源给应用服务器带来冲击。
  • Cache系统在ESI的缓存失效后回源,回源的请求处理期间不会挂起外部请求,会继续向客户端返回老版本的页面,回源请求处理完以后更新成新版本。类似Copy on Write,防止回源请求挂起导致前端服务器挂起。
  • ESI回源时对Response Header的操作不会发到客户端。

   改造效果

  最终基于CDN静态化的架构去除了单机缓存的横向扩展瓶颈,命中率越高、系统容量越大的特性决定了可以用较小的成本支持峰值流量;引入ESI编程模型,解决 了页面上的局部刷新问题,支持双11业务中一些需要全网定时切换页面内容的特殊需求;静态页面+弱依赖改造带来高可用性,并最终沉淀出了一套与应用无关的 缓存和失效体系。2013年双11当天,凭借这一整套CDN静态化架构,天猫商品详情等浏览型系统平稳度过了创造历史的一天,无论是页面访问量(PV)还 是页面请求峰值(QPS)均创新高,而系统本身非常稳定,并有充足余量承受更大级别的访问流量。同时,新的部署模型和基于CDN节点地域特性的缓存体系, 也降低了秒级请求的冲击型峰值,更好地满足了系统稳定性需求。在未来一段时间内,与天猫类似的浏览型系统均能够参照这套架构体系较为方便地完成静态化改造 和接入,并达到理想的稳定性和可伸缩目标。

  作者徐昭,花名长恭,主要负责天猫详情系统的架构优化工作。毕业于浙江大学计算机专业,热爱Java Web技术,多关注服务端性能优化,热衷开源技术的研究和分享。

浏览器兼容模式X-UA-Compatible的设置

$
0
0

X-UA-Compatible是针对IE8新加的一个设置,对于IE8以下的浏览器是不识别的。以下为一些常用的兼容模式:

<meta http-equiv="X-UA-Compatible" content="IE=9">

IE9 模式支持全范围的既定行业标准,包括 HTML5(草案), W3C CSS Level 3 规范(草案), SVG 1.0 规范等

<meta http-equiv="X-UA-Compatible" content="IE=8">

IE8 模式支持许多既定行业标准,W3C CSS Level 2.1 规范和 W3C Selectors API,有限支持 W3C CSS Level 3 规范(草案)和其他行业标准

<meta http-equiv="X-UA-Compatible" content="IE=7">

IE7 模式强制浏览器按照 IE 7 标准模式渲染文档,忽略是否定义指令

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9">

Emulate IE9 模式告诉 IE 使用指令来决定如果渲染文档。标准模式下以 IE9 渲染,怪癖模式下以 IE5 渲染。和 IE9 模式不同的是,Emulate IE9 模式会考虑指令。

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8">

Emulate IE8 模式告诉 IE 使用指令来决定如果渲染文档。标准模式下以 IE8 渲染,怪癖模式下以 IE5 渲染。和 IE8 模式不同的是,Emulate IE8 模式会考虑指令

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">

Emulate IE7 模式告诉 IE 使用指令来决定如果渲染文档。标准模式下以 IE7 渲染,怪癖模式下以 IE5 渲染。和 IE7 模式不同的是,Emulate IE7 模式会考虑指令。对于大多数站点而言,这是首选的兼容模式。

<meta http-equiv="X-UA-Compatible" content="IE=5">

IE5 模式告诉 IE7 是否以怪癖模式渲染文档

<meta http-equiv="X-UA-Compatible" content="IE=edge">

Edge 模式告诉 IE 以最高级模式渲染文档,也就是任何 IE 版本都以当前版本所支持的最高级标准模式渲染,避免版本升级造成的影响。简单的说,就是什么版本 IE 就用什么版本的标准模式渲染。

<meta http-equiv="X-UA-Compatible" content="chrome=1">

使用以上代码强制 IE 使用 Chrome Frame 渲染

提示 IE 用户安装 Google Frame:Google 官方提供了对 Google Frame 插件安装情况的检测,这里直接调用方法即可,如果检测到 IE 并未安装 Google Frame,则弹出对话框提示安装。

<script src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script><script>CFInstall.check();</script>

针对IE 6,7,8等版本的浏览器插件Google Chrome Frame,可以让用户的浏览器外观依然是IE的菜单和界面,但用户在浏览网页时,实际上使用的是Google Chrome浏览器内核。

最佳的兼容模式方案,结合考虑以上两种:

<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

如果IE有安装Google Chrome Frame,那么就走安装的组件,如果没有就和<meta http-equiv=”X-UA-Compatible” content=”edge” />一样。

更多参考: http://stackoverflow.com/questions/6771258/whats-the-difference-if-meta-http-equiv-x-ua-compatible-content-ie-edge-e


TextToSpeech 文本自动朗读

$
0
0
Android提供了自动朗读支持。

  • TextToSpeech(Context context, TextToSpeech.OnInitListener listener)
  • setLanguage(Locale loc)。如果调用setLanguage(Locale loc)的返回值是 TextToSpeech.LANG_COUNTRY_AVAILABLE 则说明当前TTS系统可以支持所设置的语言、国家选项。
  • speak(String text, int queueMode, HashMap<String,String> params)
  • synthesizeToFile(String text, HashMap<String,String> params, String filename)
  • TextToSpeech.QUEUE_FLUSH
  • TextToSpeech.QUEUE_ADD


归纳起来,使用TextToSpeech引擎的步骤如下:
(1)创建TextToSpeech对象,创建时传入OnInitListener监听器监听创建是否成功。
(2)设置TextToSpeech所使用语言、国家选项,通过返回值判断TTS是否支持该语言、国家选项。
(3)调用speak或synthesizeToFile方法。
(4)关闭TTS,释放资源。
src\org\crazyit\io\Speech.java
package org.crazyit.io;

import java.util.Locale;

import android.app.Activity;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

/**
 * Description:
 * <br/>site: <a href="http://www.crazyit.org">crazyit.org</a>
 * <br/>Copyright (c), 2001-2014, F.L
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author F.L@126.com
 * @version 1.0
*/
public class Speech extends Activity
{
	TextToSpeech tts;
	EditText editText;
	Button speech;
	Button record;
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 初始化TextToSpeech对象
		tts = new TextToSpeech(this, new OnInitListener()
		{
			@Override
			public void onInit(int status)
			{
				// 如果装载TTS成功
				if ( status == TextToSpeech.SUCCESS)
				{
					// 设置使用英式英语朗读
					int result = tts.setLanguage(Locale.UK);
					// 如果不支持所设置的语言
					if ( result != TextToSpeech.LANG_COUNTRY_AVAILABLE
						&& result != TextToSpeech.LANG_AVAILABLE)
					{
						Toast.makeText(Speech.this
							, "TTS暂时不支持这种语言的朗读。", Toast.LENGTH_LONG)
							.show();
						}
					}
				}
		});
		editText = (EditText) findViewById(R.id.txt);
		speech = (Button) findViewById(R.id.speech);
		record = (Button) findViewById(R.id.record);
		speech.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View arg0)
			{
				// 执行朗读
				tts.speak(editText.getText().toString(),
					TextToSpeech.QUEUE_ADD, null);
			}
		});
		record.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View arg0)
			{
				// 将朗读文本的音频记录到指定文件
				tts.synthesizeToFile(editText.getText().toString()
					, null, "/mnt/sdcard/sound.wav");
				Toast.makeText(Speech.this, "声音记录成功!"
						, Toast.LENGTH_LONG).show();
				}
			});
	}
	@Override
	public void onDestroy()
	{
		// 关闭TextToSpeec对象
		if ( tts != null)
		{
			tts.shutdown();
		}
	}		
}


res\layout\main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:gravity="center_horizontal"><EditText 
	android:id="@+id/txt"
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content" 
	android:lines="5"
	/><LinearLayout
	android:orientation="horizontal"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:gravity="center_horizontal">	<Button 
	android:id="@+id/speech"
	android:layout_width="wrap_content" 
	android:layout_height="wrap_content" 
	android:text="@string/speech"
	/><Button 
	android:id="@+id/record"
	android:layout_width="wrap_content" 
	android:layout_height="wrap_content" 
	android:text="@string/record"
	/>	</LinearLayout>	</LinearLayout>


res\values\strings.xml
<?xml version="1.0" encoding="utf-8"?><resources><string name="hello">Hello World, Speek!</string><string name="app_name">自动朗读</string><string name="speech">朗读</string><string name="record">记录声音</string></resources>



AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest
	xmlns:android="http://schemas.android.com/apk/res/android"
	package="org.crazyit.io"
	android:versionCode="1"
	android:versionName="1.0"><uses-sdk
		android:minSdkVersion="10"
		android:targetSdkVersion="17" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><application
		android:icon="@drawable/ic_launcher"
		android:label="@string/app_name"><activity
			android:name=".Speech"
			android:label="@string/app_name"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



java web开发 高并发处理

$
0
0
java web开发 高并发处理

java 高并发
java处理高并发高负载类网站中数据库的设计方法(java教程,java处理大量数据,java高负载数据)

一:高并发高负载类网站关注点之数据库

没错,首先是数据库,这是大多数应用所面临的首个SPOF。尤其是Web2.0的应用,数据库的响应是首先要解决的。
一般来说MySQL是最常用的,可能最初是一个mysql主机,当数据增加到100万以上,那么,MySQL的效能急剧下降。常用的优化措施是M-S(主-从)方式进行同步复制,将查询和操作和分别在不同的服务器上进行操作。我推荐的是M-M-Slaves方式,2个主Mysql,多个Slaves,需要注意的是,虽然有2个Master,但是同时只有1个是Active,我们可以在一定时候切换。之所以用2个M,是保证M不会又成为系统的SPOF。
Slaves可以进一步负载均衡,可以结合LVS,从而将select操作适当的平衡到不同的slaves上。
以上架构可以抗衡到一定量的负载,但是随着用户进一步增加,你的用户表数据超过1千万,这时那个M变成了SPOF。你不能任意扩充Slaves,否则复制同步的开销将直线上升,怎么办?我的方法是表分区,从业务层面上进行分区。最简单的,以用户数据为例。根据一定的切分方式,比如id,切分到不同的数据库集群去。

全局数据库用于meta数据的查询。缺点是每次查询,会增加一次,比如你要查一个用户nightsailer,你首先要到全局数据库群找到nightsailer对应的cluster id,然后再到指定的cluster找到nightsailer的实际数据。
每个cluster可以用m-m方式,或者m-m-slaves方式。这是一个可以扩展的结构,随着负载的增加,你可以简单的增加新的mysql cluster进去。

需要注意的是:
1、禁用全部auto_increment的字段
2、id需要采用通用的算法集中分配
3、要具有比较好的方法来监控mysql主机的负载和服务的运行状态。如果你有30台以上的mysql数据库在跑就明白我的意思了。
4、不要使用持久性链接(不要用pconnect),相反,使用sqlrelay这种第三方的数据库链接池,或者干脆自己做,因为php4中mysql的链接池经常出问题。

二:高并发高负载网站的系统架构之HTML静态化

其实大家都知道,效率最高、消耗最小的就是纯静态化 http://www.ablanxue.com/shtml/201207/776.shtml的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是 最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点 的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限 管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。
  
  除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。
  
   同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛 中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这 部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求高并发。
  

网站HTML静态化解决方案
当一个Servlet资源请求到达WEB服务器之后我们会填充指定的JSP页面来响应请求:

HTTP请求---Web服务器---Servlet--业务逻辑处理--访问数据--填充JSP--响应请求

HTML静态化之后:

HTTP请求---Web服务器---Servlet--HTML--响应请求

静态访求如下

Servlet:

public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    if(request.getParameter("chapterId") != null){
        String chapterFileName = "bookChapterRead_"+request.getParameter("chapterId")+".html";
        String chapterFilePath = getServletContext().getRealPath("/") + chapterFileName;
        File chapterFile = new File(chapterFilePath);
        if(chapterFile.exists()){response.sendRedirect(chapterFileName);return;}//如果有这个文件就告诉浏览器转向 
        INovelChapterBiz novelChapterBiz = new NovelChapterBizImpl();
        NovelChapter novelChapter = novelChapterBiz.searchNovelChapterById(Integer.parseInt(request.getParameter("chapterId")));//章节信息 
        int lastPageId = novelChapterBiz.searchLastCHapterId(novelChapter.getNovelId().getId(), novelChapter.getId());
        int nextPageId = novelChapterBiz.searchNextChapterId(novelChapter.getNovelId().getId(), novelChapter.getId());
        request.setAttribute("novelChapter", novelChapter);
        request.setAttribute("lastPageId", lastPageId);
        request.setAttribute("nextPageId", nextPageId);
        new CreateStaticHTMLPage().createStaticHTMLPage(request, response, getServletContext(), 
                chapterFileName, chapterFilePath, "/bookRead.jsp");
    }
}
生成HTML静态页面的类:

package com.jb.y2t034.thefifth.web.servlet;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
/**
* 创建HTML静态页面
* 功能:创建HTML静态页面
* 时间:2009年1011日
* 地点:home
* @author mavk
*
*/
public class CreateStaticHTMLPage {
    /**
     * 生成静态HTML页面的方法
     * @param request 请求对象
     * @param response 响应对象
     * @param servletContext Servlet上下文
     * @param fileName 文件名称
     * @param fileFullPath 文件完整路径
     * @param jspPath 需要生成静态文件的JSP路径(相对即可)
     * @throws IOException
     * @throws ServletException
     */
    public void createStaticHTMLPage(HttpServletRequest request, HttpServletResponse response,ServletContext servletContext,String fileName,String fileFullPath,String jspPath) throws ServletException, IOException{
        response.setContentType("text/html;charset=gb2312");//设置HTML结果流编码(即HTML文件编码) 
        RequestDispatcher rd = servletContext.getRequestDispatcher(jspPath);//得到JSP资源 
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();//用于从ServletOutputStream中接收资源 
        final ServletOutputStream servletOuputStream = new ServletOutputStream(){//用于从HttpServletResponse中接收资源 
            public void write(byte[] b, int off,int len){
                byteArrayOutputStream.write(b, off, len);
            }
            public void write(int b){
                byteArrayOutputStream.write(b);
            }
        };
        final PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));//把转换字节流转换成字符流 
        HttpServletResponse httpServletResponse = new HttpServletResponseWrapper(response){//用于从response获取结果流资源(重写了两个方法) 
            public ServletOutputStream getOutputStream(){
                return servletOuputStream;
            }
            public PrintWriter getWriter(){
                return printWriter;
            }
        };
        rd.include(request, httpServletResponse);//发送结果流 
        printWriter.flush();//刷新缓冲区,把缓冲区的数据输出 
        FileOutputStream fileOutputStream = new FileOutputStream(fileFullPath);
        byteArrayOutputStream.writeTo(fileOutputStream);//把byteArrayOuputStream中的资源全部写入到fileOuputStream中 
        fileOutputStream.close();//关闭输出流,并释放相关资源 
        response.sendRedirect(fileName);//发送指定文件流到客户端 
    }
}


三:高并发高负载类网站关注点之缓存、负载均衡、存储

缓存是另一个大问题,我一般用memcached来做缓存集群,一般来说部署10台左右就差不多(10g内存池)。需要注意一点,千万不能用使用
swap,最好关闭linux的swap。


负载均衡/加速

可能上面说缓存的时候,有人第一想的是页面静态化,所谓的静态html,我认为这是常识,不属于要点了。页面的静态化随之带来的是静态服务的
负载均衡和加速。我认为Lighttped+Squid是最好的方式了。
LVS <------->lighttped====>squid(s) ====lighttpd

上面是我经常用的。注意,我没有用apache,除非特定的需求,否则我不部署apache,因为我一般用php-fastcgi配合lighttpd,
性能比apache+mod_php要强很多。

squid的使用可以解决文件的同步等等问题,但是需要注意,你要很好的监控缓存的命中率,尽可能的提高的90%以上。
squid和lighttped也有很多的话题要讨论,这里不赘述。


存储
存储也是一个大问题,一种是小文件的存储,比如图片这类。另一种是大文件的存储,比如搜索引擎的索引,一般单文件都超过2g以上。
小文件的存储最简单的方法是结合lighttpd来进行分布。或者干脆使用Redhat的GFS,优点是应用透明,缺点是费用较高。我是指
你购买盘阵的问题。我的项目中,存储量是2-10Tb,我采用了分布式存储。这里要解决文件的复制和冗余。
这样每个文件有不同的冗余,这方面可以参考google的gfs的论文。
大文件的存储,可以参考nutch的方案,现在已经独立为hadoop子项目。(你可以google it)

其他:
此外,passport等也是考虑的,不过都属于比较简单的了。
四:高并发高负载网站的系统架构之图片服务器分离
大家知道,对于Web 服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他 们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用 服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule, 保证更高的系统消耗和执行效率。


利用Apache实现图片服务器的分离
缘由:
起步阶段的应用,都可能部署在一台服务器上(费用上的原因)
第一个优先分离的,肯定是数据库和应用服务器。
第二个分离的,会是什么呢?各有各的考虑,我所在的项目组重点考虑的节约带宽,服务器性能再好,带宽再高,并发来了,也容易撑不住。因此,我这篇文章的重点在这里。这里重点是介绍实践,不一定符合所有情况,供看者参考吧,
环境介绍:
WEB应用服务器:4CPU双核2G, 内存4G
  部署:Win2003/Apache Http Server 2.1/Tomcat6
数据库服务器:4CPU双核2G, 内存4G
  部署:Win2003/MSSQL2000
步骤:
步骤一:增加2台配置为:2CPU双核2G,内存2G普通服务器,做资源服务器
  部署:Tomcat6,跑了一个图片上传的简单应用,(记得指定web.xml的<distributable/>),并指定域名为res1.***.com,res2.***.com,采用ajp协议
步骤二:修改Apache httpd.conf配置
  原来应用的文件上传功能网址为:
   1、/fileupload.html
   2、/otherupload.html
  在httpd.conf中增加如下配置

<VirtualHost *:80> 
  ServerAdmin webmaster@***.com 
  ProxyPass /fileupload.html balancer://rescluster/fileupload lbmethod=byrequests stickysession=JSESSIONID nofailover=Off timeout=5 maxattempts=3    
  ProxyPass /otherupload.html balancer://rescluster/otherupload.html lbmethod=byrequests stickysession=JSESSIONID nofailover=Off timeout=5 maxattempts=3    
  #<!--负载均衡--> 
  <Proxy balancer://rescluster/> 
    BalancerMember ajp://res1.***.com:8009 smax=5 max=500 ttl=120 retry=300 loadfactor=100 route=tomcat1 
    BalancerMember ajp://res2.***.com:8009 smax=5 max=500 ttl=120 retry=300 loadfactor=100 route=tomcat2 
  </Proxy> 
 
</VirtualHost>
步骤三,修改业务逻辑:
  所有上传文件在数据库中均采用全url的方式保存,例如产品图片路径存成:http://res1.***.com/upload/20090101/product120302005.jpg

现在,你可以高枕无忧了,带宽不够时,增加个几十台图片服务器,只需要稍微修改一下apache的配置文件,即可。

五:高并发高负载网站的系统架构之数据库集群和库表散列

大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。
  
  在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。
  
   上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并 且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者 功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的 架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系 统随时增加一台低成本的数据库进来补充系统性能。


集群软件的分类:
一般来讲,集群软件根据侧重的方向和试图解决的问题,分为三大类:高性能集群(High performance cluster,HPC)、负载均衡集群(Load balance cluster, LBC),高可用性集群(High availability cluster,HAC)。
高性能集群(High performance cluster,HPC),它是利用一个集群中的多台机器共同完成同一件任务,使得完成任务的速度和可靠性都远远高于单机运行的效果。弥补了单机性能上的不足。该集群在天气预报、环境监控等数据量大,计算复杂的环境中应用比较多;
负载均衡集群(Load balance cluster, LBC),它是利用一个集群中的多台单机,完成许多并行的小的工作。一般情况下,如果一个应用使用的人多了,那么用户请求的响应时间就会增大,机器的性能也会受到影响,如果使用负载均衡集群,那么集群中任意一台机器都能响应用户的请求,这样集群就会在用户发出服务请求之后,选择当时负载最小,能够提供最好的服务的这台机器来接受请求并相应,这样就可用用集群来增加系统的可用性和稳定性。这类集群在网站中使用较多;
高可用性集群(High availability cluster,HAC),它是利用集群中系统 的冗余,当系统中某台机器发生损坏的时候,其他后备的机器可以迅速的接替它来启动服务,等待故障机的维修和返回。最大限度的保证集群中服务的可用性。这类系统一般在银行,电信服务这类对系统可靠性有高的要求的领域有着广泛的应用。
2 数据库集群的现状
数据库集群是将计算机集群技术引入到数据库中来实现的,尽管各厂商宣称自己的架构如何的完美,但是始终不能改变Oracle当先,大家追逐的事实,在集群的解决方案上Oracle RAC还是领先于包括微软在内的其它数据库厂商,它能满足客户高可用性、高性能、数据库负载均衡和方便扩展的需求。
Oracle’s Real Application Cluster (RAC)
Microsoft SQL Cluster Server (MSCS)
IBM’s DB2 UDB High Availability Cluster(UDB)
Sybase ASE High Availability Cluster (ASE)
MySQL High Availability Cluster (MySQL CS)
基于IO的第三方HA(高可用性)集群
当前主要的数据库集群技术有以上六大类,有数据库厂商自己开发的;也有第三方的集群公司开发的;还有数据库厂商与第三方集群公司合作开发的,各类集群实现的功能及架构也不尽相同。
RAC(Real Application Cluster,真正应用集群)是Oracle9i数据库中采用的一项新技术,也是Oracle数据库支持网格计算环境的核心技术。它的出现解决了传统数据库应用中面临的一个重要问题:高性能、高可伸缩性与低价格之间的矛盾。在很长一段时间里,甲骨文都以其实时应用集群技术(Real Application Cluster,RAC)统治着集群数据库市场

六:高并发高负载网站的系统架构之缓存

缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
  架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
   网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大 型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多 了,.net不是很熟悉,相信也肯定有。



Java开源缓存框架
JBossCache/TreeCache JBossCache是一个复制的事务处理缓存,它允许你缓存企业级应用数据来更好的改善性能。缓存数据被自动复制,让你轻松进行Jboss服务器之间的集群工作。JBossCache能够通过Jboss应用服务或其他J2EE容器来运行一个Mbean服务,当然,它也能独立运行。 JBossCache包括两个模块:TreeCache和TreeCacheAOP。 TreeCache --是一个树形结构复制的事务处理缓存。 TreeCacheAOP --是一个“面向对象”缓存,它使用AOP来动态管理POJO
OSCache OSCache标记库由OpenSymphony设计,它是一种开创性的JSP定制标记应用,提供了在现有JSP页面之内实现快速内存缓冲的功能。OSCache是个一个广泛采用的高性能的J2EE缓存框架,OSCache能用于任何Java应用程序的普通的缓存解决方案。OSCache有以下特点:缓存任何对象,你可以不受限制的缓存部分jsp页面或HTTP请求,任何java对象都可以缓存。 拥有全面的API--OSCache API给你全面的程序来控制所有的OSCache特性。 永久缓存--缓存能随意的写入硬盘,因此允许昂贵的创建(expensive-to-create)数据来保持缓存,甚至能让应用重启。 支持集群--集群缓存数据能被单个的进行参数配置,不需要修改代码。 缓存记录的过期--你可以有最大限度的控制缓存对象的过期,包括可插入式的刷新策略(如果默认性能不需要时)。
JCACHE JCACHE是一种即将公布的标准规范(JSR 107),说明了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的一致性等。它可被用于缓存JSP内最经常读取的数据,如产品目录和价格列表。利用JCACHE,多数查询的反应时间会因为有缓存的数据而加快(内部测试表明反应时间大约快15倍)。
Ehcache Ehcache出自Hibernate,在Hibernate中使用它作为数据缓存的解决方案。
Java Caching System JCS是Jakarta的项目Turbine的子项目。它是一个复合式的缓冲工具。可以将对象缓冲到内存、硬盘。具有缓冲对象时间过期设定。还可以通过JCS构建具有缓冲的分布式构架,以实现高性能的应用。 对于一些需要频繁访问而每访问一次都非常消耗资源的对象,可以临时存放在缓冲区中,这样可以提高服务的性能。而JCS正是一个很好的缓冲工具。缓冲工具对于读操作远远多于写操作的应用性能提高非常显著。
SwarmCache SwarmCache是一个简单而功能强大的分布式缓存机制。它使用IP组播来有效地在缓存的实例之间进行通信。它是快速提高集群式Web应用程序的性能的理想选择。
ShiftOne ShiftOne Object Cache这个Java库提供了基本的对象缓存能力。实现的策略有先进先出(FIFO),最近使用(LRU),最不常使用(LFU)。所有的策略可以最大化元素的大小,最大化其生存时间。
WhirlyCache Whirlycache是一个快速的、可配置的、存在于内存中的对象的缓存。它能够通过缓存对象来加快网站或应用程序的速度,否则就必须通过查询数据库或其他代价较高的处理程序来建立。
Jofti Jofti可对在缓存层中(支持EHCache,JBossCache和OSCache)的对象或在支持Map接口的存储结构中的对象进行索引与搜索。这个框架还为对象在索引中的增删改提供透明的功能同样也为搜索提供易于使用的查询功能。
cache4j cache4j是一个有简单API与实现快速的Java对象缓存。它的特性包括:在内存中进行缓存,设计用于多线程环境,两种实现:同步与阻塞,多种缓存清除策略:LFU, LRU, FIFO,可使用强引用(strong reference)与软引用(soft reference)存储对象。
Open Terracotta 一个JVM级的开源群集框架,提供:HTTP Session复制,分布式缓存,POJO群集,跨越群集的JVM来实现分布式应用程序协调(采用代码注入的方式,所以你不需要修改任何)。
sccache SHOP.COM使用的对象缓存系统。sccache是一个in-process cache和二级、共享缓存。它将缓存对象存储到磁盘上。支持关联Key,任意大小的Key和任意大小的数据。能够自动进行垃圾收集。
Shoal Shoal是一个基于Java可扩展的动态集群框架,能够为构建容错、可靠和可用的Java应用程序提供了基础架构支持。这个框架还可以集成到不希望绑定到特定通信协议,但需要集群和分布式系统支持的任何Java产品中。Shoal是GlassFish和JonAS应用服务器的集群引擎。
Simple-Spring-Memcached Simple-Spring-Memcached,它封装了对MemCached的调用,使MemCached的客户端开发变得超乎寻常的简单。

已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



【专访李嘉诚之五】下一个大机会

$
0
0

【专访李嘉诚之五】下一个大机会

2014年03月05日 18:11 来源于 财新网
“全球都要面对一个现实问题,就是科技对劳动力带来的挑战”
【专访李嘉诚之五】下一个大机会 - 反思 - 湖南谭颂德博客 2013年岁尾,香港,李嘉诚在办公室接受财新记者专访。杜江/特约摄影记者

   【财新网】(记者 胡舒立 王烁 特派香港记者 王端)

   财新记者:过去几十年,香港地产是一个大机会,然后全球化是一个大机会,中国市场爆发是第三个大机会,这三个机会你全都抓住了,下一个大机会是什么?

   李嘉诚:无论美国,以至全球,都要面对一个现实问题,就是科技对劳动力带来的挑战。基层劳工的边际劳动力可以是零的。

  每间公司都花尽心思提高竞争力,用科技、用数据开源,那么如何节流?随着产业机械自动化,劳动力需要越来越少,一些工种的失业情况只会持续增加,这是我们要高度重视的问题。惟一出路就是靠投资教育,推动改革,培养下一代懂得解决问题、懂得沟通等计算机不能取代的能力。

  产业有两种,传统事业还是会有的。至于新的科技,你一定要马上抓住。以制造业“改进过程”为例,有了科技,生产过程只会不断优化,对未来的制造业带来很大变化。你看这个杯子,现在是用自动化技术制造出来的,若加上人工智能技术,等于在制造过程中注入“思想”,它能够告诉你如何改善生产。

  从前工资便宜,土地便宜,现在未必是绝对有利的条件。以中国的制造业来讲,现时面对不少挑战。

  比如,最近出现一种儿童用的手表手机(Myfilip),跟手机互通的,生产过程只在办公室里进行,180万元的投入资本,就可满足1万个产品的需要,根本不需要为了降低劳工成本而在中国设厂生产,因为所用的工人不多。

  再比如说,我有部分投资的一间以色列公司Kaiima,发展了一种农业新技术,用一样的水,一样的土,农产量可以增加30%,并没有使用转基因技术。这个不得了!我原本打算帮中国争取五年的先机专利,帮助农业增产,但可惜最终没有成功。

  在以色列,当地的土壤不太好,当地人就说,“土地不能滋润我们,我们就用脑袋滋润土地。”

  我是一个傻气的人,如果没有傻气,不会办汕头大学一办30年。如果为了赚钱,为了名跟利,要我鞠躬屈膝,我不肯的;但如果为了自己的基金,教育、医疗,或者民族大义,我就都可以做。■

Phoenix——向HBase发送标准SQL成为现实

$
0
0

写在前面一:

本文总结基于HBase的SQL查询系统——Salesforce phoenix


写在前面二:

环境说明:


一、什么是Phoenix

摘自官网:


Phoenix是一个提供hbase的sql操作的框架,Phoenix是构建在HBase之上的一个SQL中间层。Phoenix完全使用Java编写,代码位于GitHub上,并且提供了一个客户端可嵌入的JDBC驱动。对于简单的低延迟查询,其性能量级为毫秒;对于百万级别的行数来说,其性能量级为秒。Phoenix并不是像HBase那样
用于map-reduce job的,而是通过标准化的语言来访问HBase数据的。

Phoenix最值得关注的特性:
1、嵌入式的JDBC驱动,实现了大部分的java.sql接口,包括元数据API
2、可以通过多部行键或是键/值单元对列进行建模
3、完善的查询支持,可以使用多个谓词以及优化的扫描键
4、DDL支持:通过CREATE TABLE、DROP TABLE及ALTER TABLE来添加/删除列
5、版本化的模式仓库:当写入数据时,快照查询会使用恰当的模式
6、DML支持:用于逐行插入的UPSERT VALUES、用于相同或不同表之间大量数据传输的UPSERT SELECT、用于删除行的DELETE
7、通过客户端的批处理实现的有限的事务支持
8、单表——还没有连接,同时二级索引也在开发当中
9、紧跟ANSI SQL标准

二、 Phoenix原理

Phoenix基本原理是将一个对于HBase client来说比较复杂的查询转换成一系列Region Scan,结合coprocessor和custom  filter在多台Region Server上进行并行查询,汇总各个Scan结果。种种迹象表明,Phoenix应该不是个优化的OLAP系统,更像是一个用于简单单表查询,过滤,排序,检索的OLTP系统。 Phoenix 可以为我们目前大数据平台提供比较便捷的数据操作能力(直接用jdbc方式),性能比较不错,注意不要使用多表查询即可 。

三、Phoenix安装配置

1、下载
phoenix-3.0.0-incubating.tar.gz
http://phoenix.incubator.apache.org/download.html#Installation
2、拷贝phoenix-3.0.0-incubating/common/phoenix-core-3.0.0-incubating.jar至$HBASE_HOME/lib/下
3、重启HBase集群

若通过客户端访问phoenix,需进行以下操作:
把phoenix-3.0.0-incubating/hadoop-1/phoenix-3.0.0-incubating-client.jar添加到Phoenix客户端的类路劲下

四、访问 Phoenix

3.1、通过命令行

进入phoenix

cd /home/yujianxin/hbase/phoenix/phoenix-3.0.0-incubating/bin
./sqlline.py slave3:2181

出现以下响应,说明安装成功


3.2、通过Java

Class.forName("org.apache.phoenix.jdbc.PhoenixDriver"); 
// connection string: jdbc:phoenix [ :<zookeeper quorum> [ :<port number> ] [ :<root node> ] ]
Connection connection = DriverManager.getConnection("jdbc:phoenix:slave3:2181");


五、使用Phoenix

使用phoenix-3.0.0-incubating.tar.gz自带的数据进行测试


执行以上命令后,查看phoenix中的表:


查看HBase中的表,如下:


即可向HBase发送标准sql语句,通过Phoenix这个构建在HBase之上的SQL中间层对HBase进行操作。


作者:wl101yjx 发表于2014-5-31 23:09:54 原文链接
阅读:88 评论:0 查看评论

Java 8 特性 – 终极手册

$
0
0

原文链接,原文作者:Andrey Redko ,译者:Justin,校对:郭蕾

1.简介

毫无疑问,Java 8是自Java  5(2004年)发布以来Java语言最大的一次版本升级,Java 8带来了很多的新特性,比如编译器、Java库、工具和JVM(Java虚拟机)本身。在这篇教程中我们将会学习这些新特性,并通过真实案例演示他们在不同场景下的用法。

本教程由下面几部分组成,每部分都涉及到Java平台的细节:

  • 语言
  • 编译器
  • Java库
  • 工具
  • 运行时(Java虚拟机)

2.Java的新特性

总体来说,Java 8是一个大的版本升级。有人可能会说,Java 8的新特性非常令人期待,但是也要花费大量的时间去学习。这一节我们会讲到这些新特性。

2.1 Lambda表达式和函数式接口

Lambda表达式(也叫做闭包)是Java 8中最大的也是期待已久的变化。它允许我们将一个函数当作方法的参数(传递函数),或者说把代码当作数据,这是每个函数式编程者熟悉的概念。很多基于JVM平台的语言一开始就支持Lambda表达式,但是Java程序员没有选择,只能使用匿名内部类来替代Lambda表达式。

Lambda表达式的设计被讨论了很久,而且花费了很多的功夫来交流。不过最后取得了一个折中的办法,得到了一个新的简明并且紧凑的Lambda表达式结构。最简单的Lambda表达式可以用逗号分隔的参数列表、->符号和功能语句块来表示。示例如下:

Arrays.asList( "a""b""d"  ).forEach( e -> System.out.println( e ) );

请注意到编译器会根据上下文来推测参数的类型,或者你也可以显示地指定参数类型,只需要将类型包在括号里。举个栗子:

Arrays.asList( "a""b""d"  ).forEach( ( String e ) -> System.out.println( e ) );

如果lambda的功能语句块太复杂,我们可以用大括号包起来,跟普通的Java方法一样,栗子如下:

Arrays.asList( "a", "b", "d" ).forEach( e -&gt; {
System.out.print( e );
System.out.print( e );
} );

Lambda表达式可能会引用类的成员或者局部变量(会被隐式地转变成final类型),下面两种写法的效果是一样的:

String separator = “,”;
Arrays.asList( “a”, “b”, “d” ).forEach(
( String e ) -> System.out.print( e + separator ) );
和:

final String separator = “,”;
Arrays.asList( “a”, “b”, “d” ).forEach(
( String e ) -> System.out.print( e + separator ) );

Lambda表达式可能会有返回值,编译器会根据上下文推断返回值的类型。如果lambda的语句块只有一行,不需要return关键字。下面两个写法是等价的:

Arrays.asList( “a”, “b”, “d” ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );

和:

Arrays.asList( “a”, “b”, “d” ).sort( ( e1, e2 ) -> {
int result = e1.compareTo( e2 );
return result;
} );
语言的设计者们思考了很多如何让现有的功能和lambda表达式友好兼容。于是就有了函数接口这个概念。函数接口是一种只有一个方法的接口,像这样地,函数接口可以隐式地转换成lambda表达式。

java.lang.Runnable 和java.util.concurrent.Callable是函数接口两个最好的例子。但是在实践中,函数接口是非常脆弱的,只要有人在接口里添加多一个方法,那么这个接口就不是函数接口了,就会导致编译失败。Java 8提供了一个特殊的注解@FunctionalInterface来克服上面提到的脆弱性并且显示地表明函数接口的目的(java里所有现存的接口都已经加上了@FunctionalInterface)。让我们看看一个简单的函数接口定义:

@FunctionalInterface
public interface Functional {
void method();
}
我们要记住默认的方法和静态方法(下一节会具体解释)不会违反函数接口的约定,栗子如下:

@FunctionalInterface
public interface FunctionalDefaultMethods {
void method();

default void defaultMethod() {
}
}

支持Lambda是Java 8最大的卖点,他有巨大的潜力吸引越来越多的开发人员转到这个开发平台来,并且在纯Java里提供最新的函数式编程的概念。对于更多的细节,请参考 官方文档

2.2 接口的默认方法和静态方法

Java 8增加了两个新的概念在接口声明的时候:默认和静态方法。默认方法和Trait有些类似,但是目标不一样。默认方法允许我们在接口里添加新的方法,而不会破坏实现这个接口的已有类的兼容性,也就是说不会强迫实现接口的类实现默认方法。

默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供一个默认的方法实现,所有这个接口的实现类都会通过继承得倒这个方法(如果有需要也可以重写这个方法),让我们来看看下面的栗子:

private interface Defaulable {
// Interfaces now allow default methods, the implementer may or
// may not implement (override) them.
default String notRequired() {
return “Default implementation”;
}
}

private static class DefaultableImpl implements Defaulable {
}

private static class OverridableImpl implements Defaulable {
@Override
public String notRequired() {
return “Overridden implementation”;
}
}

接口Defaulable使用default关键字声明了一个默认方法notRequired(),类DefaultableImpl实现了Defaulable接口,没有对默认方法做任何修改。另外一个类OverridableImpl重写类默认实现,提供了自己的实现方法。

Java 8 的另外一个有意思的新特性是接口里可以声明静态方法,并且可以实现。栗子如下:

private interface DefaulableFactory {
// Interfaces now allow static methods
static Defaulable create( Supplier< Defaulable > supplier ) {
return supplier.get();
}
}

下面是把接口的静态方法和默认方法放在一起的示例(::new 是构造方法引用,后面会有详细描述):

public static void main( String[] args ) {
Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
System.out.println( defaulable.notRequired() );

defaulable = DefaulableFactory.create( OverridableImpl::new );
System.out.println( defaulable.notRequired() );
}

控制台的输出如下:

Default implementation
Overridden implementation

JVM平台的接口的默认方法实现是很高效的,并且方法调用的字节码指令支持默认方法。默认方法使已经存在的接口可以修改而不会影响编译的过程。java.util.Collection中添加的额外方法就是最好的例子: stream()parallelStream()forEach()removeIf()

虽然默认方法很强大,但是使用之前一定要仔细考虑是不是真的需要使用默认方法,因为在层级很复杂的情况下很容易引起模糊不清甚至变异错误。更多的详细信息请参考 官方文档

2.3   方法引用

方法引用提供了一个很有用的语义来直接访问类或者实例的已经存在的方法或者构造方法。结合Lambda表达式,方法引用使语法结构紧凑简明。不需要复杂的引用。

下面我们用 Car 这个类来做示例,Car这个类有不同的方法定义。让我们来看看java 8支持的4种方法引用。

public static class Car {
public static Car create( final Supplier< Car > supplier ) {
return supplier.get();
}

public static void collide( final Car car ) {
System.out.println( “Collided ” + car.toString() );
}

public void follow( final Car another ) {
System.out.println( “Following the ” + another.toString() );
}

public void repair() {
System.out.println( “Repaired ” + this.toString() );
}
}

第一种方法引用是构造方法引用,语法是: Class::new ,对于泛型来说语法是: Class< T >::new请注意构造方法没有参数

final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );

第二种方法引用是静态方法引用,语法是: Class::static_method请注意这个静态方法只支持一个类型为Car的参数。

cars.forEach( Car::collide );

第三种方法引用是类实例的方法引用,语法是: Class::method请注意方法没有参数。

cars.forEach( Car::repair );

最后一种方法引用是引用特殊类的方法,语法是: instance::method请注意只接受Car类型的一个参数。

final Car police = Car.create( Car::new );
cars.forEach( police::follow );

运行这些例子我们将会在控制台得到如下信息(Car的实例可能会不一样):  

Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

关于方法引用更多的示例和详细信息,请参考 官方文档

2.4   重复注释

自从Java 5支持注释以来,注释变得特别受欢迎因而被广泛使用。但是有一个限制,同一个地方的不能使用同一个注释超过一次。 Java 8打破了这个规则,引入了重复注释,允许相同注释在声明使用的时候重复使用超过一次。  

重复注释本身需要被@Repeatable注释。实际上,他不是一个语言上的改变,只是编译器层面的改动,技术层面仍然是一样的。让我们来看看栗子:

package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
public @interface Filters {
Filter[] value();
}

@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
@Repeatable( Filters.class )
public @interface Filter {
String value();
};

@Filter( “filter1″ )
@Filter( “filter2″ )
public interface Filterable {
}

public static void main(String[] args) {
for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
System.out.println( filter.value() );
}
}
}

我们可以看到,注释Filter被@Repeatable( Filters.class )注释。Filters 只是一个容器,它持有Filter, 编译器尽力向程序员隐藏它的存在。通过这样的方式,Filterable接口可以被Filter注释两次。

另外,反射的API提供一个新方法getAnnotationsByType() 来返回重复注释的类型(请注意Filterable.class.getAnnotation( Filters.class )将会返回编译器注入的Filters实例)。

程序的输出将会是这样:

filter1
filter2

更多详细信息请参考 官方文档

2.5   更好的类型推断

Java 8在类型推断方面改进了很多,在很多情况下,编译器可以推断参数的类型,从而保持代码的整洁。让我们看看栗子:

package com.javacodegeeks.java8.type.inference;

public class Value< T > {
public static< T > T defaultValue() {
return null;
}

public T getOrDefault( T value, T defaultValue ) {
return ( value != null ) ? value : defaultValue;
}
}

这里是Value< String >的用法

package com.javacodegeeks.java8.type.inference;

public class TypeInference {
public static void main(String[] args) {
final Value< String > value = new Value<>();
value.getOrDefault( “22″, Value.defaultValue() );
}
}

参数Value.defaultValue()的类型被编译器推断出来,不需要显式地提供类型。在java 7, 相同的代码不会被编译,需要写成:Value.< String >defaultValue()

2.6   扩展的注释支持

Java 8扩展了注释可以使用的范围,现在我们几乎可以在所有的地方:局部变量、泛型、超类和接口实现、甚至是方法的Exception声明。一些例子如下:

package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
public @interface NonEmpty {
}

public static class Holder< @NonEmpty T > extends @NonEmpty Object {
public void method() throws @NonEmpty Exception {
}
}

@SuppressWarnings( “unused” )
public static void main(String[] args) {
final Holder< String > holder = new @NonEmpty Holder< String >();
@NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();
}
}

Java 8 新增加了两个注释的程序元素类型 ElementType. TYPE_USE ElementType. TYPE_PARAMETER ,这两个新类型描述了可以使用注释的新场合。注释处理API( Annotation Processing API)也做了一些细微的改动,来识别这些新添加的注释类型。

3.Java编译器的新特性

3.1 参数名字

很长时间以来,Java程序员想尽办法把参数名字保存在java字节码里,并且让这些参数名字在运行时可用。Java 8 终于把这个需求加入到了Java语言(使用反射API和 Parameter.getName() 方法)和字节码里(使用java编译命令javac的 –parameters参数)。

package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNames {
public static void main(String[] args) throws Exception {
Method method = ParameterNames.class.getMethod( “main”, String[].class );
for( final Parameter parameter: method.getParameters() ) {
System.out.println( “Parameter: ” + parameter.getName() );
}
}
}

如果你编译这个class的时候没有添加参数 –parameters运行的时候你会得到这个结果:

Parameter: arg0

编译的时候添加了 –parameters参数的话,运行结果会不一样:

Parameter: args

对于有经验的Maven使用者,–parameters参数可以添加到maven-compiler-plugin的配置部分:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

最新版的 Eclipse Kepler SR2 提供了编译设置项,如下图所示:

http://a3ab771892fd198a96736e50.javacodegeeks.netdna-cdn.com/wp-content/uploads/2014/05/01.ECLIPSE-JAVA-COMPILER.png

Picture 1. Configuring Eclipse projects to support new Java 8 compiler  –parameters argument.

额外的,有一个方便的方法 Parameter.isNamePresent() 来验证参数名是不是可用。

 

4.Java  库的新特性

Java 8 新添加了很多类,并且扩展了很多现有的类来更好地支持现代并发、函数式编程、日期\时间等等。

4.1 Optional

著名的 NullPointerException 是引起系统失败最常见的原因。很久以前 Google Guava项目引入了 Optional作为解决空指针异常的一种方式,不赞成代码被null检查的代码污染,期望程序员写整洁的代码。受 Google Guava的鼓励, Optional 现在是Java 8库的一部分。

Optional 只是一个容器,它可以保存一些类型的值或者null。它提供很多有用的方法,所以没有理由不显式地检查null。请参照java 8的 文档查看详细信息。

让我们看看两个 Optional 用法的小例子:一个是允许为空的值,另外一个是不允许为空的值。

Optional< String > fullName = Optional.ofNullable( null );
System.out.println( “Full Name is set? ” + fullName.isPresent() );
System.out.println( “Full Name: ” + fullName.orElseGet( () -> “[none]” ) );
System.out.println( fullName.map( s -> “Hey ” + s + “!” ).orElse( “Hey Stranger!” ) );
如果Optional实例有非空的值,方法 isPresent() 返回true否则返回false。方法orElseGet提供了回退机制,当Optional的值为空时接受一个方法返回默认值。map()方法转化Optional当前的值并且返回一个新的Optional实例。orElse方法和orElseGet类似,但是它不接受一个方法,而是接受一个默认值。上面代码运行结果如下:

Full Name is set? false
Full Name: [none]
Hey Stranger!

让我们大概看看另外一个例子。

Optional< String > firstName = Optional.of( “Tom” );
System.out.println( “First Name is set? ” + firstName.isPresent() );
System.out.println( “First Name: ” + firstName.orElseGet( () -> “[none]” ) );
System.out.println( firstName.map( s -> “Hey ” + s + “!” ).orElse( “Hey Stranger!” ) );
System.out.println();

输出如下:

First Name is set? true
First Name: Tom
Hey Tom!

更多详细信息请参考 官方文档

4.2 Stream

新增加的Stream API ( java.util.stream)引入了在Java里可以工作的函数式编程。这是目前为止对java库最大的一次功能添加,想要通过允许程序员编写有效、整洁和简明的代码从而大大提高生产率。

Stream API让集合处理简化了很多(我们后面会看到不仅限于Java集合类)。让我们从一个简单的类Task开始来看看Stream的用法。

public class Streams {
private enum Status {
OPEN, CLOSED
};

private static final class Task {
private final Status status;
private final Integer points;

Task( final Status status, final Integer points ) {
this.status = status;
this.points = points;
}

public Integer getPoints() {
return points;
}

public Status getStatus() {
return status;
}

@Override
public String toString() {
return String.format( “[%s, %d]“, status, points );
}
}
}

Task有一个属性是点数(虚拟的任务复杂度),Task可以是开放的或者关闭的。让我们引入一个小的Task集合。

final Collection< Task > tasks = Arrays.asList(
new Task( Status.OPEN, 5 ),
new Task( Status.OPEN, 13 ),
new Task( Status.CLOSED, 8 )
);

第一个问题是所有的开放的Task的点数是多少?在java 8 之前,通常的做法是用foreach迭代。但是Java8里头我们会用Stream。Stream是多个元素的序列,支持串行和并行操作。

// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();

System.out.println( “Total points: ” + totalPointsOfOpenTasks );

控制台的输出将会是:

Total points: 18
上面代码执行的流程是这样的,首先Task集合会被转化为Stream表示,然后filter操作会过滤掉所有关闭的Task,接下来使用Task::getPoints 方法取得每个Task实例的点数,mapToInt方法会把Task Stream转换成Integer Stream,最后使用Sum方法将所有的点数加起来得到最终的结果。

在我们看下一个例子之前,我们要记住一些关于Stream的说明。Stream操作被分为中间操作和终点操作。

中间操作返回一个新的Stream。这些中间操作是延迟的,执行一个中间操作比如filter实际上不会真的做过滤操作,而是创建一个新的Stream,当这个新的Stream被遍历的时候,它里头会包含有原来Stream里符合过滤条件的元素。

终点操作比如说forEach或者sum会遍历Stream从而产生最终结果或附带结果。终点操作执行完之后,Stream管道就被消费完了,不再可用。在几乎所有的情况下,终点操作都是即时完成对数据的遍历操作。

Stream的另外一个价值是Stream创造性地支持并行处理。让我们看看下面这个例子,这个例子把所有task的点数加起来。

// Calculate total points of all tasks
final double totalPoints = tasks
.stream()
.parallel()
.map( task -> task.getPoints() ) // or map( Task::getPoints )
.reduce( 0, Integer::sum );

System.out.println( “Total points (all tasks): ” + totalPoints );

这个例子跟上面那个非常像,除了这个例子里使用了parallel()方法       并且计算最终结果的时候使用了reduce方法。

输出如下:

Total points (all tasks): 26.0
我们经常需要根据条件处理一组集合,Stream可以帮我们实现这个需求。例子如下:

// Group tasks by their status
final Map< Status, List< Task > > map = tasks
.stream()
.collect( Collectors.groupingBy( Task::getStatus ) );

System.out.println( map );

控制台的输出如下:

{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
最后一个例子,我们计算每个task的点数占所有task点数的比重

// Calculate the weight of each tasks (as percent of total points)
final Collection< String > result = tasks
.stream() // Stream< String >
.mapToInt( Task::getPoints ) // IntStream
.asLongStream() // LongStream
.mapToDouble( points -> points / totalPoints ) // DoubleStream
.boxed() // Stream< Double >
.mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
.mapToObj( percentage -> percentage + “%” ) // Stream< String>
.collect( Collectors.toList() ); // List< String >

System.out.println( result );

控制台输出如下:

[19%, 50%, 30%]

之前我们提到Stream API不仅仅可以用在Java集合上,从文本文件一行一行读取文本内容很容易从Stream操作中受益。例子如下;

final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
lines.onClose( () -> System.out.println(“Done!”) ).forEach( System.out::println );
}

Stream的方法 onClose 返回一个等价的有额外句柄的Stream,当Stream的close()方法被调用的时候这个句柄会被执行。

Stream API、Lambda表达式还有接口默认方法和静态方法支持的方法引用,是Java 8对软件开发的现代范式的响应。

 

4.3日期时间API(JSR310

 Java 8引入了新的日期时间API(JSR 310)改进了日期时间的管理。日期和时间管理一直是Java开发人员最痛苦的问题。java.util.Date和后来的java.util.Calendar一点也没有改变这个情况(甚至让人们更加迷茫)。

因为上面这些原因,产生了 Joda-Time ,可以替换Java的日期时间API。 Joda-Time深刻影响了 Java 8新的日期时间API,Java 8吸收了 Joda-Time的精华。新的java.time包包含了所有关于日期、时间、日期时间、时区、Instant(跟日期类似但精确到纳秒)、duration(持续时间)和时钟操作的类。设计这些API的时候很严肃地考虑了这些类的费克边性(从java.util.Calendar吸取的痛苦教训)。如果需要修改时间对象,会返回一个新的实例。

让我们看看一些关键的类和用法示例。第一个类是Clock,Clock使用时区来访问当前的instant, date和time。Clock类可以替换  System.currentTimeMillis() 和  TimeZone.getDefault().

// Get the system clock as UTC offset
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );

控制台输出如下:

2014-04-12T15:19:29.282Z
1397315969360

其他类我们看看LocalTime和LocalDate。LocalDate只保存有ISO-8601日期系统的日期部分,有时区信息,相应地,LocalTime只保存ISO-8601日期系统的时间部分,没有时区信息。LocalDate和LocalTime都可以从Clock对象创建。

// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );

System.out.println( date );
System.out.println( dateFromClock );

// Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );

System.out.println( time );
System.out.println( timeFromClock );

控制台输出如下:

2014-04-12
2014-04-12
11:25:54.568
15:25:54.568

LocalDateTime类合并了LocalDate和LocalTime,它保存有ISO-8601日期系统的日期和时间,但是没有时区信息。让我们看一个简单的例子。

// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );

输出如下:

2014-04-12T11:37:52.309
2014-04-12T15:37:52.309

如果您需要一个类持有日期时间和时区信息,可以使用ZonedDateTime,它保存有ISO-8601日期系统的日期和时间,而且有时区信息。让我们看一些例子:

// Get the zoned date/time
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( “America/Los_Angeles” ) );

System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );

输出如下:
2014-04-12T11:47:01.017-04:00[America/New_York]
2014-04-12T15:47:01.017Z
2014-04-12T08:47:01.017-07:00[America/Los_Angeles]

最后让我们看看Duration类,Duration持有的时间精确到纳秒。它让我们很容易计算两个日期中间的差异。让我们来看一下:

 

// Get duration between two dates
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );

final Duration duration = Duration.between( from, to );
System.out.println( “Duration in days: ” + duration.toDays() );
System.out.println( “Duration in hours: ” + duration.toHours() );

上面的例子计算了两个日期(2014年4月16日和2014年5月16日)之间的持续时间(基于天数和小时)输出如下:

Duration in days: 365
Duration in hours: 8783

对于Java 8的新日期时间的总体印象还是比较积极的。一部分是因为有经历实战的Joda-Time的基础,还有一部分是因为日期时间终于被认真对待而且听取了开发人员的声音。关于更多的详细信息,请参考 官方文档

 

4.4   Nashorn javascript引擎

Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。Nashorn javascript引擎只是javax.script.ScriptEngine另一个实现,而且规则也一样,允许Java和JavaScript互相操作。这里有个小栗子:

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( “JavaScript” );

System.out.println( engine.getClass().getName() );
System.out.println( “Result:” + engine.eval( “function f() { return 1; }; f() + 1;” ) );

 

输出如下:

jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2
4.5   Base64

对Base64的支持最终成了Java 8标准库的一部分,非常简单易用:

package com.javacodegeeks.java8.base64;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Base64s {
public static void main(String[] args) {
final String text = “Base64 finally in Java 8!”;

final String encoded = Base64
.getEncoder()
.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
System.out.println( encoded );

final String decoded = new String(
Base64.getDecoder().decode( encoded ),
StandardCharsets.UTF_8 );
System.out.println( decoded );
}
}

控制台输出的编码和解码的字符串

QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!

新的Base64API也支持URL和MINE的编码解码。

( Base64. getUrlEncoder() /  Base64. getUrlDecoder()Base64. getMimeEncoder() /  Base64. getMimeDecoder()).

 

4.6   并行数组

Java 8新增加了很多方法支持并行的数组处理。最重要的大概是 parallelSort()这个方法显著地使排序在多核计算机上速度加快。下面的小例子演示了这个心的方法家族( parallelXXX)的行为。

package com.javacodegeeks.java8.parallel.arrays;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

public class ParallelArrays {
public static void main( String[] args ) {
long[] arrayOfLong = new long [ 20000 ];

Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + ” ” ) );
System.out.println();

Arrays.parallelSort( arrayOfLong );
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + ” ” ) );
System.out.println();
}
}

这一小段代码使用 parallelSetAll() t方法填充这个长度是2000的数组,然后使用 parallelSort() 排序。这个程序输出了排序前和排序后的10个数字来验证数组真的已经被排序了。示例可能的输出如下(请注意这些数字是随机产生的)

Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793

4.7   并发

Java 8在 java.util.concurrent.ConcurrentHashMap新添加了方法来支持聚集操作。这些聚集操作基于Java 8新引入的Stream和lambda表达式。  java.util.concurrent.ForkJoinPool也添加了新的方法来支持Common Pool(Common pool可以被所有的ForkAndJoinTask共用)

Java8新添加的类  java.util.concurrent.locks.StampedLock提供基于能力的锁,这个锁提供三种模式来对对鞋进行访问控制(可能可以考虑用它来替换不太有名的  java.util.concurrent.locks.ReadWriteLock)。

在 java.util.concurrent.atomic里也添加了下面一些新的类

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

 

5         新的工具

Java 8 提供了一些新的命令行工具,这一章我们会粗略地看一下这些工具有意思的地方。

5.1  Nashorn引擎:jjs

jjs是一个基于标准的Noshorn引擎,它接受javascript文件列表作为参数。作为例子,让我们创建一个func.js, 内容如下:

function f() {
return 1;
};

print( f() + 1 );

运行这个文件,把这个文件作为参数传给jjs

jjs func.js

输出结果如下

2

更多的详细信息请参考 官方文档

 

5.2 类依赖分析工具:jdeps

Jdeps是一个功能强大的命令行工具,它可以帮我们显示出包层级或者类层级java类文件的依赖关系。它接受class文件、目录、jar文件作为输入,默认情况下,jdeps会输出到控制台。

作为例子,让我们看看现在很流行的Spring框架的库的依赖关系报告。为了让报告短一些,我们只分析一个jar: org.springframework.core-3.0.5.RELEASE.jar.

jdeps org.springframework.core-3.0.5.RELEASE.jar

这个命令输出内容很多,我们只看其中的一部分,这些依赖关系根绝包来分组,如果依赖关系在classpath里找不到,就会显示not found.

org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
-> java.io
-> java.lang
-> java.lang.annotation
-> java.lang.ref
-> java.lang.reflect
-> java.util
-> java.util.concurrent
-> org.apache.commons.logging not found
-> org.springframework.asm not found
-> org.springframework.asm.commons not found
org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
-> java.lang
-> java.lang.annotation
-> java.lang.reflect
-> java.util

更多的详细信息请参考 官方文档

6         JVM的新特性

JVM内存永久区已经被metaspace替换(JEP 122)。JVM参数  -XX:PermSize 和 - XX:MaxPermSizeXX:MetaSpaceSize 和  -XX:MaxMetaspaceSize替换。

7         结论

未来就在这里,Java8通过提供新的特性让开发人员提高生产率从而推动Java这个平台前进。现在将生产系统移植到Java还太早,但是接下来的几个月它的采用率会慢慢地增长。不管怎么样,现在是开始准备让你的代码兼容Java 8,准备好使用Java 8,当Java 8已经被证实足够安全和稳定就可以使用了。

作为社区对Java 8的支持,Pivotal发布了Spring 4.0.3, 支持Java 8。

 8         资源

更多的一些资源更深入地讨论了Java 8的一些特性:

 

(全文完)如果您喜欢此文请点赞,分享,评论。



您可能感兴趣的文章

Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>