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

大数据预测:4个特征,11个典型行业

$
0
0

本文作者罗超 编辑 reMake

世界杯期间各家科技巨头利用大数据预测比赛结果,再现“章鱼保罗”雄风。世界杯结束了但大数据预测还会继续。从夜观天象到气象预报,从童话里的水晶球到今日的科技预言家,从地震云的传说再到科学家猛攻的地震预测,人类一直希望能够更早突破局限看穿未来。随着信息革命的深入,大数据时代的预测更加容易,人类的生活正在被大数据预测深刻改变。

预测是大数据核心价值

人们在谈论大数据的采集、存储和挖掘时,最常见的应用案例便是“预测股市”“预测流感”“预测消费者行为”,预测性分析是大数据最核心的功能。

大数据还拥有数据可视化和大数据挖掘的功能,对已发生的信息价值进行挖掘并辅助决策。传统的数据分析挖掘在做相似的事情,只不过效率会低一些或者说挖掘的深度、广度和精度不够。大数据预测则是基于大数据和预测模型去预测未来某件事情的概率。让分析从“面向已经发生的过去”转向“面向即将发生的未来”是大数据与传统数据分析的最大不同。

大数据预测的逻辑基础是,每一种非常规的变化事前一定有征兆,每一件事情都有迹可循,如果找到了征兆与变化之间的规律,就可以进行预测。大数据预测无法确定某件事情必然会发生,它更多是给出一个概率。

从天气预报看大数据预测的四个条件

在互联网之前便已经有基于大数据的预测分析了:天气预报。因为互联网,天气预报为代表的大数据预测的以下几个特征在更多领域得到体现。

1、大数据预测的时效性。天气预报粒度从天缩短到小时,有严苛的时效要求,基于海量数据通过传统方式进行计算,得出结论时明天早已到来,预测并无价值。其他领域的大数据预测应用特征对“时效性”有更高要求,譬如股市、实时定价,而云计算、分布式计算和超级计算机的发展则提供了这样的高速计算能力。

2、大数据预测的数据源。天气预报需要收集海量气象数据,气象卫星、气象站台负责收集,但整套系统的部署和运维耗资巨大。在互联网之前鲜有领域具备这样的数据收集能力。WEB1.0为中心化信息产生、WEB2.0为社会化创造、移动互联网则是随时随地、社会化和多设备的数据上传,每一次演化数据收集的成本都大幅降低,范围和规模则大幅扩大。大数据被引爆的同时,大数据预测所需数据源不再是问题。

3、大数据预测的动态性。不同时点的计算因子动态变化,任何变量都会引发整个系统变化,甚至产生蝴蝶效应。如果某个变量对结果起决定性作用且难以捕捉,预测难上加难,譬如人为因素。大数据预测的应用场景大都是极不稳定的领域但有固定规律,譬如天气、股市、疾病。这需要预测系统对每一个变量数据的精准捕捉,并接近实时地调整预测。发达的传感器网络外加大数据计算能力让上述两点更加容易。

4、大数据预测的规律性。大数据预测与传统的基于抽样的预测不同之处在于,其基于海量历史数据和实时动态数据,发现数据与结果之间的规律,并假设此规律会延续,捕捉到变量之后进行预测。一个领域本身便有相对稳定的规律,大数据预测才有机会得到应用。古人夜观天象就说明天气是由规律可循的,因此气象预报最早得到应用。反面案例则是规律难以捉摸,数据源收集困难的地震预测,还有双色球彩票。

大数据预测的典型应用领域

互联网给大数据预测应用的普及带来了便利条件。天气预报之外,还有哪些领域正在或者可能被大数据预测所改变呢?结合国内外案例来看,以下11个领域是最有机会的大数据预测应用领域。 

1、体育赛事预测

世界杯期间,谷歌、百度、微软和高盛等公司都推出了比赛结果预测平台。百度预测结果最为亮眼,预测全程 64 场比赛,准确率为 67%,进入淘汰赛后准确率为 94%。现在互联网公司取代章鱼保罗试水赛事预测也意味着未来的体育赛事会被大数据预测所掌控。

Google 世界杯预测基于 Opta Sports 的海量赛事数据来构建其最终的预测模型。百度则是搜索过去 5 年内全世界 987 支球队(含国家队和俱乐部队)的 3.7 万场比赛数据,同时与中国彩票网站乐彩网、欧洲必发指数数据供应商 Spdex 进行数据合作,导入博彩市场的预测数据,建立了一个囊括 199972 名球员和 1.12 亿条数据的预测模型,并在此基础上进行结果预测。

从互联网公司的成功经验来看,只要有体育赛事历史数据,并且与指数公司进行合作,便可以进行其他赛事的预测,譬如欧冠、NBA等赛事。

2、股票市场预测

去年英国华威商学院和美国波士顿大学物理系的研究发现,用户通过谷歌搜索的金融关键词或许可以金融市场的走向,相应的投资战略收益高达 326%。此前则有专家尝试通过 Twitter 博文情绪来预测股市波动。

理论上来讲股市预测更加适合美国。中国股票市场无法做到双向盈利,只有股票涨才能盈利,这会吸引一些游资利用信息不对称等情况人为改变股票市场规律,因此中国股市没有相对稳定的规律则很难被预测,且一些对结果产生决定性影响的变量数据根本无法被监控。

3、市场物价预测

CPI 表征已经发生的物价浮动情况,但统计局数据并不权威。但大数据则可能帮助人们了解未来物价走向,提前预知通货膨胀或经济危机。最典型的案例莫过于马云通过阿里 B2B 大数据提前知晓亚洲金融危机,当然这是阿里数据团队的功劳。

单个商品的价格预测更加容易,尤其是机票这样的标准化产品,去哪儿提供的“机票日历”就是价格预测,告知你几个月后机票的大概价位。商品的生产、渠道成本和大概毛利在充分竞争的市场中是相对稳定的,与价格相关的变量相对固定,商品的供需关系在电子商务平台可实时监控,因此价格可以预测,基于预测结果可提供购买时间建议,或者指导商家进行动态价格调整和营销活动以利益最大化。

5、用户行为预测

基于用户搜索行为、浏览行为、评论历史和个人资料等数据,互联网业务可以洞察消费者的整体需求,进而进行针对性的产品生产、改进和营销。《纸牌屋》选择演员和剧情、百度基于用户喜好进行精准广告营销、阿里根据天猫用户特征包下生产线定制产品、亚马逊预测用户点击行为提前发货均是受益于互联网用户行为预测。

受益于传感器技术和物联网的发展,线下的用户行为洞察正在酝酿。免费商用WIFI、ibeacon技术、摄像头影像监控、室内定位技术、NFC 传感器网络、排队叫号系统,可以探知用户线下的移动、停留、出行规律等数据,进行精准营销或者产品定制。

6、人体健康预测

中医可以通过望闻问切手段发现一些人体内隐藏的慢性病,甚至看体质便可知晓一个人将来可能会出现什么症状。人体体征变化有一定规律,而慢性病发生前人体已经会有一些持续性异常。理论上来说,如果大数据掌握了这样的异常情况,便可以进行慢性病预测。

结合智能硬件,慢性病的大数据预测变为可能。可穿戴设备和智能健康设备帮助网络收集人体健康数据,心率、体重、血脂、血糖、运动量、睡眠量等状况。如果这些数据足够精准且全面,并且有可以形成算法的慢性病预测模式,或许未来你的设备就会提醒你的身体罹患某种慢性病的风险。KickStarter 上的 My Spiroo 便可收集哮喘病人的吐气数据来指导医生诊断其未来的病情趋势。急性病却很难预测,突变和随机性特征使之难以预测。

7、疾病疫情预测

基于人们的搜索情况、购物行为预测大面积疫情爆发的可能性,最经典的“流感预测”便属于此类。如果来自某个区域的“流感”、“板蓝根”搜索需求越来越多,自然可以推测该处有流感趋势。

继世界杯、高考、景点和城市预测之后,百度近日推出了疾病预测产品。目前可以就流感、肝炎、肺结核、性病这四种疾病,对全国每一个省份以及大多数地级市和区县的活跃度、趋势图等情况,进行全面的监控。未来,百度疾病预测监控的疾病种类将从目前的4种扩展到30多种,覆盖更多的常见病和流行病。用户可以根据当地的预测结果进行针对性的预防。

8、灾害灾难预测

气象预测是最典型的灾难灾害预测。地震、洪涝、高温、暴雨这些自然灾害如果可以利用大数据能力进行更加提前的预测和告知便有助于减灾防灾救灾赈灾。与过往不同的是,过去的数据收集方式存在着死角、成本高等问题,物联网时代可以借助廉价的传感器摄像头和无线通信网络,进行实时的数据监控收集,再利用大数据预测分析,做到更精准的自然灾害预测。

9、环境变迁预测

除了进行短时间微观的天气、灾害预测之外,还可以进行更加长期和宏观的环境和生态变迁预测。森林和农田面积缩小、野生动物植物濒危、海岸线上升,温室效应这些问题是地球面临的“慢性问题“。如果人类知道越多地球生态系统以及天气形态变化数据,就越容易模型化未来环境的变迁,进而阻止不好的转变发生。而大数据帮助人类收集、储存和挖掘更多的地球数据,同时还提供了预测的工具。

10、交通行为预测

基于用户和车辆的 LBS 定位数据,分析人车出行的个体和群体特征,进行交通行为的预测。交通部门可预测不同时点不同道路的车流量进行智能的车辆调度,或应用潮汐车道;用户则可以根据预测结果选择拥堵几率更低的道路。

百度基于地图应用的 LBS 预测涵盖范围更广。春运期间预测人们的迁徙趋势指导火车线路和航线的设置,节假日预测景点的人流量指导人们的景区选择,平时还有百度热力图来告诉用户城市商圈、动物园等地点的人流情况,指导用户出行选择和商家的选点选址。

11、能源消耗预测

加州电网系统运营中心管理着加州超过 80% 的电网,向 3500 万用户每年输送 2.89 亿兆瓦电力,电力线长度超过 25000 英里。该中心采用了 Space-Time Insight 的软件进行智能管理,综合分析来自包括天气、传感器、计量设备等各种数据源的海量数据,预测各地的能源需求变化,进行智能电能调度,平衡全网的电力供应和需求,并对潜在危机做出快速响应。中国智能电网业已在尝试类似大数据预测应用。

对于单个家庭来说则可以通过智能家居设备,记录家庭成员的起居习惯,感知用户的舒适度,预测用户的温控能耗需求,进行智能的温控装置控制,还可结合阶梯电价表来帮助用户省钱。Nest正式基于大数据预测用户能耗需求的成功产品。

除了上面列举的 10 多个领域之外,大数据预测还可被应用在房地产预测、就业情况预测、高考分数线预测、选举结果预测、奥斯卡大奖预测、保险投保者风险评估、金融借贷者还款能力评估等等,让人类具备可量化有说服力可验证的洞察未来的能力,大数据预测的魅力正在释放出来。

作者微博@互联网阿超,微信SuperSofter


创之(chuang.pro)是TECH2IPO/创见旗下创业主题子站,为创业者、投资人提供最有价值资讯和观点,欢迎你与我们共同建设!









spring+hibernate+atomikos 分布式事务管理

$
0
0

网上有很多的atomikos的分布式事务管理的配置,但是大多数都是同一类型的数据库,并没有跨数据库类型的配置。使用的数据库是Oracle和mysql。

配置文件代码如下:

 

 

<bean id="propertyConfigurer" class="*.*.*.DBConfigurer"><property name="order" value="1" /><property name="ignoreUnresolvablePlaceholders" value="true" />     <property name="location" value="WEB-INF/DBconfig.properties"/> </bean><bean id="mysqlDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" 
        init-method="init" destroy-method="close"><!-- Set unique name for this DataSource -->  <property name="uniqueResourceName"><value>bpm</value></property><!-- Set XADatasource class name-->  <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /><property name="xaProperties"><props><prop key="user">${connection.username}</prop><prop key="password">${connection.password}</prop><prop key="url">${connection.url}</prop></props></property><!-- set properties for datasource connection pool -->  <property name="poolSize" value="3" /><!-- 管理 Connection 被占用的时间 --><!-- 如果不设置这个值,Atomikos使用默认的300秒(即5分钟),那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset is close 的错误 --><property name="reapTimeout"><value>20000</value></property>  </bean><bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"><property name="dataSource" ref="mysqlDataSource" /><property name="packagesToScan"><list><value>smtcl.mocs.pojos.device</value><value>smtcl.mocs.pojos.authority</value><value>smtcl.mocs.pojos.job</value></list></property><property name="hibernateProperties"><props><!-- 			    <prop key="hibernate.connection.url">${connection.url}</prop> --><prop key="hibernate.connection.driver_class">${connection.driver}</prop><!--                 <prop key="hibernate.connection.username">${connection.username}</prop> --><!--                 <prop key="hibernate.connection.password">${connection.password}</prop> --><prop key="hibernate.dialect">${connection.dialect}</prop><prop key="hibernate.show_sql">${connection.show.sql}</prop><prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop></props></property></bean><!-- atomikos事务管理器 --><bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" 
        init-method="init" destroy-method="close"><property name="forceShutdown"><value>true</value></property></bean><bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"><property name="transactionTimeout" value="300" /></bean><!-- spring 事务管理器 --><bean id="springTransactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager"><property name="transactionManager" ref="atomikosTransactionManager"/><property name="userTransaction" ref="atomikosUserTransaction" /><property name="allowCustomIsolationLevels" value="true"/> </bean><!-- 使用annotation定义事务,对于要加入事物的类,只需对该类加 @Transactional  --><tx:annotation-driven transaction-manager="springTransactionManager" /><!--     hibernate Dao层模板 --><!--     <bean id="transactionManager" --><!--         class="org.springframework.orm.hibernate3.HibernateTemplate"> --><!--         <property name="sessionFactory" ref="sessionFactory"></property> --><!--     </bean> --><bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean>

 

<bean id="erpPropertyConfigurer" class="*。*。*.ERPDBConfigurer"><property name="order" value="2" /><property name="ignoreUnresolvablePlaceholders" value="true" />     <property name="location" value="WEB-INF/ERPDBconfig.properties"/> </bean><bean id="oracleDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"><property name="uniqueResourceName"><value>chh</value></property><property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource"/><property name="xaProperties"><props><prop key="user">${connection.username}</prop><prop key="password">${connection.password}</prop><prop key="URL">${connection.url}</prop></props></property><property name="poolSize"><value>1</value></property><property name="borrowConnectionTimeout"><value>60</value></property></bean><bean id="erpSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"><property name="dataSource" ref="oracleDataSource" /><property name="packagesToScan"><list><value>smtcl.mocs.pojos.erp</value></list></property><property name="hibernateProperties"><props><!-- 			    <prop key="hibernate.connection.url">${erp.connection.url}</prop> --><prop key="hibernate.connection.driver_class">${erp.connection.driver}</prop><!--                 <prop key="hibernate.connection.username">${erp.connection.username}</prop> --><!--                 <prop key="hibernate.connection.password">${erp.connection.password}</prop> --><prop key="hibernate.dialect">${erp.connection.dialect}</prop><prop key="hibernate.show_sql">${erp.connection.show.sql}</prop><prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop></props></property></bean><!-- 	 <bean id="erpTransactionManager" --><!--         class="org.springframework.orm.hibernate3.HibernateTemplate"> --><!--         <property name="sessionFactory" ref="erpSessionFactory"></property> --><!--     </bean> --><bean id="erpTransactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory" ref="erpSessionFactory" /></bean>

 分布式事务在连接Oracle数据时,可能会报错,错误信息如下:

javax.transaction.xa.XAException 
at oracle.jdbc.xa.OracleXAResource.recover(OracleXAResource.java:526) 
at com.ibm.ws.rsadapter.spi.WSRdbXaResourceImpl.recover(WSRdbXaResourceImpl.java:1038) 
at com.ibm.ws.Transaction.JTA.XARminst.recover(XARminst.java:138) 
at com.ibm.ws.Transaction.JTA.XARecoveryData.recover(XARecoveryData.java:687) 
at com.ibm.ws.Transaction.JTA.PartnerLogTable.recover(PartnerLogTable.java:524) 
at com.ibm.ws.Transaction.JTA.RecoveryManager.resync(RecoveryManager.java:1859) 
at com.ibm.ws.Transaction.JTA.RecoveryManager.run(RecoveryManager.java:2580) 
at java.lang.Thread.run(Thread.java:810)

 可以在Oracle数据库中执行sql语句:

1.grant select on sys.dba_pending_transactions to  username;   
2.grant select on sys.pending_trans$ to username;   
3.grant select on sys.dba_2pc_pending to username;   
4.grant execute on sys.dbms_system to username;  

 



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


ITeye推荐



X2Engine 更新至 v4.1.6 正式版发布

$
0
0

X2Engine 更新至 v4.1.6 正式版发布,此版本增强了进度 UI;升级了活动订阅报告;增强了 Lead Routing;添加了 X2Idetity,反向 IP 查找;解决了 X2Flow 相关的和邮件退订链接方面的问题。更多相关内容请看 这里

X2Engine professional platinum open source crm 4.1.6

X2Engine professional platinum open source crm 4.1.6

X2Engine professional platinum open source crm 4.1.6

X2CRM是一款开源的phpCRM软件,其的中文化也比较不错,使用过后,感觉非常适合。相对于sugarcrm而言,表现得更加轻快,功能也很全。

主要功能包括:

电子邮件营销活动
捕捉互联网信息
路由信息基于业务规则
从事与分别对应与展望
快速查看联系人的操作历史记录
价格和生成销售报价
定义多步销售流程
集成的报告和图表
视觉,无代码管理设置
支持解锁iPhone和iPad、Touch手机



   

(转)ftp的port和pasv模式

$
0
0

转自:http://hi.baidu.com/xianyang1981/item/20d68be050a50aaccf2d4f8e

 

一、ftp的port和pasv模式的工作方式
       FTP使用2个TCP端口,首先是建立一个命令端口(控制端口),然后再产生一个数据端口。国内很多教科书都讲ftp使用21命令端口和20数据端口,这个应该是教书更新太慢的原因吧。实际上FTP分为主动模式和被动模式两种,ftp工作在主动模式使用tcp 21和20两个端口,而工作在被动模式会工作在大于1024随机端口。FTP最权威的参考见RFC 959,有兴趣的朋友可以仔细阅读 ftp://nic.merit.edu/documents/rfc/rfc0959.txt的文档了解FTP详细工作模式和命令。目前主流的FTP
Server服务器模式都是同时支持port和pasv两种方式,但是为了方便管理安全管理防火墙和设置ACL了解FTP Server的port和pasv模式是很有必要的。

1.1 ftp port模式(主动模式)
       主动方式的FTP是这样的:客户端从一个任意的非特权端口N(N>1024)连接到FTP服务器的命令端口(即tcp 21端口)。紧接着客户端开始监听端口N+1,并发送FTP命令“port N+1”到FTP服务器。最后服务器会从它自己的数据端口(20)连接到客户端指定的数据端口(N+1),这样客户端就可以和ftp服务器建立数据传输通道了。ftp port模式工作流程如下图所示:

针对FTP服务器前面的防火墙来说,必须允许以下通讯才能支持主动方式FTP:
1、客户端口>1024端口到FTP服务器的21端口 (入:客户端初始化的连接 S<-C)
2、FTP服务器的21端口到客户端>1024的端口(出:服务器响应客户端的控制端口 S->C) 
3、FTP服务器的20端口到客户端>1024的端口(出:服务器端初始化数据连接到客户端的数据端口 S->C)
4、客户端>1024端口到FTP服务器的20端口(入:客户端发送ACK响应到服务器的数据端口 S<-C)

 

1.2 ftp pasv模式(被动模式)
       在被动方式FTP中,命令连接和数据连接都由客户端。当开启一个FTP连接时,客户端打开两个任意的非特权本地端口(N > 1024和N+1)。第一个端口连接服务器的21端口,但与主动方式的FTP不同,客户端不会提交PORT命令并允许服务器来回连它的数据端口,而是提交 PASV命令。这样做的结果是服务器会开启一个任意的非特权端口(P > 1024),并发送PORT P命令给客户端。然后客户端发起从本地端口N+1到服务器的端口P的连接用来传送数据。ftp pasv模式工作流程如下图所示: 

 

对于服务器端的防火墙来说,必须允许下面的通讯才能支持被动方式的FTP: 
1、客户端>1024端口到服务器的21端口 (入:客户端初始化的连接 S<-C) 
2、服务器的21端口到客户端>1024的端口 (出:服务器响应到客户端的控制端口的连接 S->C) 
3、客户端>1024端口到服务器的大于1024端口 (入:客户端初始化数据连接到服务器指定的任意端口 S<-C) 
4、服务器的大于1024端口到远程的大于1024的端口(出:服务器发送ACK响应和数据到客户端的数据端口 S->C)



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


ITeye推荐



OpenVPN桌面客户端爆CSRF漏洞(可远程执行命令)

$
0
0

SEC Consult的安全研究员发现OpenVPN的桌面客户端存在CSRF漏洞。成功利用该漏洞,可以实现远程命令执行。openvpn已经发布公告,建议受影响的客户端版本立刻升级到最新版。



受影响版本

windows版本的OpenVPN Access Server "Desktop Client" app。版本号为1.5.6(漏洞发现时的最新版)及以前的版本。OpenVPN Connect,Private Tunnel和community builds 不受影响。

漏洞概要

OpenVPN Access Server "Desktop Client"包括两个部分,一个windows服务,通过本地的webserver提供XML-RPC的api。一个GUI的组件来连接这些API。这些XML-RPC的API存在csrf的漏洞。利用这些api可以实现以下几种攻击:

1,暴露受害者的真实信息。(比如,可以断开一个已经连接的VPN链接)
2,实施中间人攻击。(可以让受害者连接到一个攻击者控制的VPN服务器)
3,以SYSTEM权限执行任意命令。(通过添加一个VPN profile实现)

演示视频Youtube

日志分析方法概述

$
0
0

最近几年日志分析这方面的人才需求越来越多,主要伴随数据挖掘的快速发展而迅速增长的。碰巧又在工作中又接触到一些日志记录方面的工作,就顺便了解一下日志系统的整个流程。下面这篇文章转自百度同学的一篇文章,针对大规模日志分析,联系到hadoop,hive的解决方案,阐述的比较全面。

另外就是阿里已经开发出类似的系统odps—通过sql语言进行数据的分析处理,详情见:http://102.alibaba.com/competition/addDiscovery/faq.htm

————————————————————————————————————————

日志在计算机系统中是一个非常广泛的概念,任何程序都有可能输出日志:操作系统内核、各种应用服务器等等。日志的内容、规模和用途也各不相同,很难一概而论。

本文讨论的日志处理方法中的日志,仅指Web日志。其实并没有精确的定义,可能包括但不限于各种前端Web服务器——apache、lighttpd、tomcat等产生的用户访问日志,以及各种Web应用程序自己输出的日志。

在Web日志中,每条日志通常代表着用户的一次访问行为,例如下面就是一条典型的apache日志:

211.87.152.44 – - [18/Mar/2005:12:21:42 +0800] “GET / HTTP/1.1″ 200 899 “http://www.baidu.com/” “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Maxthon)”

从上面这条日志中,我们可以得到很多有用的信息,例如访问者的IP、访问的时间、访问的目标网页、来源的地址以及访问者所使用的客户端的UserAgent信息等。如果需要更多的信息,则要用其它手段去获取:例如想得到用户屏幕的分辨率,一般需要使用js代码单独发送请求;而如果想得到诸如用户访问的具体新闻标题等信息,则可能需要Web应用程序在自己的代码里输出。

为什么要分析日志

毫无疑问,Web日志中包含了大量人们——主要是产品分析人员会感兴趣的信息,最简单的,我们可以从中获取网站每类页面的PV值(PageView,页面访问量)、独立IP数(即去重之后的IP数量)等;稍微复杂一些的,可以计算得出用户所检索的关键词排行榜、用户停留时间最高的页面等;更复杂的,构建广告点击模型、分析用户行为特征等等。

既然这些数据是如此的有用,那么当然已经有无数现成的工具可以帮助我们来分析它们,例如awstats、Webalizer,都是专门用于统计分析Web服务器日志的免费程序。

另外还有一类产品,它们不分析直接日志,而是通过让用户在页面中嵌入js代码的方式来直接进行数据统计,或者说我们可以认为它是直接让日志输出到了它们的服务器。典型的代表产品——大名鼎鼎的Google Analytics,另外还有国内的cnzz、百度统计等。

很多人可能会说,既然如此,我们为什么还需要自己来分析日志,有必要吗?当然有。我们的用户(产品分析人员)需求是无穷尽的,上面说的这几类工具虽然很好很强大,但显然没办法满足全部的需求。

无论是本地分析的工具,还是在线的分析服务,它们虽然提很丰富的的统计分析功能,可以做一定程度的配置,但是依然很有限的。要进行稍复杂点的分析,或者要做基于日志的数据挖掘,依然需要自己来完成。

另外绝大多数日志分析工具都是只能用于单机的,数据量稍大就没辙了。同时那些提供在线分析的服务对于单个站点通常也都有最大流量的限制——这是很容易理解的,他们也需要考虑服务器的负载。

所以,很多时候还是得靠自己。

怎么进行日志分析

这并不是一个简单的问题。即使我们把“日志”限定为Web日志,依然包含了成千上万种可能的格式和数据,而是“分析”更是难以定义,也许是简单的统计值的计算,也许是复杂的数据挖掘算法。

下面并不打算讨论这些复杂的问题,而只是笼统的讨论如何构建进行日志分析工作的基础。有了这些基础会让基于日志的简单统计分析变得很简单,并让复杂的分析挖掘等变得可行。

少量数据的情况

先考虑最简单的情况,在数据规模比较小的时候,也许是几十MB、几百MB或者几十GB,总之就是在单机处理尚能忍受的时候。一切都很好办,现成的各种Unix/Linux工具——awk、grep、sort、join等都是日志分析的利器,如果仅仅是想知道某个页面的PV,一个wc+grep就能搞定。如果有稍复杂的逻辑,那就使用各种脚本语言,尤其是perl,配合伟大的正则表达式,基本就可以解决所有的问题。

例如,我们想从上面提到的apache日志中得到访问量最高前100个IP,实现很简单:

cat logfile | awk ‘{a[$1]++} END {for(b in a) print b”\t”a[b]}’|sort -k2 -r|head -n 100

不过当我们需要频繁去分析日志的时候,上面的做法在一段时间之后可能就会让我们头疼如何进行各种日志文件、用于分析的脚本文件、crontab文件等等的维护,并且可能会存在大量重复的代码来做数据格式的解析和清洗,这个时候也许就需要更合适的东西,比如——数据库。

当然,要使用数据库来进行日志分析还是需要一些代价的,最主要的就是如何将各种异构的日志文件导入的数据库中——这个过程通常称为ETL(Extraction-Transformation-Loading)。幸好依然有各种现成的开源、免费的工具来帮助我们做这件事情,并且在日志种类不太多的时候,自己写几个简单的脚本来完成这项工作也并不困难。例如可以将上面的日志去掉不必要的字段,然后导入如下的数据库中:

 


现在需要考虑一下用什么数据库来存储这些数据。MySQL是一个很经典的开源数据库,它的传统引擎(MyISAM或者InnoDB,行存储)也许并不非常的适合日志数据的存储,但是在小数据量的时候还是很够用的。而且,在这方面现在已经有了更好的选择,例如开源且免费的Infobright、Infinidb,都是专门为数据仓库应用而进行了优化的数据引擎,采用列存储,有良好的数据压缩,处理几百GB的数据基本上不是问题。

使用数据库的好处之一就是,伟大的SQL可以帮我们很简单的完成绝大部分的统计分析工作——PV只需要SELECT+COUNT,计算搜索词排行只需要SELECT+COUNT+GROUP+ORDER+LIMIT。此外,数据库本身的结构化存储模式也让日志数据的管理变的更简单,减少运维代价。

同样还是上面的那个例子,简单的一个SQL就可以搞定:

SELECT * FROM (SELECT ip, COUNT(*) AS ip_count FROM apache_log GROUP BY ip) a ORDER BY ip_count DESC LIMIT 100

至于性能问题,数据库的索引和各种优化机制通常会让我们的统计分析工作变得更快,并且上面提到的Infobright和Infinidb都专门为类似SUM、COUNt之类的聚集应用做了优化。当然也不是绝对的会快,例如在数据库中进行LIKE操作,通常会比grep一个文件还要慢很多。

更进一步的,使用基于数据库的存储,可以很容易的进行OLAP(联机分析处理)应用,从日志中挖掘价值会变的更加简单。

更多的数据怎么办

一个好的数据库似乎会让事情变的很简单,但是别忘了前面提到的都是单机数据库。一台单机在存储容量、并发性上毫无疑问都是有很大限制的。而日志数据的特点之一就是随时间持续增长,并且由于很多分析过程往往需要历史数据。短时间内的增长也许可以通过分库、分表或者数据压缩等来解决,不过很显然并不是长久之计。

想要彻底解决数据规模增长带来的问题,很自然的会想到使用分布式技术,结合上面的结论,也许使用某个分布式数据库是一个好选择,那么对最终用户就可以完全透明了。这个的确是很理想的情况,不过现实往往是残酷的。

首先,实现比较完美的分布式数据库(受限于CAP原则)是一个非常复杂的问题,因此在这里并不像单机数据库那样,有那么多开源的好东西可以用,甚至于商用的也并不是太多。当然,也并非绝对,如果有钱,还是可以考虑一下Oracle RAC、Greenplum之类东西。

其次,绝大多数分布式数据库都是NoSQL的,所以想继续用上SQL的那些优点基本上是没指望,取而代之的都是一些简单、难以使用的接口。单从这点看来,使用这些数据库的价值已经降低很多了。

所以,还是先现实一点,先退一步考虑如何解决的超大规模的日志的分析问题,而不是想如何让它变的像在小数据规模时那样简单。单单想做到这点,目前看来并不是太难,并且依然有免费的午餐可以吃。

Hadoop是伟大的Apache基金会下面的一套分布式系统,包括分布式文件系统(HDFS)、MapReduce计算框架、HBase等很多组件——这些基本都是Google的GFS/MapReduce/BigTable的克隆产品。

Hadoop经过数年的发展,目前已经很成熟了,尤其是其中的HDFS和MapReduce计算框架组件。数百台机器的集群已经被证明可以使用,可以承担PB级别的数据。

Hadoop项目中的HBase是一个按列存储的NoSQL分布式数据库,它提供的功能和接口都非常简单,只能进行简单的K-V查询,因此并不直接适用于大多数日志分析应用。所以一般使用Hadoop来做日志分析,首先还是需要将日志存储在HDFS中,然后再使用它提供的MapReduce API编写日志分析程序。

MapReduce是一种分布式编程模型,并不难学习,但是很显然使用它来处理日志的代价依然远大于单机脚本或者SQL。一个简单的词频统计计算可能都需要上百代码——SQL只需要一行,另外还有复杂的环境准备和启动脚本。

例如同样还是上面的例子,实现就要复杂的多,通常需要两轮MapReduce来完成。首先要在第一轮的mapper中计算部分ip的访问次数之和,并以ip为key输出:

//遍历输入,并聚合结果

foreach(record in input) {

ip = record.ip;

dict[ip]++;

}

//用emit输出,第一个参数为key,用于reduce的分发

foreach(<ip, count> in dict) {

emit(ip, count);

}

然后在第一轮的reduce中就可以得到每个ip完整的计数,可以顺便排个序,并且只保留前100个。

count = 0;

//对于每个key(ip),遍历所有的values(count),并累加

while(input.values.hasNext()) {

count += input.values.next();

}

//插入到大小为100的堆中

heap_insert(input.key, count);

在reduce结束的时候输出:

//输出当前reduce中count最高的100个ip

foreach(<ip, count> in dict) {

emit(ip, count);

}

由于reduce一般会有很多个,所以最后还需要将所有reduce的输出进行合并、再排序,并得到最终的前100个IP以及对应的访问量。

所以,使用Hadoop来做日志分析很显然不是一件简单事情,它带来了很多的额外的学习和运维成本,但是至少,它让超大规模的日志分析变成了可能。

怎样变得更简单

在超大规模的数据上做任何事情都不是一件容易的事情,包括日志分析,但也并不是说分布式的日志分析就一定要去写MapReduce代码,总是可以去做进一步的抽象,在特定的应用下让事情变得更简单。

也许有人会很自然的想到如果能用SQL来操作Hadoop上的数据该有多好。事实上,不仅仅只有你一个人会这么想,很多人都这么想,并且他们实现了这个想法,于是就有了Hive。

Hive现在也是Hadoop项目下面的一个子项目,它可以让我们用SQL的接口来执行MapReduce,甚至提供了JDBC和ODBC的接口。有了这个之后,Hadoop基本上被包装成一个数据库。当然实际上Hive的SQL最终还是被翻译成了MapReduce代码来执行,因此即使最简单的SQL可能也要执行好几十秒。幸好在通常的离线日志分析中,这个时间还是可以接受的。更重要的是,对于上面提到的例子,我们又可以用一样的SQL来完成分析任务了。

当然Hive并不是完全的兼容SQL语法,而且也不能做到完全的对用户屏蔽细节。很多时候为了执行性能的优化,依然需要用户去了解一些MapReduce的基本知识,根据自己的应用模式来设置一些参数,否则我们可能会发现一个查询执行很慢,或者压根执行不出来。

另外,很显然Hive也并不能覆盖所有的需求,所以它依然保留插入原始MapReduce代码的接口,以便扩展。

更多的问题

即使有了Hive这样一个类似于数据库的东西,我们依然还有很多事情需要做。例如时间久了,可能会有越来越多的需要例行执行的SQL,而这些SQL中,也许有一些是做了重复的事情;也许有一些的执行效率非常低下,一个复杂的SQL就占满了所有的计算资源。这样的系统会变得越来越难以维护的,直到有一天例行的SQL终于跑不完了。而最终用户往往不会去关心这些事情,他们只关心自己提交的查询是不是能即时得到响应,怎么样才能尽快的拿到结果。

举个简单的例子,如果发现在使用apache_log的所有查询中,几乎没有人用其中的user_agent字段,那么我们完全可以把这个字段去除掉,或者拆分成两张表,以减少多数查询的IO时间,提高执行的效率。

为了系统化的解决这些问题,我们可能需要引入例行任务的调度机制,可能需要去分析所有的SQL来发现哪些是可以合并的、哪些的性能需要优化,使用的数据表是不是需要做水平或者垂直分表等等。根据实际情况的不同,这时事情可能是人工来完成,也可能是写程序来自动分析并调整。

再者随着日志类型、分析需求的不断增长。用户会越来越多的抱怨很难找到想要的数据在哪份日志里,或者跑的好好的查询因为日志格式的变化而突然不能用了。另外上面提到的ETL过程也会变得复杂,简单的转换导入脚本很可能已经解决不了问题。这时候可能需要构建一个数据管理系统,或者干脆考虑建立一个所谓的数据仓库。

总之,随着日志数据量、日志类型、用户数量、分析需求等等的不断增长,越来越多的问题会逐渐浮现出来,日志分析这件事情可能就不再像我们最初想的那么简单,会变得越来越有价值,也越来越有挑战。

作者:ZiQingFeng 发表于2014-7-17 16:26:52 原文链接
阅读:53 评论:0 查看评论

怎么实现Web聊天

$
0
0

如果你对web聊天这个事情没什么概念,那么最佳做法可能是:openfire+jsjac

openfire是java做的开源xmpp服务器,jsjac是javascript做的开源的网页版xmpp客户端。

  1. 在openfire的管理界面里面打开http binding和BOSH,并打开“带内账户注册”。
  2. 把jsjac的simpleclient.html和jsjac.js拷贝到openfire的resources/spank目录
  3. 如果你的openfire的http端口是开放在9090端口,则访问:http://192.168.0.100:9090/simpleclient.html
  4. 在页面上HTTP Base填写:http://192.168.0.100:9090/http-bind/,随便写个账号密码,勾上“Register new account”,就可以注册账号并登录进去了。
  5. 后续登录的时候不要勾选“Register new account”,以免重复注册。

搭建一个web聊天系统就是这么简单。


ps:如果你需要做后续开发,那么resource和mechanism等XMPP相关知识是需要的。


作者:wwwsq 发表于2014-7-17 16:10:02 原文链接
阅读:96 评论:0 查看评论

牛仔裤到底洗不洗?Levi's的霸道总裁建议尽量不洗

$
0
0

牛仔裤到底洗不洗?Levi's的霸道总裁建议尽量不洗

在最近一个公开发言( link)里,我(Levi's的CEO,下同)承认我正在穿的牛仔裤从穿的那一天起就“没见过洗衣机的内部”,而且牛仔裤已经穿了一年,此话一出,语惊四座。结果头条变成了“Levi's总裁说永远不要洗你的牛仔裤”(其实,我真的不是这样说的……但这个标题不错)。这条新闻引起了热烈的讨论,并且很快就传开了,世界各地的媒体都对此做了报道。现在,我每到一个地方,人们跟我说的第一件事就是“哦,你就是那个从来不洗牛仔裤的家伙啊!”

数十年来,人们一直在争论如何保养你最好的牛仔布,尤其是牛仔布爱好者争个不停。我在那场会议上的观点原本都是关于可持续发展的,结果变成了挑战人们认为穿过一两次的衣服就该洗的观念。我之所以作这种煽动性的陈述是因为我相信其实我们并不需要像大部分认为的那样经常洗牛仔裤。(现在,说句公道话,我平时穿到办公室的裤子也是我最好的一条牛仔裤——不过那可不是为了陪我女儿在公园里面踢足球的!)

我们试着在事实基础上做每件事情。在2007年,我们的全球副总裁Michael Kobori以及一些专家给一条牛仔裤做了生命周期评估,来看看一条牛仔裤的一生要用多少水和能量,从出生(在棉花里面的时候)到死亡(回收再用或者最糟糕的情况是进了垃圾填埋地)。

牛仔裤到底洗不洗?Levi's的霸道总裁建议尽量不洗

结果相当引人注目。我们了解到一条牛仔裤要用掉3500升水——这还只是在穿了两年,一周洗一次的情况下。有1600升水用在了洗牛仔裤上。要知道这相当于6700杯饮用水!而制作一条牛仔裤只需要它一生用水总量的百分之四。我们在2011年推出了Levi's Water Less的系列产品,在制作过程中能节省百分之96的用水。自从Levi's Water Less上市以来,我们已经为地球节省了77000万升水——比纽约一个月的饮用水还要多!

现在,我们已经知道消费者在买了牛仔裤回家以后,注定有很多水要用来洗牛仔裤了。为了强调这一点,并且引导消费者环保,我们鼓励消费者少洗牛仔裤。

我当时在那场会议上的话被牛仔爱好者反复拿来说了几年:不要洗牛仔裤,或者减少洗牛仔裤的频率。其实,你也可以像我这样做,如果牛仔裤不是脏的不能看,简单清洗牛仔裤的局部就好了。而且,就算我的牛仔裤不得不洗了,我也是手洗并且把他们晾干的。

鉴于大多数CEO不会穿着牛仔裤接受采访——更不要说没洗的牛仔裤——现在你知道我为什么这样做了,就是为了鼓励每个人都改变一下洗衣服的习惯,尤其是洗牛仔裤的习惯。想象一下如果我们每个人少洗几次牛仔裤,对于整个地球的影响是多么大。不只是为了地球,更是为了你的牛仔裤。

[ 桃子 via huffingtonpost]


memcached协议

$
0
0

memcached协议

旧版: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt

新版: https://github.com/memcached/memcached/blob/master/doc/protocol.txt

一、协议

        memcached的客户端使用TCP连接同memcached进行交互,memcached服务器监听指定的端口(默认端口是11211)。Client连接到memcached服务器,发送指令,获取数据,然后关闭连接。

        通常没有必要发送任何命令来关闭某个会话。客户端可以在任何时候关闭不需要的连接。然而,通常鼓励客户端缓存这些连接,因为memcached服务器本身就被设计成为一个可以支持成百上千个连接的服务器,而客户端缓存了连接后,就可以避免重复的建立连接的开销。

        memcached协议中包含两部分数据,文本行和非结构化数据。前者是来自客户端的命令或来自服务器端的响应,后者代表客户端存取的数据。命令以\r\n结尾,数据可以是\r,\n或者\r\n结尾来标识各自部分的结束。

二、键

        memcached一般通过key来存储,一个key的长度一般不能超过250字符。key不能包含控制字符或空白字符

三、命令

有三种类型的命令:

    1、存储命令:set、add、replace、append、prepend、cas

    2、读取命令:get、gets

    3、第三种命令,不涉及到非结构化数据。客户端发出这样的命令,服务端会返回响应结果

1、过期时间

        过期时间的取值,有两种:一种是Unix时间(自1970.1.1开始到现在的秒数),另一种是相对当前时间的秒数。如果过期时间的秒数大于60*60*24*30(即30天),则服务端认为是Unix时间。

2、错误设置

        客户端发出的每个命令,服务端可能会返回错误提示字符。错误提示字符有如下三种:

      1)  ERROR\r\n :表示客户端发送的命令不存在

      2)  CLIENT_ERROR <error>\r\n :表示客户端的输入有错误

      3)  SERVER_ERROR <error>\r\n :服务器端的错误

3、存储命令:

命令格式:<command name> <key><flags> <exptime> <bytes> [noreply]\r\n

命令名:如set、add、replace、append、prepend

命令名称

作用

set

存储这个数据

add

存储这个数据,当且仅当这个key不存在的时候

replace

存储这个数据,当且仅当这个key存在

append

将数据存放在已存在的key对应的内容的后面,忽略<flags>和<exptime>

prepend

将数据存放在已存在的key对应的内容的前面,忽略<flags>和<exptime>

cas

存储这个数据,当且仅当该数据自从最后被获取到现在没有被更新

flags:一个任意的32位(旧版本是16位)无符号整数

exptime:过期时间,如果为0,表示永不过期。单位秒

bytes:表示将要存储的数据的字节数,可以为0。要存储的内容的字节数,必须等于该值,不能大于也不能小于。

例子:将一个整数12,存放到memcache中,key为var,flags为1,过期时间为1000,字节数为2

用telnet连接memcached服务器端:

输入命令如下:set var 1 1000 2,然后按回车,之后输入要存储是数据:12,按回车,服务器返回STORED,如下:

通过get命令查看刚才存储的内容:

4、获取命令

格式:

      get  <key>*\r\n

      gets <key>*\r\n

<key>*,表示可以有多个key,各个key之间用空格隔开。

执行该命令,服务器返回0个或多个item,每个item的格式如下:

      VALUE <key> <flags><bytes> [<cas unique>]\r\n

      <data block>\r\n

bytes为数据内容的长度,data block为key对应的数据内容

例子:

5、删除命令

格式: delete <key> [noreply]\r\n

noreply参数,告诉服务器不用发送响应

该命令的返回结果,可能是:

       DELETE\r\n       表示删除成功

       NOT_FOUND\r\n   没有对应的key

例子:

6、增加/减少命令

命令格式:

       incr <key> <value> [noreply]\r\n   或   decr <key> <value> [noreply]\r\n

value为要增加或减少的值。

操作成功,服务器返回操作之后的值。

对于decr操作,如果操作之后的值小于0,则置为0

不能直接使用incr和decr,必须先set或者add后再使用,而且值为数字类型,在增加时,存储的区域会扩展。

例子:

7、touch

该命令用来更新已存在的item的过期时间,格式如下:(低版本不支持该命令)

      touch <key> <exptime> [noreply]\r\n

执行该命令后,如果返回”TOUCHED\r\n”,则执行成功

8、统计命令

命令格式:

    1)    stats\r\n         查看通用的统计信息

    2)    stats <args>\r\n

通用统计信息,例子:

具体含义:

名称

类型

含义

pid

32u (32位无符号整数)

memcached server的pid

uptime

32u

memcached server自启动到现在的时间(秒)

time

32u

当前UNIX的时间

version

string

memcached server的版本号

pointer_size

32

操作系统默认的指针大小

curr_items

32u

当前存储的item数目

total_items

32u

server从启动到现在,总共累计存储的item数目

bytes

64u

当前存储item所花费的字节数

curr_connections

32u

当前的客户端连接数

total_connections

32u

server从启动到现在,累计的客户端连接数

connection_structures

32u

server分配的连接结构数

cmd_get

64u

get的次数

cmd_set

64u

set的次数

get_hits

64u

get命中的次数

get_misses

64u

get没有命中,miss的次数

evictions

64u

为新的item释放内存空间而被移除的有效item的数目。如果cache的size比较小,则淘汰策略经常发生

bytes_read

64u

从cache中读取的总字节数

bytes_written

64u

写入cache的总字节数

limit_maxbytes

32u

该memcached server分配的最大内存数量

 

9、item统计信息

stats命令后面带上参数:items。  返回存储在每个slab的item信息,格式如下:

       STAT items:<slabclass>:<stat><value>\r\n

例子:

10、itemsize统计信息

stats命令后面带上参数sizes,返回存储在cache中的总体大小及item数目。

注意:该命令会锁住cache,它会遍历每个item并计算大小,在此之间,我们是无法访问server的,因此要慎重使用该命令。

返回结果的格式如下:

     <size> <count>\r\n

其中:

     'size' is an approximate size of the item,within 32 bytes.

     'count' is the amount of items that existwithin that 32-byte range.

例子:

11、slab统计信息

stats命令带上参数slabs,返回memcached运行期间创建的每个slab的信息。

数据格式:STAT <slabclass>:<stat> <value>\r\n

例子:

名称

含义

chunk_size

每个块的大小。一个item使用一个大小适当的块。

chunks_per_page

一页的块数,一页的默认大小小于等于1M,,chunks_per_page * chunk_size = 1MB。 slab按页分配,每页划分成不同的块

total_pages

分配给slab的页数

total_chunks

分配给slab的块数

used_chunks

已分配给item的块数

free_chunks

未分配给item的块数

free_chunks_end

目前可以使用的块数

mem_requested

请求存储在该slab的字节数

active_slabs

已经分配的slab数量

total_malloced

已经分配给slab的字节数

item存放在slab中,该slab的大小大于或等于该item的大小。mem_requested表示一个slab中所有item的大小。

total_chunks * chunk_size – mem_requested,表示一个slab中所浪费的内存大小。如果有很多浪费,则需要考虑调整slab因子

12、其他命令

     1)   flush_all :执行该命令,将导致memcache中所存在的所有item都失效。也可以指定在一段时间之后失效。例子:

    2)   version : 查看memcache的版本号

    3)quit :关闭连接



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


ITeye推荐



定义浏览器统一的默认样式:Normalize.css

$
0
0

如果你从事网页前端工作,肯定会发现不同浏览器的默认样式有细微的差异,这个让你在使用 CSS 进行布局工作的时候相当烦人,况且有些差异还不是那么细微,比如不同浏览器之间的默认表单样式差异以及 <html> 和 <body> 元素的margin 和 padding 的差异等。

Normalize.css 介绍

一般前端工程师都会使用 reset.css 去处理不同浏览器的默认样式,这里推荐使用 Normalize.css,它是一个定制的 reset.css 文件,可以让所有元素在所有的现代浏览器上渲染一致。

相比其它的 reset.css,Normalize.css 保留有用的默认样式,不像其它 reset.css 那么彻底,移除了每个默认样式,然后又得费神把需要的样式再加回来。Normalize.css 定义绝大多数元素的默认样式,并且还提高了一些元素的可用性,并且代码都有详细的注释。

Normalize.css 支持的浏览器

  • Google Chrome (latest)
  • Mozilla Firefox (latest)
  • Mozilla Firefox 4
  • Opera (latest)
  • Apple Safari 6+
  • Internet Explorer 8+

使用 Normalize.css

Normalize.css 使用非常简单,并且七牛的 StaticFile.org已经包含了该库,所以无需下载,只要在网页的头部加入以下代码即可:

<link rel="stylesheet" href="http://cdn.staticfile.org/normalize/3.0.1/normalize.min.css" type="text/css" />

>>>继续阅读 定义浏览器统一的默认样式:Normalize.css的全文 …

© 我爱水煮鱼 / RSS 订阅 / 长期承接 WordPress 项目 / 主机域名优惠码 / 新浪微博

微软CEO纳德拉全员邮件:我为什么决定裁员

$
0
0

微软 CEO 纳德拉全员邮件:我为什么决定裁员

7 月 17 日,微软周四宣布,公司计划在今年裁减最多 1.8 万名员工,这将是微软历史上规模最大的一次裁员,裁员原因是微软正在消化收购来的诺基亚手机业务,并调整自身定位以适应未来发展。

在今年 4 月份完成收购诺基亚手机业务的交易以后,微软的员工总数为 127104 人,这意味着此次裁员规模相当于其员工总数的近 15%。

以下是微软 CEO 纳德拉向微软员工发出的电子邮件备忘录全文:

寄件人:萨提亚·纳德拉

收件人:全体员工

日期:2014 年 7 月 17 日

主题:着手发展进化我们的公司组织及文化

在上周向你们发出的电子邮件中,我综合阐述了我们作为一家生产力和平台公司的战略发展方向。拥有明确清晰的聚焦点是这段旅程的起点,而并非终点。我们需要迈出更加艰难的步伐来创造我们的公司组织和文化,从而让我们的愿望变成现实。今天,我将就我们未来将如何前进的问题与你们分享更多信息。在 7 月 22 日的财报电话会议上,我还将进一步共享更多具体信息,内容将与我们将把创新投资集中在哪些领域有关。

为了达成我们的愿望,我们需要以正确的方式来建设公司,而第一步就需要对公司劳动力进行重新配置。带着这种想法,我们将着手在未来一年时间里把公司的整体劳动力规模缩减最多 1.8 万人。其中,旨在为诺基亚设备和服务部门创造合力和进行战略性配置的相关工作将会占到大约 1.25 个工作岗位,被裁人员将包括专业人士和工厂工人。

就目前而言,我们正在着手进行裁员 1.3 万人的行动,其中大多数将被裁减的人员都将在未来六个月中收到通知。需要指出的很重要的一点是,虽然我们正在某些领域中裁减人员,但同时在其他一些战略性领域中则正在新聘员工。我对你们的承诺是,我们将会以尽可能审慎和透明的方式来完成这个过程。我对你们的承诺是,我们将会以尽可能审慎和透明的方式来完成这个过程。

对于将会受到这些变革影响的所有员工,我们都将提供离职金,并在许多地方提供工作调动机会。所有人都将因其对这家公司作出的恭喜而得到应有的尊重。

在今天晚些时候,你们的高级管理团队成员将会分享有关公司组织将发生什么变化的更多信息。我们的裁员行动主要以两种结果为驱动力:工作的简化以及诺基亚设备和服务部门的整合合力和战略性配置。

首先,我们将会简化工作方式以进一步推行问责制,并藉此变得更加灵活迅捷。作为工艺流程现代化的一部分内容,我们的每一种纪律都将发生变化。此外,我们还计划减少管理层级,以便加快信息流动和决策进程,这将包括将公司组织变得更加扁平化,以及扩大人事经理的控制范围等。另外,我们的业务流程和支持模式也将变得更加精简高效,团队之间的互相信任感将得到增强。

这些变革所将带来的整体结果是,微软内部的各个团队将会变得更具生产力和影响力。这些变革将同时对微软自己的劳动力和经销商的员工造成影响。每一个组织的变革都将从不同的起点出发,并以不同的速度前进。

其次,我们将致力于把诺基亚设备和服务团队整合到微软中来。我们将会实现在去年 9 月份宣布收购交易时所承诺过的合力。第一方手机产品组合将依据微软的战略发展方向进行配置。为了在高价产品类别中取得胜利,我们将把重点放在突破性创新上,以便传递微软的数字工作和数字生活体验,并使其变得更加生动。此外,我们还计划把特定的诺基亚X系列产品设计转为基于 Windows 系统运行的 Lumia 产品。这将扩大我们在廉价智能手机领域中取得的成功,同时也符合我们以 Windows Universal Apps 为中心的战略。

作出这些变革决定是很困难的,但同时也是很有必要的。在此我想要邀请你们参加我将在明天举行的月度问答会议,希望你们能投身其中,提出你脑海中的任何问题。值此迈步向前以发展进化我们的公司组织和文化之际,我要对你们的支持表示感谢。

萨提亚

本文链接

用raphael实现的jbpm4web流程设计器

$
0
0

最近准备自己根据raphael学习和网上查阅到得知识,实现一个jbpm4风格的web流程设计器。有部分功能参考了CSDN网友 wow4464提供的一个控件连线的实例,下载地址: http://download.csdn.net/detail/wow4464/7549803

 

连线的方式使用的是raphael拖拽的特性来实现的,发现在firefox下有问题,其他浏览器正常支持。除了raphael和jquery的javascript库之外,没有借助于任何第三方的库函数或插件。源码见附件。





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


ITeye推荐



hive调优

$
0
0

一、    控制hive任务中的map数: 

1.    通常情况下,作业会通过input的目录产生一个或者多个map任务。 
主要的决定因素有: input的文件总个数,input的文件大小,集群设置的文件块大小(目前为128M, 可在hive中通过set dfs.block.size;命令查看到,该参数不能自定义修改);

2.    举例: 
a)    假设input目录下有1个文件a,大小为780M,那么hadoop会将该文件a分隔成7个块(6个128m的块和1个12m的块),从而产生7个map数
b)    假设input目录下有3个文件a,b,c,大小分别为10m,20m,130m,那么hadoop会分隔成4个块(10m,20m,128m,2m),从而产生4个map数
即,如果文件大于块大小(128m),那么会拆分,如果小于块大小,则把该文件当成一个块。

3.    是不是map数越多越好? 
答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,
而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。
而且,同时可执行的map数是受限的。

 

4.    是不是保证每个map处理接近128m的文件块,就高枕无忧了? 
答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,
如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

针对上面的问题3和4,我们需要采取两种方式来解决:即减少map数和增加map数;

如何合并小文件,减少map数? 
    假设一个SQL任务:
         Select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’;
         该任务的inputdir  /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04
         共有194个文件,其中很多是远远小于128m的小文件,总大小9G,正常执行会用194个map任务。
         Map总共消耗的计算资源: SLOTS_MILLIS_MAPS= 623,020

         我通过以下方法来在map执行前合并小文件,减少map数:

         set mapred.max.split.size= 8000000000000 ;
                    set mapred.min.split.size.per.node= 8000000000000 ;
                    set mapred.min.split.size.per.rack= 8000000000000 ;
                    set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
                 再执行上面的语句,用了74个map任务,map消耗的计算资源:SLOTS_MILLIS_MAPS= 333,500
         对于这个简单SQL任务,执行时间上可能差不多,但节省了一半的计算资源。
         大概解释一下,100000000表示100M, set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;这个参数表示执行前进行小文件合并,
         前面三个参数确定合并文件块的大小,大于文件块大小128m的,按照128m来分隔,小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分隔大文件剩下的),
         进行合并,最终生成了74个块。
         
如何适当的增加map数? 

         当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。
         假设有这样一个任务:
         Select data_desc,
                count(1),
                count(distinct id),
                sum(case when …),
                sum(case when ...),
                sum(…)
        from a group by data_desc
                   如果表a只有一个文件,大小为120M,但包含几千万的记录,如果用1个map去完成这个任务,肯定是比较耗时的,这种情况下,我们要考虑将这一个文件合理的拆分成多个,
                   这样就可以用多个map任务去完成。
                   set mapred.reduce.tasks=10;
                   create table a_1 as 
                   select * from a 
                   distribute by rand(123); 
                   
                   这样会将a表的记录,随机的分散到包含10个文件的a_1表中,再用a_1代替上面sql中的a表,则会用10个map任务去完成。
                   每个map任务处理大于12M(几百万记录)的数据,效率肯定会好很多。
    
   看上去,貌似这两种有些矛盾,一个是要合并小文件,一个是要把大文件拆成小文件,这点正是重点需要关注的地方,
   根据实际情况,控制map数量需要遵循两个原则:使大数据量利用合适的map数;使单个map任务处理合适的数据量;

 

二、    控制hive任务的reduce数: 

1.    Hive自己如何确定reduce数: 
reduce个数的设定极大影响任务执行效率,不指定reduce个数的情况下,Hive会猜测确定一个reduce个数,基于以下两个设定:
hive.exec.reducers.bytes.per.reducer(每个reduce任务处理的数据量,默认为1000^3=1G) 
hive.exec.reducers.max(每个任务最大的reduce数,默认为999)
计算reducer数的公式很简单N=min(参数2,总输入数据量/参数1)
即,如果reduce的输入(map的输出)总大小不超过1G,那么只会有一个reduce任务;
如:select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt; 
            /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04 总大小为9G多,因此这句有10个reduce

2.    调整reduce个数方法一: 
调整hive.exec.reducers.bytes.per.reducer参数的值;
set hive.exec.reducers.bytes.per.reducer=500000000; (500M)
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt; 这次有20个reduce
         
3.    调整reduce个数方法二; 
set mapred.reduce.tasks = 15;
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;这次有15个reduce

4.    reduce个数并不是越多越好; 
同map一样,启动和初始化reduce也会消耗时间和资源;
另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

5.    什么情况下只有一个reduce; 
很多时候你会发现任务中不管数据量多大,不管你有没有设置调整reduce个数的参数,任务中一直都只有一个reduce任务;
其实只有一个reduce任务的情况,除了数据量小于hive.exec.reducers.bytes.per.reducer参数值的情况外,还有以下原因:
a)    没有group by的汇总,比如把select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt; 写成 select count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04';
这点非常常见,希望大家尽量改写。
b)    用了Order by
c)    有笛卡尔积
通常这些情况下,除了找办法来变通和避免,我暂时没有什么好的办法,因为这些操作都是全局的,所以hadoop不得不用一个reduce去完成;

    同样的,在设置reduce个数的时候也需要考虑这两个原则:使大数据量利用合适的reduce数;使单个reduce任务处理合适的数据量;



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


ITeye推荐



mysql 数据分离

$
0
0
网上看到一个读写分离的帖子,感觉不错!!

构建高性能web之路------mysql读写分离实战(转)
一个完整的mysql读写分离环境包括以下几个部分:

应用程序client
database proxy
database集群
在本次实战中,应用程序client基于c3p0连接后端的database proxy。database proxy负责管理client实际访问database的路由策略,采用开源框架amoeba。database集群采用mysql的master-slave的replication方案。整个环境的结构图如下所示:




实战步骤与详解

一.搭建mysql的master-slave环境

1)分别在host1(10.20.147.110)和host2(10.20.147.111)上安装mysql(5.0.45),具体安装方法可见官方文档

2)配置master

首先编辑/etc/my.cnf,添加以下配置:

log-bin=mysql-bin #slave会基于此log-bin来做replication
server-id=1 #master的标示
binlog-do-db = amoeba_study #用于master-slave的具体数据库

然后添加专门用于replication的用户:

mysql> GRANT REPLICATION SLAVE ON *.* TO repl@10.20.147.111 IDENTIFIED BY '111111';

重启mysql,使得配置生效:

/etc/init.d/mysqld restart

最后查看master状态:



3)配置slave

首先编辑/etc/my.cnf,添加以下配置:

server-id=2 #slave的标示

配置生效后,配置与master的连接:

mysql> CHANGE MASTER TO
    -> MASTER_HOST='10.20.147.110',
    -> MASTER_USER='repl',
    -> MASTER_PASSWORD='111111',
    -> MASTER_LOG_FILE='mysql-bin.000003',
    -> MASTER_LOG_POS=161261;

其中MASTER_HOST是master机的ip,MASTER_USER和MASTER_PASSWORD就是我们刚才在master上添加的用户,MASTER_LOG_FILE和MASTER_LOG_POS对应与master status里的信息

最后启动slave:

mysql> start slave;

4)验证master-slave搭建生效

通过查看slave机的log(/var/log/mysqld.log):

100703 10:51:42 [Note] Slave I/O thread: connected to master 'repl@10.20.147.110:3306',  replication started in log 'mysql-bin.000003' at position 161261

如看到以上信息则证明搭建成功,如果有问题也可通过此log找原因

二.搭建database proxy

此次实战中database proxy采用amoeba ,它的相关信息可以查阅官方文档,不在此详述

1)安装amoeba

下载amoeba(1.2.0-GA)后解压到本地(D:/openSource/amoeba-mysql-1.2.0-GA),即完成安装

2)配置amoeba

先配置proxy连接和与各后端mysql服务器连接信息(D:/openSource/amoeba-mysql-1.2.0-GA/conf/amoeba.xml):



<server> 
    <!-- proxy server绑定的端口 --> 
    <property name="port">8066</property> 
     
    <!-- proxy server绑定的IP --> 
    <!-- 
    <property name="ipAddress">127.0.0.1</property>
     --> 
    <!-- proxy server net IO Read thread size --> 
    <property name="readThreadPoolSize">20</property> 
     
    <!-- proxy server client process thread size --> 
    <property name="clientSideThreadPoolSize">30</property> 
     
    <!-- mysql server data packet process thread size --> 
    <property name="serverSideThreadPoolSize">30</property> 
     
    <!-- socket Send and receive BufferSize(unit:K)  --> 
    <property name="netBufferSize">128</property> 
     
    <!-- Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm). --> 
    <property name="tcpNoDelay">true</property> 
     
    <!-- 对外验证的用户名 --> 
    <property name="user">root</property> 
     
    <!-- 对外验证的密码 --> 
    <property name="password">root</property> 
</server> 


以上是proxy提供给client的连接配置



<dbServerList> 
    <dbServer name="server1">          
        <!-- PoolableObjectFactory实现类 --> 
        <factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory"> 
            <property name="manager">defaultManager</property> 
             
            <!-- 真实mysql数据库端口 --> 
            <property name="port">3306</property> 
             
            <!-- 真实mysql数据库IP --> 
            <property name="ipAddress">10.20.147.110</property> 
            <property name="schema">amoeba_study</property> 
             
            <!-- 用于登陆mysql的用户名 --> 
            <property name="user">root</property> 
             
            <!-- 用于登陆mysql的密码 --> 
            <property name="password"></property> 
             
        </factoryConfig> 
         
        <!-- ObjectPool实现类 --> 
        <poolConfig class="com.meidusa.amoeba.net.poolable.PoolableObjectPool"> 
            <property name="maxActive">200</property> 
            <property name="maxIdle">200</property> 
            <property name="minIdle">10</property> 
            <property name="minEvictableIdleTimeMillis">600000</property> 
            <property name="timeBetweenEvictionRunsMillis">600000</property> 
            <property name="testOnBorrow">true</property> 
            <property name="testWhileIdle">true</property> 
        </poolConfig> 
    </dbServer> 
    <dbServer name="server2"> 
         
        <!-- PoolableObjectFactory实现类 --> 
        <factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory"> 
            <property name="manager">defaultManager</property> 
             
            <!-- 真实mysql数据库端口 --> 
            <property name="port">3306</property> 
             
            <!-- 真实mysql数据库IP --> 
            <property name="ipAddress">10.20.147.111</property> 
            <property name="schema">amoeba_study</property> 
             
            <!-- 用于登陆mysql的用户名 --> 
            <property name="user">root</property> 
             
            <!-- 用于登陆mysql的密码 --> 
            <property name="password"></property> 
             
        </factoryConfig> 
         
        <!-- ObjectPool实现类 --> 
        <poolConfig class="com.meidusa.amoeba.net.poolable.PoolableObjectPool"> 
            <property name="maxActive">200</property> 
            <property name="maxIdle">200</property> 
            <property name="minIdle">10</property> 
            <property name="minEvictableIdleTimeMillis">600000</property> 
            <property name="timeBetweenEvictionRunsMillis">600000</property> 
            <property name="testOnBorrow">true</property> 
            <property name="testWhileIdle">true</property> 
        </poolConfig> 
    </dbServer>        
</dbServerList> 


以上是proxy与后端各mysql数据库服务器配置信息,具体配置见注释很明白了

最后配置读写分离策略:



<queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter"> 
    <property name="LRUMapSize">1500</property> 
    <property name="defaultPool">server1</property> 
    <property name="writePool">server1</property> 
    <property name="readPool">server2</property> 
    <property name="needParse">true</property> 
</queryRouter> 


从以上配置不然发现,写操作路由到server1(master),读操作路由到server2(slave)

3)启动amoeba

在命令行里运行D:/openSource/amoeba-mysql-1.2.0-GA/amoeba.bat即可:

log4j:WARN log4j config load completed from file:D:/openSource/amoeba-mysql-1.2.0-GA/conf/log4j.xml
log4j:WARN ip access config load completed from file:D:/openSource/amoeba-mysql-1.2.0-GA/conf/access_list.conf
2010-07-03 09:55:33,821 INFO  net.ServerableConnectionManager - Server listening on 0.0.0.0/0.0.0.0:8066.
三.client端调用与测试

1)编写client调用程序

具体程序细节就不详述了,只是一个最普通的基于mysql driver的jdbc的数据库操作程序

2)配置数据库连接

本client基于c3p0,具体数据源配置如下:



<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" 
    destroy-method="close"> 
    <property name="driverClass" value="com.mysql.jdbc.Driver" /> 
    <property name="jdbcUrl" value="jdbc:mysql://localhost:8066/amoeba_study" /> 
    <property name="user" value="root" /> 
    <property name="password" value="root" /> 
    <property name="minPoolSize" value="1" /> 
    <property name="maxPoolSize" value="1" /> 
    <property name="maxIdleTime" value="1800" /> 
    <property name="acquireIncrement" value="1" /> 
    <property name="maxStatements" value="0" /> 
    <property name="initialPoolSize" value="1" /> 
    <property name="idleConnectionTestPeriod" value="1800" /> 
    <property name="acquireRetryAttempts" value="6" /> 
    <property name="acquireRetryDelay" value="1000" /> 
    <property name="breakAfterAcquireFailure" value="false" /> 
    <property name="testConnectionOnCheckout" value="true" /> 
    <property name="testConnectionOnCheckin" value="false" /> 
</bean> 


值得注意是,client端只需连到proxy,与实际的数据库没有任何关系,因此jdbcUrl、user、password配置都对应于amoeba暴露出来的配置信息

3)调用与测试

首先插入一条数据:insert into zone_by_id(id,name) values(20003,'name_20003')

通过查看master机上的日志/var/lib/mysql/mysql_log.log:

100703 11:58:42       1 Query       set names latin1
                      1 Query       SET NAMES latin1
                      1 Query       SET character_set_results = NULL
                      1 Query       SHOW VARIABLES
                      1 Query       SHOW COLLATION
                      1 Query       SET autocommit=1
                      1 Query       SET sql_mode='STRICT_TRANS_TABLES'
                      1 Query       SHOW VARIABLES LIKE 'tx_isolation'
                      1 Query       SHOW FULL TABLES FROM `amoeba_study` LIKE 'PROBABLYNOT'
                      1 Prepare     [1] insert into zone_by_id(id,name) values(?,?)
                      1 Prepare     [2] insert into zone_by_id(id,name) values(?,?)          
                      1 Execute     [2] insert into zone_by_id(id,name) values(20003,'name_20003')

得知写操作发生在master机上

通过查看slave机上的日志/var/lib/mysql/mysql_log.log:

100703 11:58:42       2 Query       insert into zone_by_id(id,name) values(20003,'name_20003')

得知slave同步执行了这条语句

然后查一条数据:select t.name from zone_by_id t where t.id = 20003

通过查看slave机上的日志/var/lib/mysql/mysql_log.log:

100703 12:02:00      33 Query       set names latin1
                     33 Prepare     [1] select t.name from zone_by_id t where t.id = ?
                     33 Prepare     [2] select t.name from zone_by_id t where t.id = ?   
                     33 Execute     [2] select t.name from zone_by_id t where t.id = 20003

得知读操作发生在slave机上

并且通过查看slave机上的日志/var/lib/mysql/mysql_log.log发现这条语句没在master上执行



通过以上验证得知简单的master-slave搭建和实战得以生效

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


ITeye推荐



揭秘:独特的技术手段,远程控制你的手机

$
0
0

我们最近在分析恶意样本时候发现了两类独特的技术手段,一种利用轻量级web服务器jetty来窃取用户隐私,另一种使用Androidpn推送指令来远程控制。

一、更独特隐蔽的隐私窃取—web服务器

这类木马利用轻量级web服务器jetty将目标手机变成服务器后,通过url请求获取手机上的任意资源,造成用户隐私泄露。

工作方式:

在目标手机上设置jetty服务器后,通过浏览器等请求特定的URL,可获取目标手机上的资源,包括隐私信息等。

架构如下:

43221405217235

 

获取用户隐私信息对应的url路径:

19401405217235

 

该木马通过将目标手机变成服务器后,通过url请求获取手机上的任意资源,造成用户隐私泄露,这种方式更为独特而隐蔽。

二、另类指令获取—Androidpn推送

AVL移动团队近期发现了一种使用Androidpn推送指令的远控间谍软件,该程序伪装成系统应用、诱导用户激活设备管理器,根据推送获取的指令来进行上传通话记录、通话录音、环境录音、联系人、短信箱、地理位置等隐私信息,并能执行修改短信箱内容、私发短信、拦截短信、屏蔽来电等操作,窃取用户隐私、影响手机的正常体验。

android push notification原理(源自Androidpn界面截图):

36341405217236

 

向用户手机客户端push消息(源自Androidpn界面截图):

15661405217237

 

登录xmpp服务器,获取消息指令(源自Androidpn界面截图):

56951405217237

 

远控模块流程:

14049899523141 (1)

 

87021405217240

 

此外,该应用能接收指令,私自修改替换短信箱中的内容。。

49091405217242

 

该样本使用少见的Androidpn推送方式来获取指令,加上多种窃取隐私信息,尤其修改短信箱内容功能更为罕见。

三、小结

随着时间的推移,恶意代码也会使用到各种新的技术和手段,逐步强大而隐蔽,更与安全软件对抗起来。手机设备性能的提升,使得种种PC上的服务变得可能,Android系统功能的增多也给恶意软件带来更多的途径。

揭秘:独特的技术手段,远程控制你的手机,首发于 极客范 - GeekFan.net


[转载]Apache Shiro使用手册

$
0
0

第一部分 Shiro构架介绍

 

一、什么是Shiro 

Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能: 

  • 认证 - 用户身份识别,常被称为用户“登录”;
  • 授权 - 访问控制;
  • 密码加密 - 保护或隐藏数据防止被偷窥;
  • 会话管理 - 每用户相关的时间敏感的状态。

      对于任何一个应用程序,Shiro都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro要简单的多。

 

二、Shiro的架构介绍 
首先,来了解一下Shiro的三个核心组件:Subject, SecurityManager 和 Realms. 如下图: 

 

Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。 Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。 

SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。 

Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。 
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。

 

    Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。

 

Shiro完整架构图: 

 

 

除前文所讲Subject、SecurityManager 、Realm三个核心组件外,Shiro主要组件还包括: 
Authenticator :认证就是核实用户身份的过程。这个过程的常见例子是大家都熟悉的“用户/密码”组合。多数用户在登录软件系统时,通常提供自己的用户名(当事人)和支持他们的密码(证书)。如果存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为通过认证。 

Authorizer :授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web页面等等。 

SessionManager :在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。 

CacheManager :对Shiro的其他组件提供缓存支持。

 

第二部分 Shiro认证

 

       认证就是验证用户身份的过程。在认证过程中,用户需要提交实体信息(Principals)和凭据信息(Credentials)以检验用户是否合法。最常见的“实体/凭证”组合便是“用户名/密码”组合。

 

一、Shiro认证过程 

1、收集实体/凭据信息 

 

//Example using most common scenario of username/password pair:
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//”Remember Me” built-in:
token.setRememberMe(true);

 

UsernamePasswordToken支持最常见的用户名/密码的认证机制。同时,由于它实现了RememberMeAuthenticationToken接口,我们可以通过令牌设置“记住我”的功能。 

 

但是,“已记住”和“已认证”是有区别的: 
已记住的用户仅仅是非匿名用户,你可以通过subject.getPrincipals()获取用户信息。但是它并非是完全认证通过的用户,当你访问需要认证用户的功能时,你仍然需要重新提交认证信息。 
这一区别可以参考亚马逊网站,网站会默认记住登录的用户,再次访问网站时,对于非敏感的页面功能,页面上会显示记住的用户信息,但是当你访问网站账户信息时仍然需要再次进行登录认证。 

 

2、提交实体/凭据信息 

 

Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
 收集了实体/凭据信息之后,我们可以通过SecurityUtils工具类,获取当前的用户,然后通过调用login方法提交认证。

 

3、认证处理 

 

try {
    currentUser.login(token);
} catch ( UnknownAccountException uae ) { ...
} catch ( IncorrectCredentialsException ice ) { ...
} catch ( LockedAccountException lae ) { ...
} catch ( ExcessiveAttemptsException eae ) { ...
} ... catch your own ...
} catch ( AuthenticationException ae ) {
    //unexpected error?
}
 如果login方法执行完毕且没有抛出任何异常信息,那么便认为用户认证通过。之后在应用程序任意地方调用SecurityUtils.getSubject() 都可以获取到当前认证通过的用户实例,使用subject.isAuthenticated()判断用户是否已验证都将返回true. 

 

相反,如果login方法执行过程中抛出异常,那么将认为认证失败。Shiro有着丰富的层次鲜明的异常类来描述认证失败的原因,如代码示例。
 

二、登出操作 
登出操作可以通过调用subject.logout()来删除你的登录信息,如:

 

currentUser.logout(); //removes all identifying information and invalidates their session too.
 当执行完登出操作后,Session信息将被清空,subject将被视作为匿名用户。 

 

 

三、认证内部处理机制 

以上,是Shiro认证在应用程序中的处理过程,下面将详细解说Shiro认证的内部处理机制。

 

 

如上图,我们通过Shiro架构图的认证部分,来说明Shiro认证内部的处理顺序: 
1、应用程序构建了一个终端用户认证信息的AuthenticationToken 实例后,调用Subject.login方法。 
2、Sbuject的实例通常是DelegatingSubject类(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用securityManager.login(token)方法。 
3、SecurityManager接受到token(令牌)信息后会委托内置的Authenticator的实例(通常都是ModularRealmAuthenticator类的实例)调用authenticator.authenticate(token). ModularRealmAuthenticator在认证过程中会对设置的一个或多个Realm实例进行适配,它实际上为Shiro提供了一个可拔插的认证机制。 
4、如果在应用程序中配置了多个Realm,ModularRealmAuthenticator会根据配置的AuthenticationStrategy(认证策略)来进行多Realm的认证过程。在Realm被调用后,AuthenticationStrategy将对每一个Realm的结果作出响应。 
注:如果应用程序中仅配置了一个Realm,Realm将被直接调用而无需再配置认证策略。 
5、判断每一个Realm是否支持提交的token,如果支持,Realm将调用getAuthenticationInfo(token); getAuthenticationInfo 方法就是实际认证处理,我们通过覆盖Realm的doGetAuthenticationInfo方法来编写我们自定义的认证处理。 

 

四、使用多个Realm的处理机制: 

 

1、Authenticator 
默认实现是ModularRealmAuthenticator,它既支持单一Realm也支持多个Realm。如果仅配置了一个Realm,ModularRealmAuthenticator 会直接调用该Realm处理认证信息,如果配置了多个Realm,它会根据认证策略来适配Realm,找到合适的Realm执行认证信息。 
自定义Authenticator的配置: 

 

[main]
...
authenticator = com.foo.bar.CustomAuthenticator
securityManager.authenticator = $authenticator
 

 

2、AuthenticationStrategy(认证策略) 

       当应用程序配置了多个Realm时,ModularRealmAuthenticator将根据认证策略来判断认证成功或是失败。 例如,如果只有一个Realm验证成功,而其他Realm验证失败,那么这次认证是否成功呢?如果大多数的Realm验证成功了,认证是否就认为成功呢?或者,一个Realm验证成功后,是否还需要判断其他Realm的结果?认证策略就是根据应用程序的需要对这些问题作出决断。 
认证策略是一个无状态的组件,在认证过程中会经过4次的调用: 
  • 在所有Realm被调用之前
  • 在调用Realm的getAuthenticationInfo 方法之前
  • 在调用Realm的getAuthenticationInfo 方法之后
  • 在所有Realm被调用之后
       认证策略的另外一项工作就是聚合所有Realm的结果信息封装至一个AuthenticationInfo实例中,并将此信息返回,以此作为Subject的身份信息。 

 

Shiro有3中认证策略的具体实现: 

AtLeastOneSuccessfulStrategy只要有一个(或更多)的Realm验证成功,那么认证将被视为成功
FirstSuccessfulStrategy第一个Realm验证成功,整体认证将被视为成功,且后续Realm将被忽略
AllSuccessfulStrategy所有Realm成功,认证才视为成功


ModularRealmAuthenticator 内置的认证策略默认实现是AtLeastOneSuccessfulStrategy 方式,因为这种方式也是被广泛使用的一种认证策略。当然,你也可以通过配置文件定义你需要的策略,如:

 

[main]
...
authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
...

 

 
3、Realm的顺序 
       由刚才提到的认证策略,可以看到Realm在ModularRealmAuthenticator 里面的顺序对认证是有影响的。 ModularRealmAuthenticator 会读取配置在SecurityManager里的Realm。当执行认证是,它会遍历Realm集合,对所有支持提交的token的Realm调用getAuthenticationInfo 。 

因此,如果Realm的顺序对你使用的认证策略结果有影响,那么你应该在配置文件中明确定义Realm的顺序,如:  

 

blahRealm = com.company.blah.Realm
...
fooRealm = com.company.foo.Realm
...
barRealm = com.company.another.Realm

securityManager.realms = $fooRealm, $barRealm, $blahRealm
 

 

第三部分 Shiro授权

 

授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限。 
如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等。

 

一、授权的三要素 

授权有着三个核心元素:权限、角色和用户。 

 

权限 
权限是Apache Shiro安全机制最核心的元素。它在应用程序中明确声明了被允许的行为和表现。一个格式良好好的权限声明可以清晰表达出用户对该资源拥有的权限。 
大多数的资源会支持典型的CRUD操作(create,read,update,delete),但是任何操作建立在特定的资源上才是有意义的。因此,权限声明的根本思想就是建立在资源以及操作上。 
而我们通过权限声明仅仅能了解这个权限可以在应用程序中做些什么,而不能确定谁拥有此权限。 
于是,我们就需要在应用程序中对用户和权限建立关联。 
通常的做法就是将权限分配给某个角色,然后将这个角色关联一个或多个用户。 

 

权限声明及粒度 
Shiro权限声明通常是使用以冒号分隔的表达式。就像前文所讲,一个权限表达式可以清晰的指定资源类型,允许的操作,可访问的数据。同时,Shiro权限表达式支持简单的通配符,可以更加灵活的进行权限设置。 
下面以实例来说明权限表达式。 
可查询用户数据 
User:view 
可查询或编辑用户数据 
User:view,edit 
可对用户数据进行所有操作 
User:* 或 user 
可编辑id为123的用户数据 
User:edit:123 

 

角色 
Shiro支持两种角色模式: 
1、传统角色:一个角色代表着一系列的操作,当需要对某一操作进行授权验证时,只需判断是否是该角色即可。这种角色权限相对简单、模糊,不利于扩展。 
2、权限角色:一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有该权限。这种角色权限可以对该角色进行详细的权限描述,适合更复杂的权限设计。 
下面将详细描述对两种角色模式的授权实现。 

 

二、授权实现 

Shiro支持三种方式实现授权过程: 

  • 编码实现
  • 注解实现
  • JSP Taglig实现

1、基于编码的授权实现 

1.1  基于传统角色授权实现 

当需要验证用户是否拥有某个角色时,可以调用Subject 实例的hasRole*方法验证。 

 

Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("administrator")) {
    //show the admin button
} else {
    //don't show the button?  Grey it out?
}
 

 

相关验证方法如下: 

Subject方法描述
hasRole(String roleName)当用户拥有指定角色时,返回true
hasRoles(List<String> roleNames)按照列表顺序返回相应的一个boolean值数组
hasAllRoles(Collection<String> roleNames)如果用户拥有所有指定角色时,返回true


断言支持 
Shiro还支持以断言的方式进行授权验证。断言成功,不返回任何值,程序继续执行;断言失败时,将抛出异常信息。使用断言,可以使我们的代码更加简洁。 

 

Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is a bank teller and
//therefore allowed to open the account:
currentUser.checkRole("bankTeller");
openBankAccount();

 

 

断言的相关方法: 

Subject方法描述
checkRole(String roleName)断言用户是否拥有指定角色
checkRoles(Collection<String> roleNames)断言用户是否拥有所有指定角色
checkRoles(String... roleNames)对上一方法的方法重载

 

1.2 基于权限角色授权实现 
相比传统角色模式,基于权限的角色模式耦合性要更低些,它不会因角色的改变而对源代码进行修改,因此,基于权限的角色模式是更好的访问控制方式。 
它的代码实现有以下几种实现方式: 

 

1、基于权限对象的实现 
创建org.apache.shiro.authz.Permission的实例,将该实例对象作为参数传递给Subject.isPermitted()进行验证。 

 

Permission printPermission = new PrinterPermission("laserjet4400n", "print");
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted(printPermission)) {
    //show the Print button
} else {
    //don't show the button?  Grey it out?
}
Permission printPermission = new PrinterPermission("laserjet4400n", "print");
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted(printPermission)) {
    //show the Print button
} else {
    //don't show the button?  Grey it out?
}
 

 

相关方法如下: 

Subject方法描述
isPermitted(Permission p)Subject拥有制定权限时,返回treu
isPermitted(List<Permission> perms)返回对应权限的boolean数组
isPermittedAll(Collection<Permission> perms)Subject拥有所有制定权限时,返回true

 

 

2、 基于字符串的实现 
相比笨重的基于对象的实现方式,基于字符串的实现便显得更加简洁。 

 

Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted("printer:print:laserjet4400n")) {
    //show the Print button
} else {
    //don't show the button?  Grey it out?
}
 

 

使用冒号分隔的权限表达式是org.apache.shiro.authz.permission.WildcardPermission 默认支持的实现方式。 
这里分别代表了 资源类型:操作:资源ID 

类似基于对象的实现相关方法,基于字符串的实现相关方法: 
isPermitted(String perm)、isPermitted(String... perms)、isPermittedAll(String... perms) 

 

基于权限对象的断言实现 

 

Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is permitted
//to open a bank account:
Permission p = new AccountPermission("open");
currentUser.checkPermission(p);
openBankAccount();
 

 

基于字符串的断言实现 

 

Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is permitted
//to open a bank account:
currentUser.checkPermission("account:open");
openBankAccount();
 

 

断言实现的相关方法 

Subject方法说明
checkPermission(Permission p)断言用户是否拥有制定权限
checkPermission(String perm)断言用户是否拥有制定权限
checkPermissions(Collection<Permission> perms)断言用户是否拥有所有指定权限
checkPermissions(String... perms)断言用户是否拥有所有指定权限

 


2、基于注解的授权实现 

 

Shiro注解支持AspectJ、Spring、Google-Guice等,可根据应用进行不同的配置。 

相关的注解: 
@ RequiresAuthentication 
可以用户类/属性/方法,用于表明当前用户需是经过认证的用户。 

@RequiresAuthentication
public void updateAccount(Account userAccount) {
    //this method will only be invoked by a 
    //Subject that is guaranteed authenticated
    ...
}

 

@ RequiresGuest 
表明该用户需为”guest”用户 

@ RequiresPermissions 
当前用户需拥有制定权限 

 

@RequiresPermissions("account:create")
public void createAccount(Account account) {
    //this method will only be invoked by a Subject
    //that is permitted to create an account
    ...
}

 

@RequiresRoles 
当前用户需拥有制定角色 

@ RequiresUser 
当前用户需为已认证用户或已记住用户 

 

 

3、基于JSP  TAG的授权实现 
Shiro提供了一套JSP标签库来实现页面级的授权控制。 
在使用Shiro标签库前,首先需要在JSP引入shiro标签: 

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

 

下面一一介绍Shiro的标签: 
guest标签 
验证当前用户是否为“访客”,即未认证(包含未记住)的用户 

<shiro:guest>
    Hi there!  Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today!</shiro:guest>

 

user标签 
认证通过或已记住的用户

<shiro:user>
    Welcome back John!  Not John? Click <a href="login.jsp">here<a> to login.</shiro:user>

 
 authenticated标签 

已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。 
<shiro:authenticated><a href="updateAccount.jsp">Update your contact information</a>.</shiro:authenticated>

 

notAuthenticated标签 
未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。 

<shiro:notAuthenticated>
    Please <a href="login.jsp">login</a> in order to update your credit card information.</shiro:notAuthenticated>

 

principal 标签 
输出当前用户信息,通常为登录帐号信息 

Hello, <shiro:principal/>, how are you today?

 

hasRole标签 
验证当前用户是否属于该角色 

<shiro:hasRole name="administrator"><a href="admin.jsp">Administer the system</a></shiro:hasRole>

 

lacksRole标签 
与hasRole标签逻辑相反,当用户不属于该角色时验证通过 

<shiro:lacksRole name="administrator">
    Sorry, you are not allowed to administer the system.</shiro:lacksRole>

 

hasAnyRole标签 
验证当前用户是否属于以下任意一个角色。 

<shiro:hasAnyRoles name="developer, project manager, administrator">
    You are either a developer, project manager, or administrator.</shiro:lacksRole>

 

hasPermission标签 
验证当前用户是否拥有制定权限 

<shiro:hasPermission name="user:create"><a href="createUser.jsp">Create a new User</a></shiro:hasPermission>

 

lacksPermission标签 
与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过  

<shiro:hasPermission name="user:create"><a href="createUser.jsp">Create a new User</a></shiro:hasPermission>

 

 

三、Shiro授权的内部处理机制 
 
1、在应用程序中调用授权验证方法(Subject的isPermitted*或hasRole*等) 
2、Sbuject的实例通常是DelegatingSubject类(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用相应的isPermitted*或hasRole*方法。 
3、接下来SecurityManager会委托内置的Authorizer的实例(默认是ModularRealmAuthorizer 类的实例,类似认证实例,它同样支持一个或多个Realm实例认证)调用相应的授权方法。 
4、每一个Realm将检查是否实现了相同的 Authorizer 接口。然后,将调用Reaml自己的相应的授权验证方法。 

当使用多个Realm时,不同于认证策略处理方式,授权处理过程中: 
1、当调用Realm出现异常时,将立即抛出异常,结束授权验证。 
2、只要有一个Realm验证成功,那么将认为授权成功,立即返回,结束认证。 

 

 

第四部分 Ralm的实现

 

在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO. 

一、认证实现 
正如前文所提到的,Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法。 
该方法主要执行以下操作: 
1、检查提交的进行认证的令牌信息 
2、根据令牌信息从数据源(通常为数据库)中获取用户信息 
3、对用户信息进行匹配验证。 
4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。 
5、验证失败则抛出AuthenticationException异常信息。 

而在我们的应用程序中要做的就是自定义一个Realm类,继承AuthorizingRealm抽象类,重载doGetAuthenticationInfo (),重写获取用户信息的方法。 

 

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		User user = accountManager.findUserByUserName(token.getUsername());
		if (user != null) {
			return new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), getName());
		} else {
			return null;
		}
}
 

 

  二、授权实现 

而授权实现则与认证实现非常相似,在我们自定义的Realm中,重载doGetAuthorizationInfo()方法,重写获取用户权限的方法即可。 

 

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		String userName = (String) principals.fromRealm(getName()).iterator().next();
		User user = accountManager.findUserByUserName(userName);
		if (user != null) {
			SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
			for (Group group : user.getGroupList()) {
				info.addStringPermissions(group.getPermissionList());
			}
			return info;
		} else {
			return null;
		}
}
  

 

 

 第五部分 Shiro配置说明

 

Apache Shiro的配置主要分为四部分: 

  • 对象和属性的定义与配置
  • URL的过滤器配置
  • 静态用户配置
  • 静态角色配置

其中,由于用户、角色一般由后台进行操作的动态数据,因此Shiro配置一般仅包含前两项的配置。 

Apache Shiro的大多数组件是基于POJO的,因此我们可以使用POJO兼容的任何配置机制进行配置,例如:Java代码、Sping XML、YAML、JSON、ini文件等等。下面,以Spring XML的配置方式为例,并且对其中的一些配置参数进行一些简单说明。 

Shiro对象的配置: 
主要是对Shiro各个组件的实现进行定义配置,主要组件在前文已做过简单介绍,这里不再一一说明。

<bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager"><property name="cacheManager" ref="cacheManager"/><property name="sessionMode" value="native"/><!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. --><property name="realm" ref="myRealm"/><property name="sessionManager" ref="sessionManager"/> </bean>

 

  Shiro过滤器的配置 

Shiro主要是通过URL过滤来进行安全管理,这里的配置便是指定具体授权规则定义。
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><property name="loginUrl" value="/login.jsp"/><property name="successUrl" value="/home.jsp"/><property name="unauthorizedUrl" value="/unauthorized.jsp"/> --><property name="filterChainDefinitions"><value>
            # some example chain definitions:
            /admin/** = authc, roles[admin]
            /docs/** = authc, perms[document:read]
            /** = authc
            # more URL-to-FilterChain definitions here</value></property></bean>

  

 

URL过滤器配置说明: 
Shiro可以通过配置文件实现基于URL的授权验证。FilterChain定义格式: 
URL_Ant_Path_Expression = Path_Specific_Filter_Chain 
每个URL配置,表示匹配该URL的应用程序请求将由对应的过滤器进行验证。 
例如: 
[urls] 
/index.html = anon 
/user/create = anon 
/user/** = authc 
/admin/** = authc, roles[administrator] 
/rest/** = authc, rest 
/remoting/rpc/** = authc, perms["remote:invoke"] 

URL表达式说明 
1、URL目录是基于HttpServletRequest.getContextPath()此目录设置 
2、URL可使用通配符,**代表任意子目录 
3、Shiro验证URL时,URL匹配成功便不再继续匹配查找。所以要注意配置文件中的URL顺序,尤其在使用通配符时。 

Filter Chain定义说明 
1、一个URL可以配置多个Filter,使用逗号分隔 
2、当设置多个过滤器时,全部验证通过,才视为通过 
3、部分过滤器可指定参数,如perms,roles 

Shiro内置的FilterChain 

Filter NameClass
anonorg.apache.shiro.web.filter.authc.AnonymousFilter
authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
portorg.apache.shiro.web.filter.authz.PortFilter
restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter
sslorg.apache.shiro.web.filter.authz.SslFilter
userorg.apache.shiro.web.filter.authc.UserFilter

 

 

本文转载自kdboy的博客的系列文章,文章地址为: http://kdboy.iteye.com/blog/1154644

 

 



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


ITeye推荐



hibernate中get和load,find的区别

$
0
0

get和load方式是根据id取得一个记录
下边详细说一下get和load的不同,因为有些时候为了对比也会把find加进来。

1.从返回结果上对比:
load方式检索不到的话会抛出org.hibernate.ObjectNotFoundException异常
get方法检索不到的话会返回null

2.从检索执行机制上对比:
get方法和find方法都是直接从数据库中检索
而load方法的执行则比较复杂
■ 首先查找session的persistent Context中是否有缓存,如果有则直接返回
■ 如果没有则判断是否是lazy,如果不是直接访问数据库检索,查到记录返回,查不到抛出异常
■ 如果是lazy则需要建立代理对象,对象的initialized属性为false,target属性为null
■ 在访问获得的代理对象的属性时,检索数据库,如果找到记录则把该记录的对象复制到代理对象的target上,并将initialized=true,如果找不到就抛出异常。

3.根本区别说明
■ 如果你使用load方法,hibernate认为该id对应的对象(数据库记录)在数据库中是一定存在的,所以它可以放心的使用,它可以放心的使用代理来延迟加载该对象。在用到对象中的其他属性数据时才查询数据库,但是万一数据库中不存在该记录,那没办法,只能抛异常。所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时(注意:这就是由于“延迟加载”在作怪)。

由于session中的缓存对于hibernate来说是个相当廉价的资源,所以在load时会先查一下session缓存看看该id对应的对象是否存在,不存在则创建代理。所以如果你知道该id在数据库中一定有对应记录存在就可以使用load方法来实现延迟加载。

■ 对于get方法,hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查数据库,数据库中没有就返回null。

对于load和get方法返回类型:虽然好多书中都这么说:“get()永远只返回实体类”,但实际上这是不正确的,get方法如果在 session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。

get方法首先查询session缓存,没有的话查询二级缓存,最后查询数据库;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库。

4.简单总结

总之对于get和load的根本区别,一句话,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,hibernate一定要获取到真实的数据,否则返回null。



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


ITeye推荐



如何用手机拍出美好夜景

$
0
0

美好的照片不只是用各色图片滤镜软件「P」出来的,更多是拍出来的。在移动互联网飞速发展的今天,智能手机早已取代了卡片相机成为了最受欢迎的街拍设备。当然,好照片还是得靠拍摄者的摄影技术,现在的智能手机,普遍硬件素质不差,如何最大程度发挥它的潜能,这就是我们今天想要讲述的。

今天早些时候,笔者在个人微博中晒了一张拍的还算可以的夜景,让大家来猜拍摄设备。

因为之前网上相传锤子手机拍照效果很棒,再加上身为科技媒体的笔者这两天一直在微博中大谈特谈关于锤子手机的一切,所以网友自然会联想到,这张照片是锤子手机所拍摄的。

锤子手机采用了索尼最强的 IMX214 作为相机元件,结合富士通的 Milbeaut Mobile® 完整图像处理解决方案,成像效果自然不差,但是,笔者要告诉你,我这张照片其实是魅族 MX3 所拍,MX3 虽然已经是去年发布的产品,但其采用的索尼 IMX179 相机元件和富士通独立 ISP 在硬件上并不落后,虽然像素仅有区区 800W 但基本成像素质并不逊于市面上多款 1300W 的产品。

好了废话不多说,我们一起来看一看,如何用智能手机拍摄夜景。

首先,我们来看一看智能手机相机的设置选项。

授人与鱼,不如授人以渔。首先我们需要知道这些设置选项都是什么意思,魅族相机的设置选项颇为常规,仅有常见的「曝光控制」、「HDR」、「白平衡」、「快门速度」、「感光度(ISO)」等选项可供设置,我们依次来为大家解释一下,这些选项分别是做什么用的。

 

  • 曝光补偿是相机中最常见的设置项目,其目的就是有意识地变更相机自动演算出的「合适」曝光参数,让照片更明亮或者更昏暗。举个例子,如果你在取景时发现画面过亮,那么你便可以适当减低曝光补偿,反之亦然。
  • HDR 中文意思是高动态光照渲染。一般我们拍照照顾了高光区域的曝光,暗部细节就会丢失,而照顾了暗部细节,高光部分就会曝光过度,为了解决这一问题,我们使用HDR,简单地说,就是一张照片中,既包含了高光部分,又保留了暗部细节。
  • 快门速度在相机中是极其重要的设置选项之一,之前手机一般都是自动控制快门速度的,近几年随着手机相机技术进步,我们也可以在手机中使用这一相机才具备的功能。快门的速度控制着相机的进光量,慢速的快门可以让相机进入更多的光,从而提升夜景拍摄的亮度。
  • 感光度(ISO)可以通过控制相机感光元件的感光程度,来控制画质,和整个相片的明暗效果。一般情况下,提升相机感光程度是通过以下两种方式来实现的。
  1. 强行提高每个像素点的亮度和对比度。
  2. 使用多个像素点共同完成原来只要一个像素点来完成的任务。

了解了以上几点设置选项的含义,我们就来看一下这样一张夜景照片要如何对手机进行设置呢。

因为拍摄时的时间已经是夜晚七八点钟,夜幕像清水中泼洒的墨汁,迅速蔓延开来,天空中隐约还保留丝丝晴光。如果直接要拍摄的话,会是这样一番景象。

从这张照片中我们可以看出,亮部细节流失严重,画面噪点较为明显。这时我们需要通过降低感光度(ISO)来保证噪点,顺便可以降低一格曝光补偿来让暗部细节更凸现。

我的天,这样拍出来噪点是下去了,可是画面一片漆黑,什么也看不见。这时候我们就需要通过降低快门速度来控制进光量。

我们将手机的快门速度设置为 1/4 S(视情况而定,如环境更暗则可以更慢一点),因为快门速度变慢,手机的稳定性则变得尤其重要,我们可以依靠玻璃、栏杆等环境工具保证相机的稳定性,避免因抖动而带来的画面扭曲模糊。

咔嚓!一张漂亮的夜景作品就完成了~快拿去发朋友圈吧~

中国手机用户增长率遭遇“腰斩” 市场正接近饱和

$
0
0

  来自Statista的数据显示,中国手机整体用户规模的增速正在逐月放缓。从2013年4月至2014年4月的统计区间中,中国手机用户月度环比增速呈现明显下降趋势:

  1.最初5个月的增速保持在0.8%至1%之间;随后七个月,增速快速滑落至0.5%以下;

  2.2014年3月曾有过短暂的增速反弹(至0.7%),但4月又迅速回落;

  3.跨年比较首尾两个月份的增速,会发现落差超过3倍。

  在这份统计之外,工信部近期也公布了5月份中国手机用户数量的官方数据。相比四月,增长幅度仅为0.36%,依然处在0.5%之下。

  造成这一现象的原因似乎不难被找出。来自第三方的数据显示,2012年中国手机通讯用户数占到总人口的89%;而2014年官方数据显示,这一比例已增长至90.8%。

  是的,中国手机市场正在接近饱和。

  尽管中高收入人群会有明显的周期性换机需求,但更广泛的大众市场对于频繁更换手机依然谨慎。因此,手机用户普及接近天花板,这一数据对整个市场潜力评估,仍具有很高的参考意义。

  同样,寄望于智能手机更新换代形成新商机的企业也需要谨慎,因为中国市场的智能手机渗透率同样很高。来自2012年的另一份数据显示,中国智能手机用户占比达到66%,即三分之二的手机用户已经至少完成了一次设备智能化升级;而这一数据在新兴市场的俄罗斯、巴西和印度,分别仅为37%、36%和10%。

  ——这还仅是2012年的统计。过去两年是智能手机高速发展期,可以推测今天的中国智能手机比例会更高。

  唯一可以缓解这种市场担忧的证据来自我们的近邻韩国。目前韩国的手机用户占人口密度比例已超过99%,但韩国人乐观地表示,他们的市场依然有“增长空间”。

Spring MVC 与 web开发

$
0
0

项目组用了 Spring MVC 进行开发,觉得对里面的使用方式不是很满意,就想,如果是我来搭建开发环境,我会怎么做?下面就是我的想法,只关注于 MVC 的 View 层。

一、统一的响应格式

现在基本上都是用 ajax 来调用后台接口,拿到 json格式的数据再展示,有的人直接返回数据,却没有考虑异常的情况,我觉得返回的报文里必须包含表示可能的异常信息的数据和业务响应数据。我定义了下面这个类来表示报文格式:

/**
 * 统一的 HTTP 响应格式。<br/>
 * code 为 "ok" 表示业务调用成功,否则是失败的错误码,如果有多个则以逗号分隔。<br/>
 * data 是业务数据,如果失败了则是 null。
 * 
 * @author http://coderbee.net
 *
 */
public class RespBody {
    public static final String OK_CODE = "ok";
    private final String code;
    private final Object data;

    private static final RespBody OK = new RespBody(OK_CODE, null);

    private RespBody(String code, Object data) {
        this.code = code;
        this.data = data;
    }

    public static RespBody ok() {
        return OK;
    }

    public static RespBody ok(Object data) {
        return new RespBody("ok", data);
    }

    public static RespBody error(String code) {
        return new RespBody(code, null);
    }

    public static RespBody error(String code, Object msg) {
        return new RespBody(code, msg);
    }

    public String getCode() {
        return code;
    }

    public Object getData() {
        return data;
    }
}

这个类提供了一些静态方法来快速构建响应报文,这也是很重要的一个设计:用静态工厂方法而不是构造函数。

这里的 code不应该是直接的错误提示信息,应该只是简单的错误编码,这样不同的客户端都可以调用这个 API,然后再根据错误编码、客户端语言和自己的客户端特性选择合适的错误提示信息和提示方式。

二、统一的异常处理

很多人都不考虑异常的情况,导致异常栈直接抛到响应里,这是不友好也不安全的。统一的异常处理是必须的。

我定义了一个 BaseController

@Controller
public class BaseController {
    protected final static Logger logger = LoggerFactory
            .getLogger("controller");

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public RespBody exceptionHandler(Exception ex) {
        return RespBody.error("exception", ex.getMessage());
    }
}

它的作用很简单,就是定义了一个统一的异常处理逻辑。Spring MVC对异常处理的逻辑很好,如果某个 Controller类没有提供带 @ExceptionHandler注解的方法,则会查找父类是否有这种方法,所以继承自这个 BaseControllerController都自动获得异常处理能力。

三、统一的参数校验

参数校验是必须的,而且必须放在服务器端来做,客户端的校验都是可以绕过的

Spring MVC 当然也支持参数校验,在 Spring MVC 的配置文件里加入 <mvc:annotation-driven />即可以开启注解校验。

但 Spring MVC 的参数校验有些局限:

  • 不支持对基本类型和 String类型的参数进行校验,也就是只支持对 POJO 校验,这个非常不友好,如果一个接口只有很少的几个参数都必须定义一个 POJO 是很恼人的,要么就得手动校验,写一些 if 分支;
  • 每个POJO后面都得有一个 BindingResult的参数,作为对 POJO 的校验结果。

而且在每个方法里都必须对 BindingResult进行检测,来判断参数是否合法。

在 AOP 里进行参数校验

借助 Spring 对 AOP 的支持,我们可以在 AOP 里对请求的 Controller的方法进行拦截,做参数校验,如果校验不合格,则直接返回(因为我们已经有了统一的响应格式)。

在 AOP 里,我们可以用 Hibernate-Validator 进行手动校验,而不是通过 Spring-MVC 进行校验,这样我们就不需要在每个 POJO 后面放一个 BindingResult参数,且 Hibernate-Validator 支持对基本类型和 String类型的参数进行校验。

下面的代码是在 Hibernate-Validator-4.2.0-Final、validation-api-1.0.0-GA 下测试的:

首先定义一个 BindingResultHandler类,它的方法 validate校验请求的 Controller的方法的参数是否合格,如果合格则继续调用业务逻辑,否则返回错误提示。

public class BindingResultHandler {

    public Object validate(ProceedingJoinPoint pjp) throws Throwable {
        Object target = pjp.getTarget();

        MethodSignature joinPointObject = (MethodSignature) pjp.getSignature();
        Method method = joinPointObject.getMethod();

        MethodValidator validator = Validation
                .byProvider(HibernateValidator.class).configure()
                .buildValidatorFactory().getValidator()
                .unwrap(MethodValidator.class);

        Set<MethodConstraintViolation<Object>> violations = validator
                .validateAllParameters(target, method, pjp.getArgs(),
                        new Class[] {});

        if (!violations.isEmpty()) {
            StringBuilder sb = new StringBuilder(128);
            for (ConstraintViolation<Object> violation : violations) {
                sb.append(',').append(violation.getMessage());
            }
            return RespBody.error(sb.substring(1), "param validation failed .");
        }

        return pjp.proceed();
    }
}

AOP 配置:

<bean id="bindingResultHandler" class="net.coderbee.demo.controller.validation.BindingResultHandler" /><aop:config><aop:aspect id="aspectBindingResult" ref="bindingResultHandler"><aop:pointcut id="bindingResultHandlerPointcut"
            expression="execution(public * net.coderbee.demo.controller..*Controller.*(..))" /><aop:around method="validate" pointcut-ref="bindingResultHandlerPointcut" /></aop:aspect></aop:config>

这样在 Controller 里就只需做注解不能完成的校验了。

@Controller
public class UserController extends BaseController {
    @ResponseBody
    @RequestMapping(value = "/test/valids")
    public RespBody valids(@Valid User user, @Valid Address address) {

        return RespBody.ok(user);
    }
}

这样的代码会简洁很多。

Viewing all 15843 articles
Browse latest View live