编程语言简史:给C语言做个演示程序,结果他们弄出了一个操作系统UNIX

编程语言有上千种,但是流行的不过10来种,那些我们经常使用的编程语言都是谁在什么时候创造出来的呢?Casper Beyer 为我们进行了整理。

1800年

Joseph Marie Jacquard 教会了一台织布机读穿孔卡片,制造出了第一个高度多线程的处理单元。他的发明受到了预见天网(Skynet)诞生的纺织工人的强烈反对。

1842年

Ada Lovelace(英国诗人拜伦之女)为计算程序拟定“算法”,写作的第一份“程序设计流程图”,被珍视为“第一位给计算机写程序的人”。稍微有点不便的是当时还没有计算机呢。

1936年

阿兰·图灵被称为计算机科学之父,人工智能之父。但英国法庭却并不认可,还判处对他进行化学阉割。

女皇后来宽恕了他,但不幸的是当时他已经过世很久了。

1936年

Alonzo Church(算法理论重要奠基人)发明了lambda算子,跟图灵生活在同样的时代,但是他在时代的另一边,也并没有被女王阉割。

1957年

John Backus创建了FORTRAN语言,这真正是程序员使用的第一种语言。

1959年

Grace Hopper发明了第一门针对企业面向商业的编程语言,并且把这门语言叫做“面向商业的通用语言(common business-oriented language)”,简称COBOL。

1964年

John Kemeny 和 Thomas Kurtz 认为编程太难了,需要回归本源,他们把自己的编程语言叫做BASIC。

1970年

Niklaus Wirth开发了多种语言,最后流行起来的是PASCAL。他喜欢开发语言。

他还发明了让摩尔定律变得过时的Wirth定律(软件变慢的速度比硬件变快的速度更快),因为软件开发者会编写出连大型主机也没法跟上的臃肿软件。

这在后来被证明是正确的——在Electron.js被发明出来后

1972年

Dennis Ritchie在贝尔实验室上班上到无聊了,于是他决定写出带有花括号的C语言,这门语言取得了巨大成功。随后他又增加了分段错误等对开发者友好的功能来辅助提高生产率。

折腾完这门语言之后他还有时间,于是他跟在贝尔实验室的伙计决定给C语言做个演示程序,结果他们弄出了一个操作系统,UNIX。

1980年

Alan Kay发明了一门面向对象语言,他把这门语言叫做Smalltalk,在Smalltalk中一切都是对象,甚至一个对象也是对象。没人真正搞得清楚small talk是什么意思。

1983年

Jean Ichbiah注意到Ada Lovelace的程序从来都没有实际运行过,决定以她的名字开发一门语言,但是这门语言还是没有跑起来。

1983年

Bjarne Stroustrup 注意到C在编译方面花的时间还不够多,于是他把自己能想到的每一项功能都增加了进去,然后称之为C++。

每一个地方的程序员都接受了它,因为这样他们在工作的时候找借口看阿猫阿狗视频和xkcd漫画就显得比较有诚意了。

1986年

Brac Box 和 Tol Move决定在Smalltalk的基础上制作一个C语言的不可读版本,他们把这门语言叫做Objective-C,但是没人弄得清楚它的语法。

1987年

Larry Wall有宗教经验,他成为了一名牧师,并且把Perl变成了一种教义。

1991年

Guido van Rossum不喜欢花括号,于是他发明了Python,语法选择的灵感来源自Monty Python(巨蟒剧团)和Flying Circus(飞行马戏团)。

1993年

Roberto Ierusalimschy和他的朋友认为自己需要一个巴西本地化的脚本语言,在本地化期间发生了一个错误,这个错误会把指针从1而不是0开始计算,他们把这门语言叫做Lua。

1994年

Rasmus Lerdorf给他个人主页的CGI脚本做了一个模板引擎,后来他把自己的资料都放到了网上。

世界决定将这些东西用到一切,Rasmus于是匆忙地将一些数据库绑定做了进去,并把这门语言叫做PHP。

1995年

Yukihiro Matsumoto不是很高兴,因为他注意到其他程序员不是很高兴。他创建了Ruby来让程序员高兴。在他创建了Ruby后“Matz”高兴了,Ruby社区高兴了,每个人都高兴了。

1995年

Brendan Eich利用周末时间设计了一门语言,打算用这门语言来为全世界的每一个主流浏览器乃至于最终的Skynet都提供动力。

他先是找到了Netscape然后说这门语言叫做LiveScript,但在代码评审期间Java变得流行起来,所以他们决定最好还是用花括号,然后就把它更名为JavaScript。

结果表明,Java却是一个会让他们惹上麻烦的商标,JavaScript随后更名为ECMAScript,但大家还是把它叫做JavaScript。

1996年

James Gosling发明了Java,第一们真正过于繁琐的面向对象语言,在这里设计模式完全压倒了实用主义。

于是就诞生了超级有效的管理器提供商、容器提供商、服务提供商、单一管理器提供商模式。

2001年

Anders Hejlsberg重新发明了Java然后把它叫做C#,因为用C来编程感觉要比Java酷。每个人都喜欢这个新版本的Java,因为它完全不像Java。

2005年

David Hanselmeyer Hansen创建了一个web框架叫做Ruby on Rails,从此大家不再记得Ruby和Rails是两个独立的东西了。

2006年

John Resig为JavaScript写了一个帮助库,每个人都以为那是一门语言,从此从互联网上拷贝粘贴jQuery代码就成为了一门职业。

2009年

Ken Thompson 和 Rob Pike 决定做一门类似C那样的语言,但要有更安全的装置,还要有更好的卖相,并且把Gopher(囊鼠)作为吉祥物。

他们把这门语言成为Go,并把它做成开源然后另外卖Gopher商标的护膝和头盔作为收入来源。

2010年

Graydon Hoare也想把语言做成C那样,他称之为Rust。每个人都要求马上用Rust把软件的每一块都重写一遍。Graydon希望做点更有亮点的事情,于是开始为苹果开发Swift。

2012年

Anders Hjelsberg希望在web浏览器里面写C#,于是他设计出TypeScript,这东西其实是JavaScript,但里面有了更多的Java的东西。

2013年

Jeremy Ashkenas想要像Ruby开发者一样快乐,于是他创建了CoffeeScript,这东西编译后像JavaScript但是样子又更像Ruby。Jerry从来都没有变得像Matz和Ruby开发者那样真正快乐。

2014年

Chris Lattner做Swift的时候,其主要的设计目标就是不要成为Objective-C,最后它看起来像Java。

原文链接:https://medium.com/@caspervonb/a-brief-totally-accurate-history-of-programming-languages-cd93ec806124

译者:36Kr 编译组   编辑:郝鹏程。

来源:编程语言简史:有人讨厌花括号,于是他发明了Python

https://www.oschina.net/news/92787/a-brief-totally-accurate-history-of-programming-languages

J2EE框架 Spring

J2EE框架 Spring 推荐

J2EE框架 面向方面AOP/IoC Web框架
Apache
Java
跨平台

Spring Framework 是一个开源的Java/Java EE全功能栈(full-stack)的应用程序框架,以Apache许可证形式发布,也有.NET平台上的移植版本。该框架基于 Expert One-on-One Java EE Design and Development(ISBN 0-7645-4385-7)一书中的代码,最初由 Rod Johnson 和 Juergen Hoeller等开发。Spring Framework 提供了一个简易的开发方式,这种开发方式,将避免那些可能致使底层代码变得繁杂混乱的大量的属性文件和帮助类。

Spring 中包含的关键特性:

  • 强大的基于 JavaBeans 的采用控制翻转(Inversion of Control,IoC)原则的配置管理,使得应用程序的组建更加快捷简易。
  • 一个可用于从 applet 到 Java EE 等不同运行环境的核心 Bean 工厂。
  • 数据库事务的一般化抽象层,允许宣告式(Declarative)事务管理器,简化事务的划分使之与底层无关。
  • 内建的针对 JTA 和 单个 JDBC 数据源的一般化策略,使 Spring 的事务支持不要求 Java EE 环境,这与一般的 JTA 或者 EJB CMT 相反。
  • JDBC 抽象层提供了有针对性的异常等级(不再从SQL异常中提取原始代码), 简化了错误处理, 大大减少了程序员的编码量. 再次利用JDBC时,你无需再写出另一个 ‘终止’ (finally) 模块. 并且面向JDBC的异常与Spring 通用数据访问对象 (Data Access Object) 异常等级相一致.
  • 以资源容器,DAO 实现和事务策略等形式与 Hibernate,JDO 和 iBATIS SQL Maps 集成。利用众多的翻转控制方便特性来全面支持, 解决了许多典型的Hibernate集成问题. 所有这些全部遵从Spring通用事务处理和通用数据访问对象异常等级规范.
  • 灵活的基于核心 Spring 功能的 MVC 网页应用程序框架。开发者通过策略接口将拥有对该框架的高度控制,因而该框架将适应于多种呈现(View)技术,例如 JSP,FreeMarker,Velocity,Tiles,iText 以及 POI。值得注意的是,Spring 中间层可以轻易地结合于任何基于 MVC 框架的网页层,例如 Struts,WebWork,或 Tapestry。
  • 提供诸如事务管理等服务的面向方面编程框架。

在设计应用程序Model时,MVC 模式(例如Struts)通常难于给出一个简洁明了的框架结构。Spring却具有能够让这部分工作变得简单的能力。程序开发员们可以使用Spring的 JDBC 抽象层重新设计那些复杂的框架结构。

React 定义组件的参数-生命周期

定义组件的参数-生命周期
创建期:getDefaultProps
创建期:getInitialState
创建期:componentWillMount
创建期:componentDidMount
存在期:componentWillReceiveProps
存在期:shouldComponentUpdate
存在期:componentWillUpdate
存在期:componentDidUpdate
销毁&清理期:componentWillUnmount

定义组件的参数-生命周期
生命周期相关参数,是React定义组件时提供的一系列处理函数(钓子函数),这些函数会在组件生命周期的某个阶段调用。

创建期:getDefaultProps

object getDefaultProps()

创建期:getInitialState

object getInitialState()
在组件挂载前(即:创建期)调用一次,其返回值将做为this.state的初始值。

getInitialState()方法会组件类创建的时候调用一次,其返回值会被缓存下来。该方法用于设置props属性的默认值,但仅对于非必须属性。如果父组件没有指定props中的某个值,此返回对象中的相应属性将会合并到this.props。

getInitialState()方法会在组件实例创建前调用,这时还不能使用this.props属性,且其返回对象是在所有实例间共享的。

创建期:componentWillMount

componentWillMount()
componentWillMount()服务器端和客户端都只调用一次,在初始化渲染执行之前被调用。如果在这个方法内调用setState()方法,render()方法将会收到更新后的state,也就是说这是我做在组件渲染前最后一个修改state的机会。

创建期:componentDidMount

componentDidMount()
componentDidMount()会在组件初始化(渲染完成)后立即调用一次,我们一般在这个方法中使用this.getDOMNode()方法访问原始DOM。

存在期:componentWillReceiveProps

componentWillReceiveProps(object nextProps)

componentWillReceiveProps在将要接受新的props时被调用
componentWillReceiveProps()方法会在组件生命周期的存在期调用,当组件感知到props属性改变,会调用此方法。render()方法将会在其后调用,这时我们可以通过this.setState()来阻止组件的再次渲染。

存在期:shouldComponentUpdate

boolean shouldComponentUpdate(object nextProps, object nextState)
shouldComponentUpdate()方法发生在组件生命周期的存在器,在组件收到新的props或state。在这个方法中,我们可以访问组件的props和state属性,通过这两个属性可以确认组件是否需要更新,如果不需要更新,则返回false,则其后的方法将不会在执行。如:

shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.id !== this.props.id;
}

存在期:componentWillUpdate

componentWillUpdate(object nextProps, object nextState)
componentWillUpdate()会在收到新的props或state后调用,类似componentWillMount()。

存在期:componentDidUpdate

componentDidUpdate(object prevProps, object prevState)
componentDidUpdate()会在组件重新渲染后立即被调用,当我们需要在组件重新渲染后操作DOM则需要使用这个方法。

销毁&清理期:componentWillUnmount

componentWillUnmount()
componentWillUnmount()是组件销毁&清理期唯一调用的方法,它会在组件从DOM中移除时被调用,这时我们可以清理一些状态或清理在componentDidMount中创建的DOM元素。

Hibernate5 注解 命名策略

对于hibernate注解实体中属性对应数据库表的列名,怎么命名的问题,我们肯定不愿一个个属性去配置

Hibernaet5.1 之前  在applicationContext.xml中的sessionFactory中配置

<property name=”namingStrategy”>
<bean class=”org.hibernate.cfg.ImprovedNamingStrategy”></bean>
</property>

5.1开始

hibernate.ejb.naming_strategy将不再被支持,而是被替换成了两个属性:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy

对于physical_naming_strategy有两个常用的配置:

org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

对于PhysicalNamingStrategyStandardImplDefaultNamingStrategy的效果,

对于SpringPhysicalNamingStrategyImprovedNamingStrategy的效果。

法1:在sessionFactory的bean里配置

<property name=”PhysicalNamingStrategy”>
<bean class=”org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl”></bean>
</property>

法2:在sessionFactory bean的hibernateProperties property中配置

<prop key=”hibernate.physical_naming_strategy”>org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl</prop>

org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy 是 spring boot 包提供地

<property name=”ImplicitNamingStrategy”>
<bean class=”org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl” />
</property>

或是

<prop key=”hibernate.implicit_naming_strategy”>org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl</prop>

基于Dubbo框架构建分布式服务 集群容错

Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配置就能够实现分布式服务调用,也就是说服务提供方(Provider)发布的服务可以天然就是集群服务,比如,在实时性要求很高的应用场景下,可能希望来自消费方(Consumer)的调用响应时间最短,只需要选择Dubbo的Forking Cluster模式配置,就可以对一个调用请求并行发送到多台对等的提供方(Provider)服务所在的节点上,只选择最快一个返回响应的,然后将调用结果返回给服务消费方(Consumer),显然这种方式是以冗余服务为基础的,需要消耗更多的资源,但是能够满足高实时应用的需求。
有关Dubbo服务框架的简单使用,可以参考我的其他两篇文章(《基于Dubbo的Hessian协议实现远程调用》,《Dubbo实现RPC调用使用入门》,后面参考链接中已给出链接),这里主要围绕Dubbo分布式服务相关配置的使用来说明与实践。

Dubbo服务集群容错

假设我们使用的是单机模式的Dubbo服务,如果在服务提供方(Provider)发布服务以后,服务消费方(Consumer)发出一次调用请求,恰好这次由于网络问题调用失败,那么我们可以配置服务消费方重试策略,可能消费方第二次重试调用是成功的(重试策略只需要配置即可,重试过程是透明的);但是,如果服务提供方发布服务所在的节点发生故障,那么消费方再怎么重试调用都是失败的,所以我们需要采用集群容错模式,这样如果单个服务节点因故障无法提供服务,还可以根据配置的集群容错模式,调用其他可用的服务节点,这就提高了服务的可用性。
首先,根据Dubbo文档,我们引用文档提供的一个架构图以及各组件关系说明,如下所示:

上述各个组件之间的关系(引自Dubbo文档)说明如下:

  • 这里的Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。
  • Directory代表多个Invoker,可以把它看成List,但与List不同的是,它的值可能是动态变化的,比如注册中心推送变更。
  • Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
  • Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。
  • LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。

我们也简单说明目前Dubbo支持的集群容错模式,每种模式适应特定的应用场景,可以根据实际需要进行选择。Dubbo内置支持如下6种集群模式:

  • Failover Cluster模式

配置值为failover。这种模式是Dubbo集群容错默认的模式选择,调用失败时,会自动切换,重新尝试调用其他节点上可用的服务。对于一些幂等性操作可以使用该模式,如读操作,因为每次调用的副作用是相同的,所以可以选择自动切换并重试调用,对调用者完全透明。可以看到,如果重试调用必然会带来响应端的延迟,如果出现大量的重试调用,可能说明我们的服务提供方发布的服务有问题,如网络延迟严重、硬件设备需要升级、程序算法非常耗时,等等,这就需要仔细检测排查了。
例如,可以这样显式指定Failover模式,或者不配置则默认开启Failover模式,配置示例如下:

1 <dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService"version="1.0.0"
2      cluster="failover" retries="2" timeout="100"ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
3      <dubbo:method name="queryRoomUserCount" timeout="80" retries="2" />
4 </dubbo:service>

上述配置使用Failover Cluster模式,如果调用失败一次,可以再次重试2次调用,服务级别调用超时时间为100ms,调用方法queryRoomUserCount的超时时间为80ms,允许重试2次,最坏情况调用花费时间160ms。如果该服务接口org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService还有其他的方法可供调用,则其他方法没有显式配置则会继承使用dubbo:service配置的属性值。

  • Failfast Cluster模式

配置值为failfast。这种模式称为快速失败模式,调用只执行一次,失败则立即报错。这种模式适用于非幂等性操作,每次调用的副作用是不同的,如写操作,比如交易系统我们要下订单,如果一次失败就应该让它失败,通常由服务消费方控制是否重新发起下订单操作请求(另一个新的订单)。

  • Failsafe Cluster模式

配置值为failsafe。失败安全模式,如果调用失败, 则直接忽略失败的调用,而是要记录下失败的调用到日志文件,以便后续审计。

  • Failback Cluster模式

配置值为failback。失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

  • Forking Cluster模式

配置值为forking。并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。

  • Broadcast Cluster模式

配置值为broadcast。广播调用所有提供者,逐个调用,任意一台报错则报错(2.1.0开始支持)。通常用于通知所有提供者更新缓存或日志等本地资源信息。
上面的6种模式都可以应用于生产环境,我们可以根据实际应用场景选择合适的集群容错模式。如果我们觉得Dubbo内置提供的几种集群容错模式都不能满足应用需要,也可以定制实现自己的集群容错模式,因为Dubbo框架给我提供的扩展的接口,只需要实现接口com.alibaba.dubbo.rpc.cluster.Cluster即可,接口定义如下所示:

01 @SPI(FailoverCluster.NAME)
02 public interface Cluster {
03
04     /**
05      * Merge the directory invokers to a virtual invoker.
06      * @param <T>
07      * @param directory
08      * @return cluster invoker
09      * @throws RpcException
10      */
11     @Adaptive
12     <T> Invoker<T> join(Directory<T> directory) throws RpcException;
13
14 }

关于如何实现一个自定义的集群容错模式,可以参考Dubbo源码中内置支持的汲取你容错模式的实现,6种模式对应的实现类如下所示:

1 com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
2 com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
3 com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
4 com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
5 com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
6 com.alibaba.dubbo.rpc.cluster.support.AvailableCluster

可能我们初次接触Dubbo时,不知道如何在实际开发过程中使用Dubbo的集群模式,后面我们会以Failover Cluster模式为例开发我们的分布式应用,再进行详细的介绍。

Dubbo服务负载均衡

Dubbo框架内置提供负载均衡的功能以及扩展接口,我们可以透明地扩展一个服务或服务集群,根据需要非常容易地增加/移除节点,提高服务的可伸缩性。Dubbo框架内置提供了4种负载均衡策略,如下所示:

  • Random LoadBalance:随机策略,配置值为random。可以设置权重,有利于充分利用服务器的资源,高配的可以设置权重大一些,低配的可以稍微小一些
  • RoundRobin LoadBalance:轮询策略,配置值为roundrobin。
  • LeastActive LoadBalance:配置值为leastactive。根据请求调用的次数计数,处理请求更慢的节点会受到更少的请求
  • ConsistentHash LoadBalance:一致性Hash策略,具体配置方法可以参考Dubbo文档。相同调用参数的请求会发送到同一个服务提供方节点上,如果某个节点发生故障无法提供服务,则会基于一致性Hash算法映射到虚拟节点上(其他服务提供方)

在实际使用中,只需要选择合适的负载均衡策略值,配置即可,下面是上述四种负载均衡策略配置的示例:

1 <dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService"version="1.0.0"
2      cluster="failover" retries="2" timeout="100" loadbalance="random"
3      ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
4      <dubbo:method name="queryRoomUserCount" timeout="80" retries="2"loadbalance="leastactive" />
5 </dubbo:service>

上述配置,也体现了Dubbo配置的继承性特点,也就是dubbo:service元素配置了loadbalance=”random”,则该元素的子元素dubbo:method如果没有指定负载均衡策略,则默认为loadbalance=”random”,否则如果dubbo:method指定了loadbalance=”leastactive”,则使用子元素配置的负载均衡策略覆盖了父元素指定的策略(这里调用queryRoomUserCount方法使用leastactive负载均衡策略)。
当然,Dubbo框架也提供了实现自定义负载均衡策略的接口,可以实现com.alibaba.dubbo.rpc.cluster.LoadBalance接口,接口定义如下所示:

01 /**
02 * LoadBalance. (SPI, Singleton, ThreadSafe)
03 *
04 * <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
05 *
06 * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
07 * @author qian.lei
08 * @author william.liangf
09 */
10 @SPI(RandomLoadBalance.NAME)
11 public interface LoadBalance {
12
13      /**
14      * select one invoker in list.
15      * @param invokers invokers.
16      * @param url refer url
17      * @param invocation invocation.
18      * @return selected invoker.
19      */
20     @Adaptive("loadbalance")
21      <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
22
23 }

如何实现一个自定义负载均衡策略,可以参考Dubbo框架内置的实现,如下所示的3个实现类:

1 com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
2 com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
3 com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance

Dubbo服务集群容错实践

手机应用是以聊天室为基础的,我们需要收集用户的操作行为,然后计算聊天室中在线人数,并实时在手机应用端显示人数,整个系统的架构如图所示:

上图中,主要包括了两大主要流程:日志收集并实时处理流程、调用读取实时计算结果流程,我们使用基于Dubbo框架开发的服务来提供实时计算结果读取聊天人数的功能。上图中,实际上业务接口服务器集群也可以基于Dubbo框架构建服务,就看我们想要构建什么样的系统来满足我们的需要。
如果不使用注册中心,服务消费方也能够直接调用服务提供方发布的服务,这样需要服务提供方将服务地址暴露给服务消费方,而且也无法使用监控中心的功能,这种方式成为直连。
如果我们使用注册中心,服务提供方将服务发布到注册中心,而服务消费方可以通过注册中心订阅服务,接收服务提供方服务变更通知,这种方式可以隐藏服务提供方的细节,包括服务器地址等敏感信息,而服务消费方只能通过注册中心来获取到已注册的提供方服务,而不能直接跨过注册中心与服务提供方直接连接。这种方式的好处是还可以使用监控中心服务,能够对服务的调用情况进行监控分析,还能使用Dubbo服务管理中心,方便管理服务,我们在这里使用的是这种方式,也推荐使用这种方式。使用注册中心的Dubbo分布式服务相关组件结构,如下图所示:

下面,开发部署我们的应用,通过如下4个步骤来完成:

  • 服务接口定义

服务接口将服务提供方(Provider)和服务消费方(Consumer)连接起来,服务提供方实现接口中定义的服务,即给出服务的实现,而服务消费方负责调用服务。我们接口中给出了2个方法,一个是实时查询获取当前聊天室内人数,另一个是查询一天中某个/某些聊天室中在线人数峰值,接口定义如下所示:

01 package org.shirdrn.dubbo.api;
02
03 import java.util.List;
04
05 public interface ChatRoomOnlineUserCounterService {
06
07      String queryRoomUserCount(String rooms);
08     
09      List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat);
10 }

接口是服务提供方和服务消费方公共遵守的协议,一般情况下是服务提供方将接口定义好后提供给服务消费方。

  • 服务提供方

服务提供方实现接口中定义的服务,其实现和普通的服务没什么区别,我们的实现类为ChatRoomOnlineUserCounterServiceImpl,代码如下所示:

01 package org.shirdrn.dubbo.provider.service;
02
03 import java.util.List;
04
05 import org.apache.commons.logging.Log;
06 import org.apache.commons.logging.LogFactory;
07 import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService;
08 import org.shirdrn.dubbo.common.utils.DateTimeUtils;
09
10 import redis.clients.jedis.Jedis;
11 import redis.clients.jedis.JedisPool;
12
13 import com.alibaba.dubbo.common.utils.StringUtils;
14 import com.google.common.base.Strings;
15 import com.google.common.collect.Lists;
16
17 public class ChatRoomOnlineUserCounterServiceImpl implementsChatRoomOnlineUserCounterService {
18
19      private static final Log LOG = LogFactory.getLog(ChatRoomOnlineUserCounterServiceImpl.class);
20      private JedisPool jedisPool;
21      private static final String KEY_USER_COUNT = "chat::room::play::user::cnt";
22      private static final String KEY_MAX_USER_COUNT_PREFIX = "chat::room::max::user::cnt::";
23      private static final String DF_YYYYMMDD = "yyyyMMdd";
24
25      public String queryRoomUserCount(String rooms) {
26           LOG.info("Params[Server|Recv|REQ] rooms=" + rooms);
27           StringBuffer builder = new StringBuffer();
28           if(!Strings.isNullOrEmpty(rooms)) {
29                Jedis jedis = null;
30                try {
31                     jedis = jedisPool.getResource();
32                     String[] fields = rooms.split(",");
33                     List<String> results = jedis.hmget(KEY_USER_COUNT, fields);
34                     builder.append(StringUtils.join(results, ","));
35                } catch (Exception e) {
36                     LOG.error("", e);
37                } finally {
38                     if(jedis != null) {
39                          jedis.close();
40                     }
41                }
42           }
43           LOG.info("Result[Server|Recv|RES] " + builder.toString());
44           return builder.toString();
45      }
46     
47      @Override
48      public List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat) {
49           // HGETALL chat::room::max::user::cnt::20150326
50           LOG.info("Params[Server|Recv|REQ] rooms=" + rooms + ",date=" + date + ",dateFormat=" + dateFormat);
51           String whichDate = DateTimeUtils.format(date, dateFormat, DF_YYYYMMDD);
52           String key = KEY_MAX_USER_COUNT_PREFIX + whichDate;
53           StringBuffer builder = new StringBuffer();
54           if(rooms != null && !rooms.isEmpty()) {
55                Jedis jedis = null;
56                try {
57                     jedis = jedisPool.getResource();
58                     return jedis.hmget(key, rooms.toArray(new String[rooms.size()]));
59                } catch (Exception e) {
60                     LOG.error("", e);
61                } finally {
62                     if(jedis != null) {
63                          jedis.close();
64                     }
65                }
66           }
67           LOG.info("Result[Server|Recv|RES] " + builder.toString());
68           return Lists.newArrayList();
69      }
70     
71      public void setJedisPool(JedisPool jedisPool) {
72           this.jedisPool = jedisPool;
73      }
74
75 }

代码中通过读取Redis中数据来完成调用,逻辑比较简单。对应的Maven POM依赖配置,如下所示:

01 <dependencies>
02      <dependency>
03           <groupId>org.shirdrn.dubbo</groupId>
04           <artifactId>dubbo-api</artifactId>
05           <version>0.0.1-SNAPSHOT</version>
06      </dependency>
07      <dependency>
08           <groupId>org.shirdrn.dubbo</groupId>
09           <artifactId>dubbo-commons</artifactId>
10           <version>0.0.1-SNAPSHOT</version>
11      </dependency>
12      <dependency>
13           <groupId>redis.clients</groupId>
14           <artifactId>jedis</artifactId>
15           <version>2.5.2</version>
16      </dependency>
17      <dependency>
18           <groupId>org.apache.commons</groupId>
19           <artifactId>commons-pool2</artifactId>
20           <version>2.2</version>
21      </dependency>
22      <dependency>
23           <groupId>org.jboss.netty</groupId>
24           <artifactId>netty</artifactId>
25           <version>3.2.7.Final</version>
26      </dependency>
27 </dependencies>

有关对Dubbo框架的一些依赖,我们单独放到一个通用的Maven Module中(详见后面“附录:Dubbo使用Maven构建依赖配置”),这里不再多说。服务提供方实现,最关键的就是服务的配置,因为Dubbo基于Spring来管理配置和实例,所以通过配置可以指定服务是否是分布式服务,以及通过配置增加很多其它特性。我们的配置文件为provider-cluster.xml,内容如下所示:

01 <?xml version="1.0" encoding="UTF-8"?>
02
03 <beans xmlns="http://www.springframework.org/schema/beans"
04      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
05      xmlns:p="http://www.springframework.org/schema/p"
06      xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd
07      http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
08
09      <beanclass="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
10           <property name="systemPropertiesModeName"value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
11           <property name="ignoreResourceNotFound" value="true" />
12           <property name="locations">
13                <list>
14                     <value>classpath*:jedis.properties</value>
15                </list>
16           </property>
17      </bean>
18     
19      <dubbo:application name="chatroom-cluster-provider" />
20      <dubbo:registry address="zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" />
21     
22      <dubbo:protocol name="dubbo" port="20880" />
23     
24      <dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService"version="1.0.0"
25           cluster="failover" retries="2" timeout="1000" loadbalance="random"actives="100" executes="200"
26           ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
27           <dubbo:method name="queryRoomUserCount" timeout="500" retries="2"loadbalance="roundrobin" actives="50" />
28      </dubbo:service>
29     
30      <bean id="chatRoomOnlineUserCounterService"class="org.shirdrn.dubbo.provider.service.ChatRoomOnlineUserCounterServiceImpl" >
31           <property name="jedisPool" ref="jedisPool" />
32      </bean>
33     
34      <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy">
35           <constructor-arg index="0">
36                <bean class="org.apache.commons.pool2.impl.GenericObjectPoolConfig">
37                     <property name="maxTotal" value="${redis.pool.maxTotal}" />
38                     <property name="maxIdle" value="${redis.pool.maxIdle}" />
39                     <property name="minIdle" value="${redis.pool.minIdle}" />
40                     <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/>
41                     <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
42                     <property name="testOnReturn" value="${redis.pool.testOnReturn}" />
43                     <property name="testWhileIdle" value="true" />
44                </bean>
45           </constructor-arg>
46           <constructor-arg index="1" value="${redis.host}" />
47           <constructor-arg index="2" value="${redis.port}" />
48           <constructor-arg index="3" value="${redis.timeout}" />
49      </bean>
50     
51 </beans>

上面配置中,使用dubbo协议,集群容错模式为failover,服务级别负载均衡策略为random,方法级别负载均衡策略为roundrobin(它覆盖了服务级别的配置内容),其他一些配置内容可以参考Dubbo文档。我们这里是从Redis读取数据,所以使用了Redis连接池。
启动服务示例代码如下所示:

01 package org.shirdrn.dubbo.provider;
02
03 import org.shirdrn.dubbo.provider.common.DubboServer;
04
05 public class ChatRoomClusterServer {
06
07      public static void main(String[] args) throws Exception {
08           DubboServer.startServer("classpath:provider-cluster.xml");
09      }
10
11 }

上面调用了DubboServer类的静态方法startServer,如下所示:

01 public static void startServer(String config) {
02      ClassPathXmlApplicationContext context = newClassPathXmlApplicationContext(config);
03      try {
04           context.start();
05           System.in.read();
06      } catch (IOException e) {
07           e.printStackTrace();
08      } finally {
09           context.close();
10      }
11 }

方法中主要是初始化Spring IoC容器,全部对象都交由容器来管理。

  • 服务消费方

服务消费方就容易了,只需要知道注册中心地址,并引用服务提供方提供的接口,消费方调用服务实现如下所示:

01 package org.shirdrn.dubbo.consumer;
02
03 import java.util.Arrays;
04 import java.util.List;
05
06 import org.apache.commons.logging.Log;
07 import org.apache.commons.logging.LogFactory;
08 import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService;
09 import org.springframework.context.support.AbstractXmlApplicationContext;
10 import org.springframework.context.support.ClassPathXmlApplicationContext;
11
12 public class ChatRoomDubboConsumer {
13
14      private static final Log LOG = LogFactory.getLog(ChatRoomDubboConsumer.class);
15     
16      public static void main(String[] args) throws Exception {
17           AbstractXmlApplicationContext context = newClassPathXmlApplicationContext("classpath:consumer.xml");
18           try {
19                context.start();
20                ChatRoomOnlineUserCounterService chatRoomOnlineUserCounterService = (ChatRoomOnlineUserCounterService) context.getBean("chatRoomOnlineUserCounterService");        
21                getMaxOnlineUserCount(chatRoomOnlineUserCounterService);             
22                getRealtimeOnlineUserCount(chatRoomOnlineUserCounterService);             
23                System.in.read();
24           } finally {
25                context.close();
26           }
27          
28      }
29
30      private static void getMaxOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService) {
31           List<String> maxUserCounts = liveRoomOnlineUserCountService.getMaxOnlineUserCount(
32                     Arrays.asList(new String[] {"1482178010" , "1408492761", "1430546839", "1412517075", "1435861734"}), "20150327", "yyyyMMdd");
33           LOG.info("After getMaxOnlineUserCount invoked: maxUserCounts= " + maxUserCounts);
34      }
35
36      private static void getRealtimeOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService)
37                throws InterruptedException {
38           String rooms = "1482178010,1408492761,1430546839,1412517075,1435861734";
39           String onlineUserCounts = liveRoomOnlineUserCountService.queryRoomUserCount(rooms);
40           LOG.info("After queryRoomUserCount invoked: onlineUserCounts= " + onlineUserCounts);
41      }
42 }

对应的配置文件为consumer.xml,内容如下所示:

01 <?xml version="1.0" encoding="UTF-8"?>
02
03 <beans xmlns="http://www.springframework.org/schema/beans"
04      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
05      xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd
06      http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
07
08      <dubbo:application name="chatroom-consumer" />
09      <dubbo:registry address="zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" />
10     
11      <dubbo:reference id="chatRoomOnlineUserCounterService"interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version="1.0.0">
12           <dubbo:method name="queryRoomUserCount" retries="2" />
13      </dubbo:reference>
14
15 </beans>

也可以根据需要配置dubbo:reference相关的属性值,也可以配置dubbo:method指定调用的方法的配置信息,详细配置属性可以参考Dubbo官方文档。

  • 部署与验证

开发完成提供方服务后,在本地开发调试的时候可以怎么简单怎么做,如果是要部署到生产环境,则需要打包后进行部署,可以参考下面的Maven POM配置:

01 <build>
02      <plugins>
03           <plugin>
04                <groupId>org.apache.maven.plugins</groupId>
05                <artifactId>maven-shade-plugin</artifactId>
06                <version>1.4</version>
07                <configuration>
08                     <createDependencyReducedPom>true</createDependencyReducedPom>
09                </configuration>
10                <executions>
11                     <execution>
12                          <phase>package</phase>
13                          <goals>
14                               <goal>shade</goal>
15                          </goals>
16                          <configuration>
17                               <transformers>
18                                    <transformerimplementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
19                                    <transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
20                                         <mainClass>org.shirdrn.dubbo.provider.ChatRoomClusterServer</mainClass>
21                                    </transformer>
22                               </transformers>
23                          </configuration>
24                     </execution>
25                </executions>
26           </plugin>
27      </plugins>
28 </build>

这里也给出Maven POM依赖的简单配置:

1 <dependencies>
2      <dependency>
3           <groupId>org.shirdrn.dubbo</groupId>
4           <artifactId>dubbo-api</artifactId>
5           <version>0.0.1-SNAPSHOT</version>
6      </dependency>
7 </dependencies>

我们开发的服务应该是分布式的,首先是通过配置内容来决定,例如设置集群模式、设置负载均衡模式等,然后在部署的时候,可以在多个节点上同一个服务,这样多个服务都会注册到Dubbo注册中心,如果某个节点上的服务不可用了,可以根据我们配置的策略来选择其他节点上的可用服务,后面通过Dubbo服务管理中心和监控中心就能更加清楚明了。

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

struts2 upgrade 2.3 to 2.5 migration 升级向导

Dependencies

Update Struts dependencies to 2.5.

Remove the following plugin dependencies because they were dropped and aren’t supported anymore.

  • Dojo Plugin
  • Codebehind Plugin
  • JSF Plugin
  • Struts1 Plugin

StrutsPrepareAndExecuteFilter

The org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter was moved to org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.

In web.xml replace this:

<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

with that:

<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

There were other package changes, please read Version Notes 2.5 for more details.

DTD

Struts DTD was updated to 2.5 version.

In struts.xml replace 2.3 DTD version:

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

with 2.5:

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"

Tags attributes

The id attribute was replaced with var attribute in the following tags.

  • <s:action>
  • <s:append>
  • <s:bean>
  • <s:date>
  • <s:generator>
  • <s:iterator>
  • <s:merge>
  • <s:number>
  • <s:set>
  • <s:sort>
  • <s:subset>
  • <s:text>
  • <s:url>

If you have something like that in your code:

<s:url id="url" action="login">

change it to:

<s:url var="url" action="login">

The <s:set> tag name attribute is replaced with var attribute.

From:

<s:set id="str1" value="'string1 value'" />
<s:set name="str2" value="'string2 value'" />

to:

<s:set var="str1" value="'string1 value'" />
<s:set var="str2" value="'string2 value'" />

Also escape attribute was renamed to escapeHtml attribute.

From:

<s:property escape="true" var="someProperty"/>

to:

<s:property escapeHtml="true" var="someProperty"/>

Div tag

The <s:div> tag was dropped.

Replace <s:div> with plain HTML <div> tag.

Field names

If you have field names which starts with single lower case letter, for example:

private String sTrng;
public String getSTrng() {...}
public void setSTrng(String str) {...}

change accessors to getsTrng and setsTrng.

Or better yet, change field names to not contain single lower case letter:

private String strng;
public String getStrng() {...}
public void setStrng(String str) {...}

For additional info see WW-3909.

Tiles

Depending on from which version of struts you upgrade and whether you used tiles-plugin or tiles3-plugin you may need to do different steps.

Struts 2.5 just provides a tiles-plugin which uses Tiles3. So support for Tiles2 has been dropped as well as the name tiles3-plugin.

Now the only maven dependency looks like this:

maven dependecy for tiles-plugin
<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-tiles-plugin</artifactId>
    <version>${struts2.version}</version>
</dependency>

You may need to update DTD in your tiles.xml files to Tiles3:

tiles3 dtd
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"

A Listener in web.xml is required. It is not necessary to configure paths to tiles.xml files here as they are picked up automatically.

StrutsTilesListener in web.xml
<listener>
  <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
</listener>

Optionally you may remove TilesDefinitions from XML and annotate actions instead. See Tiles Plugin for more details.

Temp/Work directory of ApplicationServer/ServletContainer

Users reported it was necessary for them to remove temp/work directory of their ApplicationServer/ServletContainer. Likely to force server to recompile JSPs.

来源:https://cwiki.apache.org/confluence/display/WW/Struts%202.3%20to%202.5%20migration

Java的基本数据类型

变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。

内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。

因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。

Java的两大数据类型:

  • 内置数据类型
  • 引用数据类型

内置数据类型

Java语言提供了八种基本类型。六种数字类型(四个整数型(默认是int 型),两个浮点型(默认是double 型)),一种字符类型,还有一种布尔型。

byte:

  • byte数据类型是8位、有符号的,以二进制补码表示的整数;(256个数字),占1字节
  • 最小值是-128(-2^7);
  • 最大值是127(2^7-1);
  • 默认值是0;
  • byte类型用在大型数组中节约空间,主要代替整数,因为byte变量占用的空间只有int类型的四分之一;
  • 例子:byte a = 100,byte b = -50。

short:

  • short数据类型是16位、有符号的以二进制补码表示的整数,占2字节
  • 最小值是-32768(-2^15);
  • 最大值是32767(2^15 – 1);
  • Short数据类型也可以像byte那样节省空间。一个short变量是int型变量所占空间的二分之一;
  • 默认值是0;

int:

  • int数据类型是32位、有符号的以二进制补码表示的整数;占3字节
  • 最小值是-2,147,483,648(-2^31);
  • 最大值是2,147,485,647(2^31 – 1);
  • 一般地整型变量默认为int类型;
  • 默认值是0;
  • 例子:int a = 100000, int b = -200000。

long:

  • long数据类型是64位、有符号的以二进制补码表示的整数;占4字节
  • 最小值是-9,223,372,036,854,775,808(-2^63);
  • 最大值是9,223,372,036,854,775,807(2^63 -1);
  • 这种类型主要使用在需要比较大整数的系统上;
  • 默认值是0L;
  • char数据类型可以储存任何字符;
  • 例子:char letter = ‘A’。

char 类型可以参与整型计算,然后转换成字符型

对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:

public class PrimitiveTypeTest {  
    public static void main(String[] args) {  
        // byte  
        System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);  
        System.out.println("包装类:java.lang.Byte");  
        System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);  
        System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);  
        System.out.println();  
  
        // short  
        System.out.println("基本类型:short 二进制位数:" + Short.SIZE);  
        System.out.println("包装类:java.lang.Short");  
        System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);  
        System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);  
        System.out.println();  
  
        // int  
        System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);  
        System.out.println("包装类:java.lang.Integer");  
        System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);  
        System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);  
        System.out.println();  
  
        // long  
        System.out.println("基本类型:long 二进制位数:" + Long.SIZE);  
        System.out.println("包装类:java.lang.Long");  
        System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);  
        System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);  
        System.out.println();  
  
        // float  
        System.out.println("基本类型:float 二进制位数:" + Float.SIZE);  
        System.out.println("包装类:java.lang.Float");  
        System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);  
        System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);  
        System.out.println();  
  
        // double  
        System.out.println("基本类型:double 二进制位数:" + Double.SIZE);  
        System.out.println("包装类:java.lang.Double");  
        System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);  
        System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);  
        System.out.println();  
  
        // char  
        System.out.println("基本类型:char 二进制位数:" + Character.SIZE);  
        System.out.println("包装类:java.lang.Character");  
        // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台  
        System.out.println("最小值:Character.MIN_VALUE="  
                + (int) Character.MIN_VALUE);  
        // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台  
        System.out.println("最大值:Character.MAX_VALUE="  
                + (int) Character.MAX_VALUE);  
    }  
}  

编译以上代码输出结果如下所示:

基本类型:byte 二进制位数:8
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127

基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767

基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647

基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807

基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38

基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308

基本类型:char 二进制位数:16
包装类:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535

Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的”E+数字”表示E之前的数字要乘以10的多少倍。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。

实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。


引用类型

  • 引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型,比如Employee、Pubby等。变量一旦声明后,类型就不能被改变了。
  • 对象、数组都是引用数据类型。
  • 所有引用类型的默认值都是null。
  • 一个引用变量可以用来引用与任何与之兼容的类型。
  • 例子:Animal animal = new Animal(“giraffe”)。

Java常量

常量就是一个固定值。它们不需要计算,直接代表相应的值。

常量指不能改变的量。 在Java中用final标志,声明方式和变量类似:

final double PI = 3.1415927;

虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。

字面量可以赋给任何内置类型的变量。例如:

byte a = 68;
char a = 'A'

byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。

当使用常量的时候,前缀0表示8进制,而前缀0x代表16进制。例如:

int decimal = 100;
int octal = 0144;
int hexa =  0x64;

和其他语言一样,Java的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:

"Hello World"
"two\nlines"
"\"This is in quotes\""

字符串常量和字符常量都可以包含任何Unicode字符。例如:

char a = '\u0001';
String a = "\u0001";

Java语言支持一些特殊的转义字符序列。

符号 字符含义
\n 换行 (0x0a)
\r 回车 (0x0d)
\f 换页符(0x0c)
\b 退格 (0x08)
\s 空格 (0x20)
\t 制表符
\” 双引号
\’ 单引号
\\ 反斜杠
\ddd 八进制字符 (ddd)
\uxxxx 16进制Unicode字符 (xxxx)

Log4j2.xml Eclipse 添加自动提示

参考:https://issues.apache.org/jira/browse/LOG4J2-411

Support of XSD/DTD linked to the configuration file

It would be very nice, if the the XML configuration uses an dedicated namespace, e.g. http://logging.apache.org/log4j/2.0/config.
This feature allows using XML catalogs to locate the schema locally, e.g. with Eclipse IDE.
The Log4j-events.xsd contains already such a declaration:

targetNamespace="http://logging.apache.org/log4j/2.0/events"

Then the configuration XML file needs only a small extension:

log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="WARN" xmlns="http://logging.apache.org/log4j/2.0/config">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
    </Console>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="Console" />
    </Root>
  </Loggers>
</Configuration>

JAVA BIO与NIO、AIO的区别

IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。

一、BIO

在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端会线程会等待请求结束后才继续执行。

二、NIO (New IO)

NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题: 在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。

NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。  也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

BIO与NIO一个比较重要的不同,是我们使用BIO的时候往往会引入多线程,每个连接一个单独的线程;而NIO则是使用单线程或者只使用少量的多线程,每个连接共用一个线程。

NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。

在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题。

HTTP/1.1出现后,有了Http长连接,这样除了超时和指明特定关闭的http header外,这个链接是一直打开的状态的,这样在NIO处理中可以进一步的进化,在后端资源中可以实现资源池或者队列,当请求来的话,开启的线程把请求和请求数据传送给后端资源池或者队列里面就返回,并且在全局的地方保持住这个现场(哪个连接的哪个请求等),这样前面的线程还是可以去接受其他的请求,而后端的应用的处理只需要执行队列里面的就可以了,这样请求处理和后端应用是异步的.当后端处理完,到全局地方得到现场,产生响应,这个就实现了异步处理。

三、AIO

与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。  即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。  在JDK1.7中,这部分内容被称作NIO.2,主要在Java.nio.channels包下增加了下面四个异步通道:

  • AsynchronousSocketChannel
  • AsynchronousServerSocketChannel
  • AsynchronousFileChannel
  • AsynchronousDatagramChannel

其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。

BIO是一个连接一个线程。

NIO是一个请求一个线程。

AIO是一个有效请求一个线程。

先来个例子理解一下概念,以银行取款为例:

  • 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写);
  • 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API);
  • 阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回);
  • 非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)

Java对BIO、NIO、AIO的支持:

  • Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
  • Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
  • Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,

BIO、NIO、AIO适用场景分析:

  • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
  • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
  • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

另外,I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。

在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作。

在比较这两个模式之前,我们首先的搞明白几个概念,什么是阻塞和非阻塞,什么是同步和异步,同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。

一般来说I/O模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO

同步阻塞IO:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!

同步非阻塞IO:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。

异步阻塞IO:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!

异步非阻塞IO:在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种IO模型。

Java9新功能之HTTP2和REPL

本文转自:云栖社区云

【摘要】对Java 9的炒作将不再局限于模块化(modularity),Java 9正在搜罗大量额外的功能模块,这些功能模块正作为Java增强提案(JEP)提交,并在OpenJDK (Java SE的参考实现项目)中实现。 在这篇文章中,我们将重点关注一些或将在Java 9整个生命周期中,对开发者的工作生…

对Java 9的炒作将不再局限于模块化(modularity),Java 9正在搜罗大量额外的功能模块,这些功能模块正作为Java增强提案(JEP)提交,并在OpenJDK (Java SE的参考实现项目)中实现。

在这篇文章中,我们将重点关注一些或将在Java 9整个生命周期中,对开发者的工作生活影响最大的JEP,包括新的HTTP/2支持和JShell REPL(读取-求值-打印-循环),后者带来了基于shell的交互式Java开发环境和探索性开发API。

HTTP/2

HTTP/2标准是HTTP协议的最新版本。当前版本HTTP/1.1始于1999年,存在着非常严重的问题,包括:

对头阻塞

在HTTP/1.1中,响应接收的顺序和请求发送的顺序相同。这意味着,例如,当查看一个包含许多小图像的大HTML页面时,图像资源将不得不在HTML页面资源之后排队,在浏览器完全加载完HTML页面之前,图像资源无法被发送。这就是“对头阻塞”,会导致许多潜在的页面渲染问题。

在HTTP/2中,响应数据可以按块(chunk)传输,甚至可以交叉传输,因此真正实现了请求和响应的多路复用。

一个站点的连接数限制

在HTTP/1.1标准中有这样的描述:“一个单用户的客户端不能与任何服务器保持2个以上的连接”。这个限制和对头阻塞问题一起,严重限制了页面的性能。

HTTP/2打破这种限制并认为连接是持久的,只有当用户跳转后或者发生技术性故障事件时,连接才会关闭。对多路复用的使用将有助于降低页面性能瓶颈。

HTTP控制头的开销

当前的HTTP版本使用简单的、基于文本的HTTP头信息来控制通信。这样做的优点是非常简单且易于理解,调试也很简单,只需通过连接指定端口并输入一些文本。然而,使用基于文本的协议会让小的响应包不成比例地膨胀。此外,大量的HTTP响应几乎没有或者根本没有有效负载(比如,HEAD请求只是要确定资源是否发生变化)。为实际上只包含最后修改时间的响应,使用完全基于文本的头信息(大约有700个字节,在HTTP1.1中,它们不能被压缩,尽管很容易做到)是当前HTTP标准中,不可思议的浪费。

另一个思路是对HTTP头信息使用二进制编码。这种方式能够极大地提高较小请求的速度且占用的网络带宽非常小。这正是HTTP/2已经选择的方法,虽然以协议精神制定标准应该选择基于文本的协议,但是二进制的效率有令人信服的理由,让我们这样做。

HTTP/2带来的期望

HTTP/2标准是由IETF HTTP工作组创建的,该组织由来自Mozilla、Google、 Microsoft、Apple,以及其他公司的代表和工程师组成,由来自CDN领军公司Akamai的高级工程师Mark Nottingham任主 席。因此,HTTP/2是一个为优化大型、高流量的网站而生的版本,它在实现简单、易于调试的基础上,确保了性能和网络带宽消耗。

该组织主 席总结了一些HTTP/2的关键属性:

相同的HTTP API成本更低的请求网络和服务器端友好缓存推送思维革命更多加密方式

带给Java的意义

自从1.0版本开始,Java就支持HTTP,但是多数代码出自完全不同的时代。例如,Java对HTTP的支持是围绕相对协议无关的框架(URL类)设计的,因此在网站成为主导地位的90年代,这种实现显得很不清晰。

Java对HTTP的支持是基于当时最好的设计思想,但是时过境迁,最重要的是Java对HTTP原始的支持出来时,HTTPS还没有出现。因此,Java的API将HTTPS作为一种移花接木,导致了不能简化的复杂性。

在现代社会,HTTPS开始变得无所不在,让HTTP日渐成为落后的技术。甚至,美国政府现在都通过了完全迁到HTTPS-only的计划。

JDK内核对HTTP的支持已经无法跟上现实网络的发展步伐。实际上,甚至JDK8也只不过是交付了一个支持HTTP/1.0的客户端,然而,大多数的开发者早已转而使用第三方客户端库了,比如Apache的HttpComponents。

所有这一切意味着,对HTTP/2的支持将是Java未来十年的核心功能。这也让我们重新审视我们的固有思维,重新写一套API并提供重新来过的机会。HTTP/2将是未来数年内,每位开发者主要面对的API。

新的API不再坚持协议中立性,使开发者可以完全抛弃过去的使用方式。这套API只关注HTTP协议,但是要进一步理解的是HTTP/2并没有从根本上改变原有的语义。因此,这套API是HTTP协议独立的,同时提供了对新协议中帧和连接处理的支持。

在新的API中,一个简单的HTTP请求,可以这样创建和处理:

HttpResponse response = HttpRequest .create(new URI(“http://www.infoq.com”)) .body(noBody()) .GET().send(); int responseCode = response.responseCode(); String responseBody = response.body(asString()); System.out.println(responseBody);

这种符合流畅风格/建造者模式(fluent/builder)的API,与现存的遗留系统相比,对开发者来说,更具现代感和舒适感。

虽然当前的代码库只支持HTTP/1.1,但是已经包含了新的API。这使得在对HTTP/2支持完成对过程中,开发者可以实验性地使用和验证新的API。

相关代码已经进入OpenJDK沙箱仓库中,并很快登陆JDK 9的主干。到那个时候,新的API将开始自动构建到Oracle的二进制beta版本中。现在,对HTTP/2的支持已经可用,并将在未来数月内最终完成。

在此期间,你可以使用Mercurial迁出源代码,并根据AdoptOpenJDK构建指导编译你迁出地代码,这样你就可以实验性地使用新的API了。

第一批完成的功能之一是当前版本力不能及的异步API。这个功能让长期运行的请求,可以通过sendAsync()方法,切换到VM管理的后台线程中:

HttpRequest req = HttpRequest .create(new URI(“http://www.infoq.com”)) .body(noBody()) .GET(); CompletableFuture<HttpResponse> aResp = req.sendAsync(); Thread.sleep(10); if (!aResp.isDone()) { aResp.cancel(true); System.out.println(“Failed to reply quickly…”); return; } HttpResponse response = aResp.get();

相比HTTP/1.1的实现,新的API带给开发者最多的是方便性,因为HTTP/1.1没有提供对已经发送到服务器端的请求的取消机制,而HTTP/2可以让客户端向已经被服务器端处理的请求,发送取消命令。

JShell

很多语言都为探索性开发提供了交互式环境。在某些情况下(特别是Clojure和其他Lisp方言),交互式环境占据了开发者的大部分编码时间,甚至是全部。其他语言,比如Scala或者JRuby也广泛使用REPL。

当然,此前Java曾经推出过Beanshell脚本语言,但是它没有实现完全标准化,而且近年来,该项目已经处于不活跃状态。在Java 8(以及jjs REPL)中引入的Nashorn Java实现打开了更广泛地考虑REPL并将交互式开发成为可能的大门。

一项努力将现代REPL引入Java 9的工作,以JEP 222作为开始,收录在OpenJDK的Kulla项目中。Kulla这个名字来自古巴比伦神话,是建造之神。该项目的主旨是提供最近距离的“完整Java”体验。该项目没有引入新的非Java语义,并禁用了Java语言中对交互式开发没有用处的语义(比如上层的访问控制修改或同步的语义)。

与所有REPL一样,JShell提供了命令行,而不是类似IDE的体验。语句和表达式能够在执行状态上下文中,被立即求值,而不是非得打包到类中。方法也是自由浮动的,而不必属于某个特定的类。相反,JShell使用代码片断“snippets”来提供上层执行环境。

与HTTP/2 API相似,JShell已经在独立的项目开发,以免在快速发展的时期影响主干构建的稳定性。JShell预计在2015年8月期间合并到主干。

现在,开发者可以参考AdoptOpenJDK说明指导,从头构建Kulla(源代码可以从Mercurial地址获得)。

对于一些上手实验,最简单的可能是使用一个独立的试验jar。这些jar包是社区专为不想从头构建的开发者构建好的。

这些试验jar包可以从AdoptOpenJDK CloudBees的CI构建实例中获得。

要使用它们,你需要安装Java 9 beta版(或者OpenJDK 9的构建版本)。然后下载jar文件,重命名为kulla.jar,然后在命令行输入如下:

$ java -jar kulla.jar | Welcome to JShell — Version 0.610 | Type /help for help ->

这是REPL的标准界面,和往常一样,命令是从单个字符开始并最终发出的。

JShell有一个相当完整(但仍在发展)的帮助语法,可以通过如下命令轻松获得:

-> /help Type a Java language expression, statement, or declaration. Or type one of the following commands: /l or /list [all] — list the source you have typed /seteditor <executable> — set the external editor command to use /e or /edit <name or id> — edit a source entry referenced by name or id /d or /drop <name or id> — delete a source entry referenced by name or id /s or /save [all|history] <file> — save the source you have typed /o or /open <file> — open a file as source input /v or /vars — list the declared variables and their values /m or /methods — list the declared methods and their signatures /c or /classes — list the declared classes /x or /exit — exit the REPL /r or /reset — reset everything in the REPL /f or /feedback <level> — feedback information: off, concise, normal, verbose, default, or ? /p or /prompt — toggle display of a prompt /cp or /classpath <path> — add a path to the classpath /h or /history — history of what you have typed /setstart <file> — read file and set as the new start-up definitions /savestart <file> — save the default start-up definitions to the file /? or /help — this help message /! — re-run last snippet /<n> — re-run n-th snippet /-<n> — re-run n-th previous snippet Supported shortcuts include: — show possible completions for the current text Shift- — for current method or constructor invocation, show a synopsis of the method/constructor

JShell支持TAB键自动补全, 因此我们可以很容易找到println()或者其他我们想使用的方法:

-> System.out.print print( printf( println(

传统的表达式求值也很容易,但是相比其他动态类型语言,Java的静态类型特征会更严格一点。JShell会自动创建临时变量来保存表达式的值,并确保它们保持在上下文域内供以后使用:

-> 3 * (4 + 5) | Expression value is: 27 | assigned to temporary variable $1 of type int -> System.out.println($1); 27

我们还可以使用/list命令,查看到目前为止输入的所有源代码:

-> /list 9 : 3 * (4 + 5) 10 : System.out.println($1);

使用/vars命令显示所有的变量(包括显式定义的和临时的),以及他们当前持有的值:

-> String s = “Dydh da” | Added variable s of type String with initial value “Dydh da” -> /vars | int $1 = 27 | String s = “Dydh da”

除了支持简单的代码行,REPL还允许非常简单地创建类和其它用户定义的类型。例如,可以用如下短短一行来创建类(请注意,开始和结束括号是必需的):

-> class Pet {} | Added class Pet -> class Cat extends Pet {} | Added class Cat

JShell代码非常简洁、自由浮动的性质意味着我们可以非常简单地使用REPL来演示Java语言的功能。例如,让我们来看看著名的类型问题,即Java数组的协变问题:

-> Pet[] pets = new Pet[1] | Added variable pets of type Pet[] with initial value [LPet;@2be94b0f -> Cat[] cats = new Cat[1] | Added variable cats of type Cat[] with initial value [LCat;@3ac42916 -> pets = cats | Variable pets has been assigned the value [LCat;@3ac42916 -> pets[0] = new Pet() | java.lang.ArrayStoreException thrown: REPL.$REPL13$Pet | at (#20:1)

这样的功能使JShell成为一种伟大的教学或研究工具,而且最接近Scala REPL的体验。使用/classpath切换,可以加载额外的jar包,从而可以在REPL直接使用互动式探索性API。

参与

主要的IDE已开始提供支持JDK 9早期版本的构建——包括Netbeans和Eclipse Mars。[urlhttps://www.jetbrains.com/idea/download/?spm=5176.blog26632.yqblogcon1.14.tXFWPP=””]IntelliJ 14.1[/url]据称支持JDK9,但目前还不清楚对新的模块化JDK扩展的支持力度。

到目前为止,这些IDE还不支持HTTP/2和JShell,因为这些功能还没有登陆OpenJDK的主干,但是开发者应该很期望它们能够早日出现在标准的JDK beta版本中,并且有IDE插件可以紧随其后。这些API仍在开发中,项目的领导者正在积极寻求最终用户的使用和参与。

The JDK 9 Outreach programme is also underway to encourage developers to test their code and applications on JDK 9 before it arrives. HTTP/2 & JShell aren’t the only new features being worked on – other new JDK 9 functionality under development as JEPs includes

JDK 9的宣传计划也正在鼓励开发者测试他们的代码并在JDK 9上运行应用程序。正在开发的新功能不止包括HTTP/2和JShell—— 其他作为JEP,JDK 9正在开发的新功能还包括:

102 Process API的更新(Process API Updates)165 编译器控制(Compiler Control)227 Unicode 7.0245 验证虚拟机代码行标记参数(Validate JVM Command-Line Flag Arguments)248: G1作为默认的垃圾回收器(Make G1 the Default Garbage Collector)TLS的一系列更新(TLS Updates) (JEP 219, 244, 249)

目前正在审议(以及考虑应该放在哪个Java版本)的所有JEP的完整列表可以在这里找到。

Spring Data JPA 查询方法支持的关键字

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = 1?
Between findByStartDateBetween … where x.startDate between 1? and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> age) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

通过解析方法名创建查询

通过前面的例子,读者基本上对解析方法名创建查询的方式有了一个大致的了解,这也是 Spring Data JPA 吸引开发者的一个很重要的因素。该功能其实并非 Spring Data JPA 首创,而是源自一个开源的 JPA 框架 Hades,该框架的作者 Oliver Gierke 本身又是 Spring Data JPA 项目的 Leader,所以把 Hades 的优势引入到 Spring Data JPA 也就是顺理成章的了。

框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。

在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByUserAddressZip ()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,详细规则如下(此处假设该方法针对的域对象为 AccountInfo 类型):

  • 先判断 userAddressZip (根据 POJO 规范,首字母变为小写,下同)是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
  • 从右往左截取第一个大写字母开头的字符串(此处为 Zip),然后检查剩下的字符串是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为 AccountInfo 的一个属性;
  • 接着处理剩下部分( AddressZip ),先判断 user 所对应的类型是否有 addressZip 属性,如果有,则表示该方法最终是根据 “AccountInfo.user.addressZip” 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 “AccountInfo.user.address.zip” 的值进行查询。

可能会存在一种特殊情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上 “_” 以显式表达意图,比如 “findByUser_AddressZip()” 或者 “findByUserAddress_Zip()”。

在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:

  • And — 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);
  • Or — 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);
  • Between — 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);
  • LessThan — 等价于 SQL 中的 “<“,比如 findBySalaryLessThan(int max);
  • GreaterThan — 等价于 SQL 中的”>”,比如 findBySalaryGreaterThan(int min);
  • IsNull — 等价于 SQL 中的 “is null”,比如 findByUsernameIsNull();
  • IsNotNull — 等价于 SQL 中的 “is not null”,比如 findByUsernameIsNotNull();
  • NotNull — 与 IsNotNull 等价;
  • Like — 等价于 SQL 中的 “like”,比如 findByUsernameLike(String user);
  • NotLike — 等价于 SQL 中的 “not like”,比如 findByUsernameNotLike(String user);
  • OrderBy — 等价于 SQL 中的 “order by”,比如 findByUsernameOrderBySalaryAsc(String user);
  • Not — 等价于 SQL 中的 “! =”,比如 findByUsernameNot(String user);
  • In — 等价于 SQL 中的 “in”,比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
  • NotIn — 等价于 SQL 中的 “not in”,比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;

Java 9进入第一轮问题修复阶段

来源:infoq.com  作者:Abraham Marín Pérez ,译者 尚剑

Java 9功能特性正式完成,这意味着第一个问题修复阶段已经开始。HTTP/2客户端没有在截止日期前完成,现已降级为孵化器功能。由于现在的目标是在7月准备好可发布的Java 9,所以目前不太可能添加任何新的JEP。

InfoQ此前的报道中提到,第一轮问题修复阶段,或者说“启动Rampdown”阶段的目的是解决P1至P3级别的问题。其中,根据Java平台的首席架构师Mark Reinhold提出的流程,问题修复应该优先考虑Java 9中的新问题,而不是影响Java 9但已经存在于Java 8或更早版本Java中的问题,之所以这么做,可能是因为相对于新的问题,公众更容易忍受已经存在的问题。Reinhold提供的缺陷列表显式地过滤掉了只与文档、演示和测试相关的缺陷,这似乎表明了他们对用户体验的关注。在撰写本文时,该列表中有194个缺陷。

这一阶段还包括一个规定,如果有正当理由,可以留下一些未解决的P1至P2级别的问题。希望推迟其解决方案的问题所有者必须在错误报告中指出其请求的原因(复杂性、风险、时间不足等),然后相关区域负责人、小组负责人和JDK 9 Project 负责人将分析这些数据并同意或拒绝延期。在写这篇文章的时候,这个列表中暂时还没有推迟请求,但以后可能会出现。

这个Rampdown阶段在特定的扩展功能完成阶段之后进行,以给予一些JEP完成的时间。HTTP/2客户端以及增强弃用、jlink、和新的HotSpot编译系统都是在2016年7月出现风险的功能。其中,HTTP/2 Client是唯一一个没有最终做出来的功能,转而成为孵化器功能。这意味着,尽管HTTP/2 Client将包含在Java 9中,但默认情况下不可访问:该功能将被打包在前缀为jdk.incubator.的模块下,开发人员必须显式地使用–add-mod标记才能访问该功能。然而,如果开发人员选择这样做,他们将需要考虑到孵化器功能不是标准API的一部分,因此该功能可能随时被修改。

阅读英文原文Java 9 Enters First Bug Fixing Round

jstat命令使用

jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:

jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]

jstat -class

Loaded:加载class的数量
Bytes:所占用空间大小
Unloaded:未加载数量
Bytes:未加载占用空间
Time:时间

编译统计

jstat -compiler

Compiled:编译数量。
Failed:失败数量
Invalid:不可用数量
Time:时间
FailedType:失败类型
FailedMethod:失败的方法

垃圾回收统计
jstat -gc

S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

堆内存统计
jstat -gccapacity

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数

新生代垃圾回收统计
jstat -gcnew

S0C:第一个幸存区大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间

新生代内存统计
jstat -gcnewcapacity

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数

老年代垃圾回收统计
jstat -gcold

MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

老年代内存统计
jstat -gcoldcapacity

OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:老年代大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
元数据空间统计

jstat -gcmetacapacity

MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

总结垃圾回收统计
jstat -gcutil

S0:幸存1区当前使用比例
S1:幸存2区当前使用比例
E:伊甸园区使用比例
O:老年代使用比例
M:元数据区使用比例
CCS:压缩使用比例
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

JVM编译方法统计
jstat -printcompilation

Compiled:最近编译方法的数量
Size:最近编译方法的字节码数量
Type:最近编译方法的编译类型。
Method:方法名标识。

Java进程线程 CPU 占用高负载高问题排查

java -jar 运行应用,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高。

问题分析:
1,程序属于CPU密集型,和开发沟通过,排除此类情况。
2,程序代码有问题,出现死循环,或是死锁, 可能性极大。

过程:

1.代码是不能定位,从日志上也无法分析得出。
2.top,发现PID,83021 的Java进程占用CPU高达900%,出现故障。
3.找到该进程后,如何定位具体线程或代码呢,首先显示线程列表,并按照CPU占用高的线程排序:ps -mp 83021 -o THREAD,tid,time | sort -rn | head -n 10

USER     %CPU PRI SCNT WCHAN  USER SYSTEM    TID     TIME
ubox     88.9  19    – futex_    –      –  83032 08:21:49
ubox     88.9  19    – –         –      –  83031 08:21:49
ubox     88.9  19    – –         –      –  83030 08:21:49
ubox     88.9  19    – –         –      –  83028 08:21:49
ubox     88.9  19    – –         –      –  83027 08:21:49
ubox     88.9  19    – –         –      –  83025 08:21:49
ubox     88.9  19    – –         –      –  83024 08:21:49
ubox     88.9  19    – –         –      –  83023 08:21:49
ubox      712   –    – –         –      –      – 2-18:57:53

找到了耗时最高的线程83032,占用CPU时间超过8小时了!
4.将需要的线程ID转换为16进制格式:

printf “%x\n” 83032
14458

5.最后打印线程的堆栈信息:jstack 83021 | grep 14458 -A 5

6.将输出的信息给开发部进行确认,这样就能找出有问题的代码。
通过最近几天的监控,CPU已经安静下来了。

php-fpm添加service服务

Nginx通过FastCGI运行PHP比Apache包含PHP环境有明显的优势,最近有消息称,PHP5.4将很有可能把PHP-FPM补丁包含在内核里,nginx服务器平台上运行PHP将更加轻松,下面我们就来看一篇php-fpm平滑启动并配置服务例子。

我的PHP是源码安装的。php-fpm在PHP 5.3.2以后的版本不支持以前的php-fpm (start|restart|stop|reload) ,那么如果将php-fpm配置成服务,并添加平滑启动/重启。

配置php-fpm.conf(vi  php-7.1.0/etc/php-fpm.conf),将pid(;pid = run/php-fpm.pid)前的;去掉。

因为编译安装php的,所以会在php目录生成很多二进制文件,找到init.d.php-fpm,拷贝到init.d下。

cp  php-7.1.0/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm

设置权限,并添加服务

chmod +x /etc/init.d/php-fpm
chkconfig –add php-fpm

以后可以使用如下命令管理php-fpm了

service php-fpm status

service php-fpm start
service php-fpm restart
service php-fpm reload

service php-fpm stop

Spring cron 表达式

Cron表达式是一个字符串,字符串以5或6个空格隔开,分开工6或7个域,每一个域代表一个含义,Cron有如下两种语法
格式:
Seconds Minutes Hours DayofMonth Month DayofWeek Year 或
Seconds Minutes Hours DayofMonth Month DayofWeek
每一个域可出现的字符如下:
代码
Seconds:可出现,-  *  / 四个字符,有效范围为0-59的整数
Minutes:可出现,-  *  / 四个字符,有效范围为0-59的整数
Hours:可出现,-  *  / 四个字符,有效范围为0-23的整数
DayofMonth:可出现,-  *  / ? L W C八个字符,有效范围为0-31的整数
Month:可出现,-  *  / 四个字符,有效范围为1-12的整数或JAN-DEc
DayofWeek:可出现,-  *  / ? L C #四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推
Year:可出现,-  *  / 四个字符,有效范围为1970-2099年
每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:
代码
(1)*:表示匹配该域的任意值,假如在Minutes域使用*,即表示每分钟都会触发事件。(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13  13 15 20 * ?,其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。(3)-:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次

(4)/:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.

(5),:表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

(6)L:表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

(7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份

(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。

(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。
举几个例子:

代码
0 0  2  1 *  ? *  表示在每月的1日的凌晨2点调度任务
0 15 10 ? *  MON-FRI 表示周一到周五每天上午10:15执行作业
0 15 10 ? 6L 2002-2006 表示200-2006年的每个月的最后一个星期五上午10:15执行作业
91linux
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。
按顺序依次为
秒(0~59)
分钟(0~59)
小时(0~23)
天(月)(0~31,但是你需要考虑你月的天数)
月(0~11)
天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
7.年份(1970-2099)
其中每个元素可以是一个值(如6),一个连续区间(9-12),一个间隔时间(8-18/4)(/表示每隔4小时),一个列表(1,3,5),通配符。由于”月份中的日期”和”星期中的日期”这两个元素互斥的,必须要对其中一个设置?.
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时
0 0 12 ? * WED 表示每个星期三中午12点
“0 0 12 * * ?” 每天中午12点触发
“0 15 10 ? * *” 每天上午10:15触发
“0 15 10 * * ?” 每天上午10:15触发
“0 15 10 * * ? *” 每天上午10:15触发
“0 15 10 * * ? 2005” 2005年的每天上午10:15触发
“0 * 14 * * ?” 在每天下午2点到下午2:59期间的每1分钟触发
“0 0/5 14 * * ?” 在每天下午2点到下午2:55期间的每5分钟触发
“0 0/5 14,18 * * ?” 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
“0 0-5 14 * * ?” 在每天下午2点到下午2:05期间的每1分钟触发
“0 10,44 14 ? 3 WED” 每年三月的星期三的下午2:10和2:44触发
“0 15 10 ? * MON-FRI” 周一至周五的上午10:15触发
“0 15 10 15 * ?” 每月15日上午10:15触发
“0 15 10 L * ?” 每月最后一日的上午10:15触发
“0 15 10 ? * 6L” 每月的最后一个星期五上午10:15触发
“0 15 10 ? * 6L 2002-2005” 2002年至2005年的每月的最后一个星期五上午10:15触发
“0 15 10 ? * 6#3” 每月的第三个星期五上午10:15触发
有些子表达式能包含一些范围或列表
例如:子表达式(天(星期))可以为 “MON-FRI”,”MON,WED,FRI”,”MON-WED,SAT”
“*”字符代表所有可能的值
因此,”*”在子表达式(月)里表示每个月的含义,”*”在子表达式(天(星期))表示星期的每一天

“/”字符用来指定数值的增量
例如:在子表达式(分钟)里的”0/15″表示从第0分钟开始,每15分钟
在子表达式(分钟)里的”3/20″表示从第3分钟开始,每20分钟(它和”3,23,43″)的含义一样
“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值
当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为”?”

“L” 字符仅被用于天(月)和天(星期)两个子表达式,它是单词”last”的缩写
但是它在两个子表达式里的含义是不同的。
在天(月)子表达式中,”L”表示一个月的最后一天
在天(星期)自表达式中,”L”表示一个星期的最后一天,也就是SAT
如果在”L”前有具体的内容,它就具有其他的含义了
例如:”6L”表示这个月的倒数第6天,”FRIL”表示这个月的最一个星期五
注意:在使用”L”参数时,不要指定列表或范围,因为这会导致问题

字段   允许值   允许的特殊字符
秒    0-59    , – * /
分    0-59    , – * /
小时    0-23    , – * /
日期    1-31    , – * ? / L W C
月份    1-12 或者 JAN-DEC    , – * /
星期    1-7 或者 SUN-SAT    , – * ? / L C #
年(可选)    留空, 1970-2099    , – * /
 注意:日和星期是任先其一
  ?:代表可有可无
  *:代表每一年
  秒  分  时  日  月    星期几  年
  0  0  0  10  12    ?    2009      //代表:2009年12月10日0点0分0秒执行(星期几:’?’代表忽略)
  0  0  0  10  12    ?    *        //代表:每年12月10日0点0分0秒执行
  0  0  0  10  *    ?            //代表:每月10日0点0分0秒执行
  0  0  1  1  *    ?            //代表:每月1号1点0分0秒执行
  0  0  1  1  3,6,9    ?            //代表:3月 6月 9月,1号1点0分0秒执行
  0  0  1  1  2-5    ?

Stateless Authentication implementation using JWT, Nginx+Lua and Memcached

If you already have an idea on stateless authentication and JWT then proceed with this implementation blog otherwise just go through the previous blog Stateless Authentication to get an idea.

As i mentioned in my previous blog JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.

Client can access the the resources from different applications. So to validate the token at applications, we require the secret or a public/private key.

Problems of validating the token in every application

  1. We have to maintain the secret key in all the applications and have to write or inject the token validation logic in every application. The validation logic may include more than token validation like fingerprint mismatch, session idle time out and many more based on the requirement.
  2. If the applications are developed in different languages then we have to implement the token validation logic based on application technology stack and maintenance is very difficult.

Solution

Instead of maintaining the validation logic in every application, we can write our validation logic at one common place so that every request can make use of that logic irrespective of application (Note: Here Applications could be developed in any language). I have chosen reverse proxy server (Nginx) to maintain the validation logic with the help of Lua.

Advantages

  1. We don’t need to maintain the secret or private/public key in every application. Just maintain at authentication server side to generate a token and at proxy server (Nginx) to validate the token.
  2. Maintenance of the validation logic easy.

Before jumping in to the flow and implementation let’s see why we have chosen this technology stack.

Why JWT ? 

To achieve the stateless authentication we have chosen JWT (JSON Web Token). We can easily, securely transmitting information between parties as a JSON object. If we want to put some sensitive information in JWT token, we can encrypt the JWT payload itself using the JSON Web Encryption (JWE) specification.

Why Nginx + Lua ?

Nginx+Lua is a self-contained web server embedding the scripting language Lua. Powerful applications can be written directly inside Nginx without using cgi, fastcgi, or uwsgi. By adding a little Lua code to an existing Nginx configuration file, it is easy to add small features.

One of the core benefits of Nginx+Lua is that it is fully asynchronous. Nginx+Lua inherits the same event loop model that has made Nginx a popular choice of webserver. “Asynchronous” simply means that Nginx can interrupt your code when it is waiting on a blocking operation, such as an outgoing connection or reading a file, and run the code of another incoming HTTP Request.

Why Memcached ?

To keep the application more secured, along with the token validation we are doing the fingerprint check and handling idle time out as well. Means, if the user is idle for some time and not doing any action then user has to be logged out from the application. To do the fingerprint check and idle time out check, some information needs to be shared across the applications. To share the information across the applications we have chosen Memcached (Distributed Cache).

Note: If you don’t want to do fingerprint mismatch check and idle time out check, then you can simply ignore the Memcached component from the flow.

Flow

 

Untitled presentation (2)

 

Step 1

Client try to access the resource from the application with out JWT token or invalid token. As shown in the flow, request goes to the proxy server (Nginx).

Step 2

Nginx looks for the auth header (X-AUTH-TOKEN) and validates the token with the help of Lua.

 

Step 3

As token is not present or invalid, nginx sends below response to the client.

 

Step 4

Now user has to login in to the system, So client will load the login page.

Step 5

Client will send a request to the authenticate server to authenticate the user. Along with username and password client sends the fingerprint also. Here we are considering fingerprint to make sure that all the requests are initiating from the same device where user logged in to the system.

Sample authenticate request body

 

Step 6

Authenticate server validates the credentials and create a JWT token with TokenId (random generated UUID) as a claim and this tokenId is useful to uniquely identify the user. And set the JWT token in response header (X-AUTH-TOKEN).

Create JWT Token

Add this dependency to your pom.xml to work on JWT

While creating the token you can set any number of claims.

CustomClaim.java

Generated JWT token looks like below

And the JWT token payload looks like below. You can put what ever data you want like roles & permissions associated to him and so on…

 

Step 7

Put TokenId as a key and user meta information like fingerprint, last access time etc… as a value in memcached which is useful to verify the fingerprint and session idle time out at nginx side using Lua.

Sample Memcached content

 

Put Content in Memcached

Add this dependency to your pom.xml to work on Memcached

 

 

Step 8

Send back response to the client from authentication server with response header X-AUTH-TOKEN

 

Step 9

Fetch the token from response header and store it in local storage at client side. So that we can send this token in request header from next request onwards.

Step 10

Now client access the resource from application with valid JWT token. As shown in the flow request goes to the proxy server (Nginx). With every request client will send a fingerprint in some header and consider header name as “FINGER-PRINT”.

Step 11

Nginx validates the token. As token is valid, extract the TokenId from the JWT token to fetch the user meta information from memcached.

If there is no entry in the memcached with “TokenId” then Nginx simply senda a response as “LOGGED_OUT” to the client.

But in our case user is logged in into the system, So there will be an entry in memcached with TokenId. So fetch that user meta information to do the following checks.

Fingerprint mismatch : While sending the authenticate request, client is sending fingerprint along with username and password. We are storing that fingerprint value in memcached and we use this value to compare with the fingerprint which is coming in every request. If fingerprint matches, then it’s proceed further. Otherwise nginx will send a response to client saying that fingerprint is mismatched.

Session idle time out :  While successful authentication of a user at authentication server side, we are putting configured session_idle_timeout of a user in memcached. If it’s configured as “-1”, then we simply skip the session idle time out check. Otherwise for every request just we check whether session is idle or not. If session is not idle, we update the last_access_time value to current system time in memcached. If session is idle then Nginx send below response to the client.

Complete Validation Logic at Nginx using Lua

base-validation.lua

Step 12

Once the request gone through the above mentioned validation logic, Nginx proxy_pass the request to the application.

sample-nginx.conf

Step 13

Application sends a response of requested resource to the client.

How to achieve logout ?

There is a open question (unanswered) regarding how to achieve the log out at server side, if we go by the stateless authentication using JWT.

Mostly people are discussing about handling the log out at client side.

  • When user clicks on logout, simply client can remove the token from local storage.

But i come up with a solution to achieve the logout at server side by make use of Memcached.

  • When user clicks on logout, Remove the entry from Memcached which  we put it in Step 7. And client also can delete the token from local storage as well. If you see the validation logic which i have completely covered in Step 11, there i’m checking the entry in memcached. If there is no entry in memcached, means user logged out from the application.

[原创] Maven Surefire Plugin Using POJO Tests 使用 Junit 或 TestNg 测试类默认命名规范

开始时候 运行测试用例发现只有部分运行了,始终找不到原因

原来在单元测试时候默认 规范可以使用三种,但是网上找到的都是互相抄袭地错误方法

JUnit 4以及TestNG。

在默认情况下,maven-surefire-plugin的test目标会自动执行测试源码路径

(默认为src/test/java/)下所有符合一组命名模式的测试类。这组模式为:

**/Test*.java:任何子目录下所有命名以Test开关的Java类。

**/*TestCase.java:任何子目录下所有命名以TestCase结尾的Java类。

**/*Test.java:任何子目录下所有命名以Test结尾的Java类。

但是 Maven Surefire 只支持一种: 如果需要其他规则要手动写到配置文件里

POJO tests look very much like JUnit or TestNG tests, though they do not require dependencies on these artifacts. A test class should be named **/*Test and should contain test* methods which will each be executed by Surefire.

https://maven.apache.org/surefire/maven-surefire-plugin/examples/pojo-test.html

另外有返回值的方法, TestNG 需要使用 <suite allow-return-values=”true”> 配置,默认会跳过

[转][Google Guava] 强大的集合工具类:java.util.Collections中未包含的集合工具

原文链接 译文链接 译者:沈义扬,校对:丁一

尚未完成: Queues, Tables工具类

任何对JDK集合框架有经验的程序员都熟悉和喜欢java.util.Collections包含的工具方法。Guava沿着这些路线提供了更多的工具方法:适用于所有集合的静态方法。这是Guava最流行和成熟的部分之一。

我们用相对直观的方式把工具类与特定集合接口的对应关系归纳如下:

集合接口 属于JDK还是Guava 对应的Guava工具类
Collection JDK Collections2:不要和java.util.Collections混淆
List JDK Lists
Set JDK Sets
SortedSet JDK Sets
Map JDK Maps
SortedMap JDK Maps
Queue JDK Queues
Multiset Guava Multisets
Multimap Guava Multimaps
BiMap Guava Maps
Table Guava Tables

在找类似转化、过滤的方法?请看第四章,函数式风格。

静态工厂方法

在JDK 7之前,构造新的范型集合时要讨厌地重复声明范型:

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>();

我想我们都认为这很讨厌。因此Guava提供了能够推断范型的静态工厂方法:

List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList();
Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();

可以肯定的是,JDK7版本的钻石操作符(<>)没有这样的麻烦:

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>();

但Guava的静态工厂方法远不止这么简单。用工厂方法模式,我们可以方便地在初始化时就指定起始元素。

Set<Type> copySet = Sets.newHashSet(elements);
List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");

此外,通过为工厂方法命名(Effective Java第一条),我们可以提高集合初始化大小的可读性:

List<Type> exactly100 = Lists.newArrayListWithCapacity(100);
List<Type> approx100 = Lists.newArrayListWithExpectedSize(100);
Set<Type> approx100Set = Sets.newHashSetWithExpectedSize(100);

确切的静态工厂方法和相应的工具类一起罗列在下面的章节。

注意:Guava引入的新集合类型没有暴露原始构造器,也没有在工具类中提供初始化方法。而是直接在集合类中提供了静态工厂方法,例如:

Multiset<String> multiset = HashMultiset.create();

Iterables

在可能的情况下,Guava提供的工具方法更偏向于接受Iterable而不是Collection类型。在Google,对于不存放在主存的集合 ——比如从数据库或其他数据中心收集的结果集,因为实际上还没有攫取全部数据,这类结果集都不能支持类似size()的操作 ——通常都不会用Collection类型来表示。

因此,很多你期望的支持所有集合的操作都在Iterables类中。大多数Iterables方法有一个在Iterators类中的对应版本,用来处理Iterator。

截至Guava 1.2版本,Iterables使用FluentIterable进行了补充,它包装了一个Iterable实例,并对许多操作提供了”fluent”(链式调用)语法。

下面列出了一些最常用的工具方法,但更多Iterables的函数式方法将在第四章讨论。

常规方法

concat(Iterable<Iterable>) 串联多个iterables的懒视图* concat(Iterable...)
frequency(Iterable, Object) 返回对象在iterable中出现的次数 与Collections.frequency (Collection,   Object)比较;Multiset
partition(Iterable, int) 把iterable按指定大小分割,得到的子集都不能进行修改操作 Lists.partition(List, int)paddedPartition(Iterable, int)
getFirst(Iterable, T default) 返回iterable的第一个元素,若iterable为空则返回默认值 与Iterable.iterator(). next()比较;FluentIterable.first()
getLast(Iterable) 返回iterable的最后一个元素,若iterable为空则抛出NoSuchElementException getLast(Iterable, T default)
FluentIterable.last()
elementsEqual(Iterable, Iterable) 如果两个iterable中的所有元素相等且顺序一致,返回true 与List.equals(Object)比较
unmodifiableIterable(Iterable) 返回iterable的不可变视图 与Collections. unmodifiableCollection(Collection)比较
limit(Iterable, int) 限制iterable的元素个数限制给定值 FluentIterable.limit(int)
getOnlyElement(Iterable) 获取iterable中唯一的元素,如果iterable为空或有多个元素,则快速失败 getOnlyElement(Iterable, T default)

*译者注:懒视图意味着如果还没访问到某个iterable中的元素,则不会对它进行串联操作。

Iterable<Integer> concatenated = Iterables.concat(
        Ints.asList(1, 2, 3),
        Ints.asList(4, 5, 6)); // concatenated包括元素 1, 2, 3, 4, 5, 6
String lastAdded = Iterables.getLast(myLinkedHashSet);
String theElement = Iterables.getOnlyElement(thisSetIsDefinitelyASingleton);
//如果set不是单元素集,就会出错了!

与Collection方法相似的工具方法

通常来说,Collection的实现天然支持操作其他Collection,但却不能操作Iterable。

下面的方法中,如果传入的Iterable是一个Collection实例,则实际操作将会委托给相应的Collection接口方法。例如,往 Iterables.size方法传入是一个Collection实例,它不会真的遍历iterator获取大小,而是直接调用 Collection.size。

方法 类似的Collection方法 等价的FluentIterable方法
addAll(Collection addTo,   Iterable toAdd) Collection.addAll(Collection)
contains(Iterable, Object) Collection.contains(Object) FluentIterable.contains(Object)
removeAll(Iterable   removeFrom, Collection toRemove) Collection.removeAll(Collection)
retainAll(Iterable   removeFrom, Collection toRetain) Collection.retainAll(Collection)
size(Iterable) Collection.size() FluentIterable.size()
toArray(Iterable, Class) Collection.toArray(T[]) FluentIterable.toArray(Class)
isEmpty(Iterable) Collection.isEmpty() FluentIterable.isEmpty()
get(Iterable, int) List.get(int) FluentIterable.get(int)
toString(Iterable) Collection.toString() FluentIterable.toString()

FluentIterable

除了上面和第四章提到的方法,FluentIterable还有一些便利方法用来把自己拷贝到不可变集合

ImmutableList
ImmutableSet toImmutableSet()
ImmutableSortedSet toImmutableSortedSet(Comparator)

Lists

除了静态工厂方法和函数式编程方法,Lists为List类型的对象提供了若干工具方法。

方法 描述
partition(List, int) 把List按指定大小分割
reverse(List) 返回给定List的反转视图。注: 如果List是不可变的,考虑改用ImmutableList.reverse()
List countUp = Ints.asList(1, 2, 3, 4, 5);
List countDown = Lists.reverse(theList); // {5, 4, 3, 2, 1}
List<List> parts = Lists.partition(countUp, 2);//{{1,2}, {3,4}, {5}}

静态工厂方法

Lists提供如下静态工厂方法:

具体实现类型 工厂方法
ArrayList basic, with elements, from Iterable, with exact capacity, with expected size, from Iterator
LinkedList basic, from Iterable

Sets

Sets工具类包含了若干好用的方法。

集合理论方法

我们提供了很多标准的集合运算(Set-Theoretic)方法,这些方法接受Set参数并返回SetView,可用于:

  • 直接当作Set使用,因为SetView也实现了Set接口;
  • copyInto(Set)拷贝进另一个可变集合;
  • immutableCopy()对自己做不可变拷贝。
方法
union(Set, Set)
intersection(Set, Set)
difference(Set, Set)
symmetricDifference(Set,   Set)

使用范例:

Set<String> wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight");
Set<String> primes = ImmutableSet.of("two", "three", "five", "seven");
SetView<String> intersection = Sets.intersection(primes,wordsWithPrimeLength);
// intersection包含"two", "three", "seven"
return intersection.immutableCopy();//可以使用交集,但不可变拷贝的读取效率更高

其他Set工具方法

方法 描述 另请参见
cartesianProduct(List<Set>) 返回所有集合的笛卡儿积 cartesianProduct(Set...)
powerSet(Set) 返回给定集合的所有子集
Set<String> animals = ImmutableSet.of("gerbil", "hamster");
Set<String> fruits = ImmutableSet.of("apple", "orange", "banana");

Set<List<String>> product = Sets.cartesianProduct(animals, fruits);
// {{"gerbil", "apple"}, {"gerbil", "orange"}, {"gerbil", "banana"},
//  {"hamster", "apple"}, {"hamster", "orange"}, {"hamster", "banana"}}

Set<Set<String>> animalSets = Sets.powerSet(animals);
// {{}, {"gerbil"}, {"hamster"}, {"gerbil", "hamster"}}

静态工厂方法

Sets提供如下静态工厂方法:

具体实现类型 工厂方法
HashSet basic, with elements, from Iterable, with expected size, from Iterator
LinkedHashSet basic, from Iterable, with expected size
TreeSet basic, with Comparator, from Iterable

Maps

Maps类有若干值得单独说明的、很酷的方法。

uniqueIndex

Maps.uniqueIndex(Iterable,Function)通常针对的场景是:有一组对象,它们在某个属性上分别有独一无二的值,而我们希望能够按照这个属性值查找对象——译者注:这个方法返回一个Map,键为Function返回的属性值,值为Iterable中相应的元素,因此我们可以反复用这个Map进行查找操作。

比方说,我们有一堆字符串,这些字符串的长度都是独一无二的,而我们希望能够按照特定长度查找字符串:

ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(strings,
    new Function<String, Integer> () {
        public Integer apply(String string) {
            return string.length();
        }
    });

如果索引值不是独一无二的,请参见下面的Multimaps.index方法。

difference

Maps.difference(Map, Map)用来比较两个Map以获取所有不同点。该方法返回MapDifference对象,把不同点的维恩图分解为:

entriesInCommon() 两个Map中都有的映射项,包括匹配的键与值
entriesDiffering() 键相同但是值不同值映射项。返回的Map的值类型为MapDifference.ValueDifference,以表示左右两个不同的值
entriesOnlyOnLeft() 键只存在于左边Map的映射项
entriesOnlyOnRight() 键只存在于右边Map的映射项
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
MapDifference<String, Integer> diff = Maps.difference(left, right);

diff.entriesInCommon(); // {"b" => 2}
diff.entriesInCommon(); // {"b" => 2}
diff.entriesOnlyOnLeft(); // {"a" => 1}
diff.entriesOnlyOnRight(); // {"d" => 5}

处理BiMap的工具方法

Guava中处理BiMap的工具方法在Maps类中,因为BiMap也是一种Map实现。

BiMap工具方法 相应的Map工具方法
synchronizedBiMap(BiMap) Collections.synchronizedMap(Map)
unmodifiableBiMap(BiMap) Collections.unmodifiableMap(Map)

静态工厂方法

Maps提供如下静态工厂方法:

具体实现类型 工厂方法
HashMap basic, from Map, with expected size
LinkedHashMap basic, from Map
TreeMap basic, from Comparator, from SortedMap
EnumMap from Class, from Map
ConcurrentMap:支持所有操作 basic
IdentityHashMap basic

Multisets

标准的Collection操作会忽略Multiset重复元素的个数,而只关心元素是否存在于Multiset中,如containsAll方法。为此,Multisets提供了若干方法,以顾及Multiset元素的重复性:

方法 说明 Collection方法的区别
containsOccurrences(Multiset   sup, Multiset sub) 对任意o,如果sub.count(o)<=super.count(o),返回true Collection.containsAll忽略个数,而只关心sub的元素是否都在super中
removeOccurrences(Multiset   removeFrom, Multiset toRemove) 对toRemove中的重复元素,仅在removeFrom中删除相同个数。 Collection.removeAll移除所有出现在toRemove的元素
retainOccurrences(Multiset   removeFrom, Multiset toRetain) 修改removeFrom,以保证任意o都符合removeFrom.count(o)<=toRetain.count(o) Collection.retainAll保留所有出现在toRetain的元素
intersection(Multiset,   Multiset) 返回两个multiset的交集; 没有类似方法
Multiset<String> multiset1 = HashMultiset.create();
multiset1.add("a", 2);

Multiset<String> multiset2 = HashMultiset.create();
multiset2.add("a", 5);

multiset1.containsAll(multiset2); //返回true;因为包含了所有不重复元素,
//虽然multiset1实际上包含2个"a",而multiset2包含5个"a"
Multisets.containsOccurrences(multiset1, multiset2); // returns false

multiset2.removeOccurrences(multiset1); // multiset2 现在包含3个"a"
multiset2.removeAll(multiset1);//multiset2移除所有"a",虽然multiset1只有2个"a"
multiset2.isEmpty(); // returns true

Multisets中的其他工具方法还包括:

copyHighestCountFirst(Multiset) 返回Multiset的不可变拷贝,并将元素按重复出现的次数做降序排列
unmodifiableMultiset(Multiset) 返回Multiset的只读视图
unmodifiableSortedMultiset(SortedMultiset) 返回SortedMultiset的只读视图
Multiset<String> multiset = HashMultiset.create();
multiset.add("a", 3);
multiset.add("b", 5);
multiset.add("c", 1);

ImmutableMultiset highestCountFirst = Multisets.copyHighestCountFirst(multiset);
//highestCountFirst,包括它的entrySet和elementSet,按{"b", "a", "c"}排列元素

Multimaps

Multimaps提供了若干值得单独说明的通用工具方法

index

作为Maps.uniqueIndex的兄弟方法,Multimaps.index(Iterable, Function)通常针对的场景是:有一组对象,它们有共同的特定属性,我们希望按照这个属性的值查询对象,但属性值不一定是独一无二的。

比方说,我们想把字符串按长度分组。

ImmutableSet digits = ImmutableSet.of("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine");
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
    public Integer apply(String string) {
        return string.length();
    }
};

ImmutableListMultimap<Integer, String> digitsByLength= Multimaps.index(digits, lengthFunction);
/*
*  digitsByLength maps:
*  3 => {"one", "two", "six"}
*  4 => {"zero", "four", "five", "nine"}
*  5 => {"three", "seven", "eight"}
*/

invertFrom

鉴于Multimap可以把多个键映射到同一个值(译者注:实际上这是任何map都有的特性),也可以把一个键映射到多个值,反转Multimap也会很有用。Guava 提供了invertFrom(Multimap toInvert,
Multimap dest)
做这个操作,并且你可以自由选择反转后的Multimap实现。

注:如果你使用的是ImmutableMultimap,考虑改用ImmutableMultimap.inverse()做反转。

ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.putAll("b", Ints.asList(2, 4, 6));
multimap.putAll("a", Ints.asList(4, 2, 1));
multimap.putAll("c", Ints.asList(2, 5, 3));

TreeMultimap<Integer, String> inverse = Multimaps.invertFrom(multimap, TreeMultimap<String, Integer>.create());
//注意我们选择的实现,因为选了TreeMultimap,得到的反转结果是有序的
/*
* inverse maps:
*  1 => {"a"}
*  2 => {"a", "b", "c"}
*  3 => {"c"}
*  4 => {"a", "b"}
*  5 => {"c"}
*  6 => {"b"}
*/

forMap

想在Map对象上使用Multimap的方法吗?forMap(Map)把Map包装成SetMultimap。这个方法特别有用,例如,与Multimaps.invertFrom结合使用,可以把多对一的Map反转为一对多的Multimap。

Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 1, "c", 2);
SetMultimap<String, Integer> multimap = Multimaps.forMap(map);
// multimap:["a" => {1}, "b" => {1}, "c" => {2}]
Multimap<Integer, String> inverse = Multimaps.invertFrom(multimap, HashMultimap<Integer, String>.create());
// inverse:[1 => {"a","b"}, 2 => {"c"}]

包装器

Multimaps提供了传统的包装方法,以及让你选择Map和Collection类型以自定义Multimap实现的工具方法。

只读包装 Multimap ListMultimap SetMultimap SortedSetMultimap
同步包装 Multimap ListMultimap SetMultimap SortedSetMultimap
自定义实现 Multimap ListMultimap SetMultimap SortedSetMultimap

自定义Multimap的方法允许你指定Multimap中的特定实现。但要注意的是:

  • Multimap假设对Map和Supplier产生的集合对象有完全所有权。这些自定义对象应避免手动更新,并且在提供给Multimap时应该是空的,此外还不应该使用软引用、弱引用或虚引用。
  • 无法保证修改了Multimap以后,底层Map的内容是什么样的。
  • 即使Map和Supplier产生的集合都是线程安全的,它们组成的Multimap也不能保证并发操作的线程安全性。并发读操作是工作正常的,但需要保证并发读写的话,请考虑用同步包装器解决。
  • 只有当Map、Supplier、Supplier产生的集合对象、以及Multimap存放的键值类型都是可序列化的,Multimap才是可序列化的。
  • Multimap.get(key)返回的集合对象和Supplier返回的集合对象并不是同一类型。但如果Supplier返回的是随机访问集合,那么Multimap.get(key)返回的集合也是可随机访问的。

请注意,用来自定义Multimap的方法需要一个Supplier参数,以创建崭新的集合。下面有个实现ListMultimap的例子——用TreeMap做映射,而每个键对应的多个值用LinkedList存储。

ListMultimap<String, Integer> myMultimap = Multimaps.newListMultimap(
    Maps.<String, Collection>newTreeMap(),
    new Supplier<LinkedList>() {
        public LinkedList get() {
            return Lists.newLinkedList();
        }
    });

Tables

Tables类提供了若干称手的工具方法。

自定义Table

堪比Multimaps.newXXXMultimap(Map, Supplier)工具方法,Tables.newCustomTable(Map, Supplier<Map>)允许你指定Table用什么样的map实现行和列。

// 使用LinkedHashMaps替代HashMaps
Table<String, Character, Integer> table = Tables.newCustomTable(
Maps.<String, Map<Character, Integer>>newLinkedHashMap(),
new Supplier<Map<Character, Integer>> () {
public Map<Character, Integer> get() {
return Maps.newLinkedHashMap();
}
});

transpose

transpose(Table<R, C, V>)方法允许你把Table<C, R, V>转置成Table<R, C, V>。例如,如果你在用Table构建加权有向图,这个方法就可以把有向图反转。

包装器

还有很多你熟悉和喜欢的Table包装类。然而,在大多数情况下还请使用ImmutableTable

Unmodifiable Table RowSortedTable

PHP跳出多层循环

使用下面方法可以直接跳出循环(推荐):

$http = 0;

foreach($arr as $v){

    for($i=1;$i<=3;$i++){
        
        if($http == 200) break 2;

        echo $i,"\n";

        $http = 200;
    }

    echo $v,'这里不会输出';
}

echo 'END';

但是有些特殊情况使 层数不确定,或是很多层时(一般不会有这种情况):可以使用goto;

foreach($arr as $v){
    for($a=1;$a<=3;$a++)
    for($b=1;$b<=3;$b++)
    for($c=1;$c<=3;$c++)
    for($x=1;$x<=3;$x++)
    for($d=1;$d<=3;$d++)
    for($v=1;$v<=3;$v++)
    for($z=1;$z<=3;$z++)
    for($n=1;$n<=3;$n++){

//        if($http == 200) break ??;
        if($http == 200) goto end;

        echo $n,"\n";

        $http = 200;
    }

    echo $v,'这里不会输出';
}
end:

echo 'END';

打造你的php安全程序第一步:禁止掉不安全的php函数(php.ini)

为了使php程序更安全,很多站长都选择了禁用一些比较敏感的函数,那影响php安全的函数到底有哪些呢,下面我们列出了一些:
1、phpinfo()
功能描述:输出 PHP 环境信息以及相关的模块、WEB 环境等信息。
危险等级:中
2、passthru()
功能描述:允许执行一个外部程序并回显输出,类似于 exec()。
危险等级:高
3、exec()
功能描述:允许执行一个外部程序(如 UNIX Shell 或 CMD 命令等)。
危险等级:高
4、system()
功能描述:允许执行一个外部程序并回显输出,类似于 passthru()。
危险等级:高
5、chroot()
功能描述:可改变当前 PHP 进程的工作根目录,仅当系统支持 CLI 模式
PHP 时才能工作,且该函数不适用于 Windows 系统。
危险等级:高
6、scandir()
功能描述:列出指定路径中的文件和目录。
危险等级:中
7、chgrp()
功能描述:改变文件或目录所属的用户组。
危险等级:高
8、chown()
功能描述:改变文件或目录的所有者。
危险等级:高
9、shell_exec()
功能描述:通过 Shell 执行命令,并将执行结果作为字符串返回。
危险等级:高
10、proc_open()
功能描述:执行一个命令并打开文件指针用于读取以及写入。
危险等级:高
11、proc_get_status()
功能描述:获取使用 proc_open() 所打开进程的信息。
危险等级:高
12、error_log()
功能描述:将错误信息发送到指定位置(文件)。
安全备注:在某些版本的 PHP 中,可使用 error_log() 绕过 PHP safe mode,
执行任意命令。
危险等级:低
13、ini_alter()
功能描述:是 ini_set() 函数的一个别名函数,功能与 ini_set() 相同。
具体参见 ini_set()。
危险等级:高
14、ini_set()
功能描述:可用于修改、设置 PHP 环境配置参数。
危险等级:高
15、ini_restore()
功能描述:可用于恢复 PHP 环境配置参数到其初始值。
危险等级:高
16、dl()
功能描述:在 PHP 进行运行过程当中(而非启动时)加载一个 PHP 外部模块。
危险等级:高
17、pfsockopen()
功能描述:建立一个 Internet 或 UNIX 域的 socket 持久连接。
危险等级:高
18、syslog()
功能描述:可调用 UNIX 系统的系统层 syslog() 函数。
危险等级:中
19、readlink()
功能描述:返回符号连接指向的目标文件内容。
危险等级:中
20、symlink()
功能描述:在 UNIX 系统中建立一个符号链接。
危险等级:高
21、popen()
功能描述:可通过 popen() 的参数传递一条命令,并对 popen() 所打开的文件进行执行。
危险等级:高
22、stream_socket_server()
功能描述:建立一个 Internet 或 UNIX 服务器连接。
危险等级:中
23、putenv()
功能描述:用于在 PHP 运行时改变系统字符集环境。在低于 5.2.6 版本的 PHP 中,可利用该函数
修改系统字符集环境后,利用 sendmail 指令发送特殊参数执行系统 SHELL 命令。
危险等级:高

禁用方法如下:
打开/etc/php.ini文件,
查找到 disable_functions ,添加需禁用的函数名,如下:
phpinfo,eval,passthru,exec,system,chroot,scandir,chgrp,chown,shell_exec,proc_open,proc_get_status

看过这些函数,站长们应该去检查一下自己的php.ini 看看这些函数是否禁用了。

当然禁止了这些可能会引起安全因素的php函数。你还必须在php.ini里,进一步把php的错误给关闭,这样才更加有针对性的排除你的php被别人利用错误信息进行攻击。
在php.ini里把
display_error = On
改为:
display_error = Off 即可;

10个令人惊讶的NodeJS开源项目

在几年的时间里,NodeJS逐渐发展成一个成熟的开发平台,吸引了许多开发者。有许多大型高流量网站都采用NodeJS进行开发,像PayPal,此外,开发人员还可以使用它来开发一些快速移动Web框架。

除了Web应用外,NodeJS也被应用在许多方面,本文盘点了NodeJS在其它方面所开发的十大令人神奇的项目,这些项目涉及到应用程序监控、媒体流、远程控制、桌面和移动应用等等。

  1.NodeOS

NodeOS是采用NodeJS开发的一款友好的操作系统,该操作系统是完全建立在Linux内核之上的,并且采用shell和NPM进行包管理,采用 NodeJS不仅可以很好地进行包管理,还可以很好的管理脚本、接口等。目前,Docker和Vagrant都是采用NodeOS的首个版本进行构建的。

 

  2.Noduino

许多硬件黑客希望通过Web页面即可控制他们的Arduino,Noduino就是这样的一个项目,一个简单灵活的JavaScript和NodeJS 框架,通过使用HTML5、Socket.IO和NodeJS的Web应用来控制Arduino。目前,该项目刚刚启动,支持一些常用功能,比如从 Arduino中捕获事件(例如点击按钮)等。

  3.Node-WebKit

Node-Webkit是一个基于Chromium与NodeJS的应用程序运行器,允许开发者使用Web技术编写桌面应用。它是NodeJS与WebKit技术的融合,提供一个跨Windows、Linux平台的客户端应用开发的底层框架。

跨平台开发并非易事,其中一种方式便是使用Web技术和Node-Webkit开发桌面应用来代替那些庞大且笨重的开发框架。

  4.PDFKit

PDFKit是采用NodeJS开发的一款PDF文档生成库,它使用一个“HTML5 canvas-like API”来创建矢量图形和字体嵌入,并且支持许多标准的PDF功能,如文件的安全性、表的创建、文本换行、项目符号、高亮提示、注释等PDF功能。

注意,PDFKit是一款PDF生成工具,而不是一个文档转换系统。如果你想对现有的PDF文档进行操作,你可以使用另一个NodeJS项目—— Scissors

  5.Log.io

Log.io是一个基于NodeJS开发的实时日志监控项目,在浏览器里访问。需要注意的是,Log.io只监视日志变动并不存储日志,不过这个没关系,只要知道日志存储在哪个机器上。

Log.io使用 Socket.io库发 送活动报告的,和其他的监控工具一样,Log.io也采用服务器-客户端的模式。Log.io由两部分组成:server和 harveste,server运行在机器 A(服务器)上监视和纪录其他机器发来的日志消息;log harvester 运行在机器 B(客户端)上用来监听和收集机器 B上的日志改动,并将改动发送给机器 A,每个需要纪录日志的机器都需要一个harvester。

  6.NodecastLeapcast

受谷歌Chromecast技术的启发,开发者使用NodeJS开发出不少Chromecast仿真应用。如Nodecast或Leapcast。在 PC上运行Nodecast或Leapcast,启动移动设备,选择一个支持Chromecast的应用程序,然后你就可以把移动广播上的内容映射到电脑 上了,把电脑当成一个流媒体使用。

在这两个应用中,Nodecast比较简单些,但相应的功能也比较少,它仅经过了YouTube和Google Music的测试( DEMO)。注意,大家不要把Nodecast与 Nodecast库混淆,后者使用DIAL发现协议提供链接设备(类似Chromecast)。

  7.Nexe

Nexe是一款小巧却非常实用的NodeJS工具,它可以为NodeJS应用创建单一可执行的文件,并且无需安装运行时,这样,一些非技术终端的用户就 无需变动NodeJS应用的所有依赖程序。如果你想发布一个NodeJS应用程序,并且没有GUI,Nexe则是您的最佳选择。目前该应用程序的一个弊端 是不能在Windows平台上工作,只适用于Linux和Mac OS X平台,并且它也不支持本地NodeJS模块。

  8.Hyro

Hyro是使用NodeJS开发的一款实时HTML5编辑器,如下图所示,左边显示HTML源码,右边显示内容。语法高亮由 CodeMirror提供。Hyro并不打算成为一款成熟的Web IDE,更像是一款轻量级的HTML或CSS记事本。

  9.Haroopad

Haroopad是一款Linux上的markdown编辑器,使用Chromium作为UI,支持Windows、Mac OS X和Linux。主题样式丰富,语法标亮支持54种编程语言。 如下图所示,一边是代码编辑窗口,一边是预览窗口,可以实时更新。其邮件导出功能可以将文档发送到Tumblr和Evernote。

  10.TiddlyWiki5

TiddlyWiki是一款交互式的wiki,非常灵活,它也可以在浏览器里作为单一的HTML文件或者是一款功能强大的NodeJS应用程序。

TiddlyWiki5是全新设计的5.0版本,它可以直接集成NodeJS解锁一系列的功能,但在单机模式下是不可用的。目前,TiddlyWiki5仍处于测试阶段。

来自: InfoWorld

 

C/C++/Perl/汇编/Java效率比较

这篇文章真的不错,转载于此,盖因这里支持直接Copy网页,省去了排版的麻烦,^_^

本文适合初学编程的程序员阅读,它对比了几种编程语言在解决同一问题的时候的运效率。并通过具体的例子进行了量化分析。主要目的是帮助初学者认识各种编程语言的特质,并且能够理性的选择适合的编程语言来进行工作。

事发

我无聊的翻着散落案头的书籍,这些都是五花八门的关于编程和系统管理的著作。干了这么多年程序员,大大小小的软件和项目也做了无数。每每有新入行的朋友问我这个所谓的”老前辈”:哪种语言最好之类的问题,我总会作出一副知识渊博的样子,复述着从更老的老前辈那里听来的或者某些名著上看来的”知识”。就好比我们从学习编程的第一天起,就被计算机老师告知,COBOL语言是擅长处理商务事务、FOTRAN语言是用于科学计算一样。类似的知识还有”汇编语言比C语言快得多”以及”JAVA是一种效率很低的语言环境”在一代又一代的程序员中口耳相传,几乎成为了毋庸置疑的真理。

我产生了一个想法,能不能对于同一个应用用几种编程语言分别实现,来比较一下看看到底哪种语言效率最高?

老实说我自己都觉得这个想法很无聊,想想谁会反复用不同的语言写同一个程序呢?下雨天打孩子,闲着也是闲着。再说,对于某种语言的弱点和优势有一个量化的分析,对于我们今后在做项目的时候面临工具选择也少许有一点指导意义。另外,觉得好玩才是我做这件事情的真正原因。

选题

选择一个什么样的程序问题进行这样的测试呢?这是一个很关键的问题,也最容易影响测试的公平性。另外的,对于每种语言,各自的优势都是不同的。程序员的偏爱也是各不相同的。在网上和现实中,对于什么语言更好一些的争论从来就没有停止过。甚至的,各门各派的程序员所构成的各种阵营,把某种语言奉若神明的也不在少数。不信,你在CSDN的JAVA论坛说一句”JAVA执行效率太低了云云”试试?立刻会被铺天盖地的板砖掀翻在地。类似的,还有管理员对于操作系统的偏好和争论:在Linux论坛你要是表扬Windows,其惨烈程度简直是难以言状。因此,从这个意义上来说,程序员们对于编程语言的偏好,类似于战士之喜爱枪械,赛手之喜爱赛车,已经上升为一种精神层面的东西了。蔡学镛先生说得好:有人逢微软必反,有人逢微软必捧。这是一种纯粹的精神上的爱,但它可能会影响正常的、科学的思考。

可以预料的,我这篇文章一定会遭到各路豪杰的迎头痛击。

好了,让我们言归正转吧。首先的,我们的选题中要使用的各种程序语言的最常用的要素。什么是最常用的要素呢?当然了,大家都有的就是赋值、数组操作、循环、判断等。另外,对IO的操作也是编程语言重要的内容。其次的,操作时间一定要长,否则,对于解释性的语言来说是极不公平的:解释器还没调入内存呢,人家编译派的已经运行完了。最后,就是程序不能太复杂。除了我没有那么大的毅力用各种语言完成一个复杂算法的决心外,程序过于复杂,算法在测试中起的作用就越来越大,影响运行效率的原因也就增加了。算法过于复杂,开发工具的扩展部分用得也就越多。于是就成了语言附加库之间的竞赛了,这是我不愿意看到的。

考虑上述因素,我设计了一个简单的选题:从指定文本文件中搜索指定字符串,计算个数。并且打印出搜索到的个数作为结果输出。作为程序员的你粗粗过一下脑子,马上会想到这个算法里面包含了条件判断、循环、数组操作等基本的程序语言因素。这满足了上面第一个条件。另外的,为了满足第二个条件,我准备了一个多达2G的文本文件,总共有文本1500万行多。这保怔了足够的运行时间(但应该不会太长),而决不会一眨眼就执行完了。最后的,我们都知道,在文本串里面搜索子串的算法是数据结构课本中的一个典型的例子(考试也经常被考到的),也满足算法简单的要求。同时,为了让每个程序的环境都一样,我得每测试一次就重新启动一次机器,避免CACHE的影响。

准备

比赛嘛,就需要公平。首先的,硬件平台要统一。我找了一台看起来还不错的机器(服务器):两颗PIII800,1G内存。操作系统嘛,原来的机器上有新装的Windows2000Server版本。几乎没装什么别的应用。我偷懒了一下,没有重新安装OS,就这样用吧。

第一个选手:PERL

如果别人交给我这个题目,我会马上决定用PERL语言来做这件事。这个题目是完全的文本处理问题,还有比用PERL来做更合适的吗?因为PERL是专门为了文本处理而编制的语言。事实上也是这样,我用了2分钟,写了几行代码,就轻松实现了这个问题。这也说明了,选择适用的编程语言工具,比选择喜爱的工具更重要。

#!/usr/bin/perl
$filename=”d:/access.log_”;
$count = 0;
open(FILE , “<$filename”);
while(<FILE>)
{
@match_list = ($_ =~ /HIT/g);
$count=$count+@match_list;
}
close(FILE);
print “Count = $count “;
exit

PERL是一位语言学家Larry Wall发明的,事实上,早期这种语言是专门用于在UNIX平台处理文字文件的(Perl=Practical Extraction Report Language:实用报表析取语言)。后来人们发现有大量文本构成的HTML页面用PERL来做CGI程序生成动态页面再合适不过了。因为互联网的兴起,PERL跟着发大了起来。这种语言的语法和C语言基本类似,因此比较好掌握,并且的,其关于”正则表达式”处理的强大功能目前基本上无人能够望其项背。事实上,类似于”过滤出含有TOM或者ABC的、并且后者的第一个和第三个字母大写,前者最少出现2次,后者出现5次、而且中间间隔8个或4个字母或空格的文本行”。我猜你正在反复的揣摩这句话,事实上,这就是所谓正则表达式,这样的问题,在PERL只需要一行语句就可以完成。在C语言中需要多少语句才能实现呢。

我略略解释一下上面的程序,让没有用过PERL语言的程序员也有个感性认识。

第一行是在UNIX中才用得到,因为PERL是一种基于解释的脚本语言。

第四行是打开文件

下面的循环是一行一行的读文件的内容。循环中间的第一句话是把凡是文本行中含有的HIT全部放到一个数组中;循环中中的第二句话是统计一下刚才的数组中有几个HIT,然后累加起来。循环完成了,我们的任务也就完成了。怎么样,很简单吧?”/HIT/g”就是最简单的正则表达式。br />
现在的PERL语言早已经不是原来的脚本语言形象了,现代PERL几乎具备了其特语言的所有特性,并且的在模块的功能帮助下,可以实现很大的应用。而且还增加了一些面向对象的特点。尽管大多数人仍然在用它处理大量的文本,但也有使用PERL完成大型应用的,尤其是在WEB方面。值得一提的是PERL也是一个跨平台语言。

我的这个程序在测试平台上,使用PERL5.8解释器,用了8分18秒08完成了1500万行文本的扫描,并得出了正确的结果。

第二个选手:纯C

也许年龄大了,但是我真的很喜欢C语言。而且我最喜欢的就是使用指针和强制类型转换来任意操作数据。我甚至会在程序里通过指针手工拼凑一个长整性的数据。说句可能引起争议的话,我觉得JAVA语言抛弃可爱的指针的做法基本上就是逃避。因为掌握不好就不用,到头来就是牺牲了效率。

本文这个题目,用C语言来实现应该还是比较不错的选择。下面的代码就是在VC下面实现的纯C代码的字符串搜索程序(为了避免图形界面的干扰,一律做成控制台程序)。编译的时候使用速度优先编译选项。

#include <stdio.h>
#include <string.h>void main()
{
int len=2048;
char filename[20];//文件名
char buff[10000];//文件缓冲区
char hit[5];
FILE *fd;
int i,j,flag=0,over=0;
int max,readed;
int count=0;//最后的结果
strcpy(&filename[0] , “d:/access.log_”);
strcpy(&hit[0] , “HIT”);
buff[0]=0x0;
buff[1]=0x0;
//打开文件:
if((fd = fopen(&filename[0] , “rb”))==NULL)
{
printf(“Error : Can not open file %s “,&filename[0]);
}
//读取文件内容
while(over != 1)
{
readed = fread(&buff[2] , 1 , len , fd);
if(readed < len)
{
over=1;
max=readed;
}
else
{
max=len;
}
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
//把最后两个字符转移到前面两个字节以防止切断搜索串.
buff[0]=buff[max];
buff[1]=buff[max+1];
}
fclose(fd);
printf(“count:%d “,count);
}

程序很好懂,用的也是教科书上面的标准字符串搜索算法,但是比前面的PERL程序长多了吧?那是因为人家PERL已经帮你完成了大部分工作。但是看到上面这段程序的运行结果你可能会高兴起来,它最快一次只用了2分10秒52,最慢也只用了2分20秒59就完成了1500万行文本的搜索任务。平均2分15秒多。为什么每次时间不一样呢?我不清楚具体原因,但学过操作系统的朋友会明白,只有在单道单任务的系统中,代码才能有执行上的可再现性。

有经验的朋友可能会说,你的缓冲区只用了2048字节,加大它速度还会增加呢。是的,而且我相信还有高手能作出更快的程序来,但这不重要,重要的是我们要考察的是不同语言完成同一件工作的效率。而且你能够明白,在程序中,改进什么能够提高效率,这就足够了。因为C语言程序中,这些都是自由可控的。

第三个选手:C++

C++和前面的C是亲戚。我简单的把前面的C代码移植过来,然后把文件输入部分改成了流类对象。至于算法部分嘛。跟前面的C是一模一样的。最后在编译的时候,除了使用速度最佳编译选项外,当然还用了C++的编译参数,因此执行文件的长度比前面的C要长一些,这说明我加的流类代码比标准C库要复杂。是的,C++应该说是目前流行的计算机编程语言中复杂度排名靠前的。其复杂的类和继承关系,以及各种初始化的次序和构造函数执行顺序等都需要考虑。还有多态以及动态联编技术等。C++也是我非常喜欢的语言,提供了面向对象的代码重用特性和足够的安全型,但是在效率上的确比纯C略逊一筹。你知道吗,大部分的操作系统核心几乎都是用纯C写成的,尽管很复杂,但很少有使用面向对象技术的。为什么,不是面向对象技术不好,也不是操作系统核心不够复杂(那什么复杂?),主要的考虑就是效率问题。

#include <stdio.h>
#include <string.h>
#include <fstream.h>void main()
{
int len=2048;
char filename[20];//文件名
char buff[10000];//文件缓冲区
char hit[5];
int i,j,flag=0;
int max;
int count=0;//最后的结果
strcpy(&filename[0] , “d:/access.log_”);
strcpy(&hit[0] , “HIT”);
buff[0]=0x0;
buff[1]=0x0;
//用输入流打开文件:
ifstream input(&filename[0]);
//读取文件内容
while(input)
{
input.getline(&buff[2] , len);
max = strlen(&buff[2]);
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}

}
printf(“count:%d “,count);
}

这段C++程序在测试平台上用了最快4分25秒95 到最慢5分40秒68的时间完成1500万行的文本检索,并在2G的文件中检索出10951968个”HIT”字符串。这结果是正确的。

第四个选手:汇编

本以为汇编程序能够达到前所未有的高速,把前面的选手远远抛在身后而笑傲江湖。这一想法支撑我完成了艰涩的代码。可事实上测试的结果缺让我大失所望,完全用机器指令书写的程序,去掉缓冲区才几百字节,算法和前面的C程序一模一样,扫描1500万行文本竟然最快也要2分14秒56!这甚至还比不过C语言的最快纪录。而平均下来,汇编程序的速度竟然和前面的C程序在伯仲之间。恐怕这样的结果也出乎大部分人的意外。因为我们从入行的那一天起,就被告知汇编是你所能够掌握的最快的语言!尽管代码坚涩难懂,但性能的代价是值得的。而从这里的测试看,你觉得向下面这样的代码,实现和C语言一样的速度和功能值得吗?

;堆栈段
STSG SEGMENT STACK ‘S’
DW 64 DUP(?)
STSG ENDS;数据段
DATA SEGMENT
rlength EQU 2048
fname DB ‘access.log_’,0
hit DB ‘HIT$’
fd DW ? ;文件句柄
resault DB ‘count : $’ ;结果提示
count DD 0 ;存放结果
disflag DB 0 ;显示标志
buff DB 5000 dup(0) ;缓冲区
DATA ENDS

;代码段
CODE SEGMENT
MAIN PROC FAR
ASSUME CS:CODE,DS:DATA,SS:STSG,ES:NOTHING
MOV AX,DATA
MOV DS,AX
;我的代码开始:
mov ah,3dh ;打开文件
lea dx,fname
mov al,00h ;文件打开方式
int 21h ;开始操作
;这里就不作错误处理了,偷懒喽!
;CF=0表示正确,CF=1表示错误,AX是文件句柄或者是错误代码
mov fd,ax ;保存文件句柄

READ: mov ah,3fh ;读文件
mov bx,fd ;文件句柄
mov cx,rlength ;要读length字节

lea dx,buff ;给出读缓冲区指针
add dx,2 ;缓冲区指针向后错两个(目的是解决边界问题:有一个HIT正好横跨rlength界限)
int 21h ;开始读
;AX里面是实际读出的字节数
;读完了以后,扫描缓冲区
push ax ;保存AX字节数
cmp ax,0
jz ALLEND ;文件读完了就退出

sub dx,2 ;指针向前错2个,
mov si,dx
add dx,2 ;把指针回到原来的位置
add dx,ax ;计算结尾
LOD3: cmp si,dx ;到头了就重新读一次文件
jz OVR
lods buff
lea bx,HIT
cmp al,[bx]
jnz LOD3 ;读第一个字节不相等就重新读一个

cmp si,dx
jz OVR
lods buff
cmp al,[bx+1]
jnz LOD3 ;如果第一个字节相等,就读第2个字节,不行等就从第一个字节再重比较。

cmp si,dx ;如果第二个字节也相等的话,就比较第三个字节。
jz OVR
lods buff
cmp al,[bx+2]
jnz LOD3 ;第三个字节不相等再从头开始
;有一个HIT匹配
push bx
lea bx,count
add WORD ptr [bx],1 ;计数器增加一个
adc WORD ptr [bx+2],0 ;进位
pop bx
jmp LOD3

OVR: mov ah,[si-1]
mov BYTE ptr buff+1 , ah
mov ah,[si-2]
mov BYTE ptr buff , ah

pop ax ;恢复这次总共读出的字节数
cmp ax,rlength ;看看是不是最后一次(剩余的零头)
jz READ
;如果是最后一次读文件,

ALLEND: mov ah,3eh ;关闭文件
mov bx,fd ;文句柄
int 21h ;关闭文件

mov ah,9 ;显示结果字符串
lea dx,resault
int 21h

;转换2进制结果到10进制ACSII形式
mov bx, WORD ptr count
call TERN

mov ax,4c00h ;返回DOS
int 21h
;结束代码,最大的数字已经排到了最前面
MAIN ENDP

TERN PROC ;这个子程序是转换并显示2进制数字的
mov cx,10000
call DEC_DIV
mov cx,1000
call DEC_DIV
mov cx,100
call DEC_DIV
mov cx,10
call DEC_DIV
mov cx,1
call DEC_DIV
ret
TERN ENDP
DEC_DIV PROC
mov ax,bx
mov dx,0
div cx
mov bx,dx
mov dl,al
add dl,30H
mov ah,disflag ;read flag
cmp ah,0
jnz DISP ;已经显示过有效数字了
cmp dl,30H
jz NODISP
mov disflag,1 ;作用是第一个有效数字出现前不显示0
DISP: mov ah,2
int 21H
NODISP: ret
DEC_DIV ENDP
CODE ENDS
END MAIN

上面这段代码我猜你也懒得仔细阅读。其实他不能”显示结果”。因为最后这段负责把最终结果转换成可显示ASCII码的程序实际上只能转换二进制十六位的数据,而最终的结果高达1000万挂零,显示会出错。由于这最终结果的显示已经和程序的运行没有大关系了,因此,我也就懒得去写一个32位的ASCII转换程序了。就这样吧。

第五个选手:JAVA

JAVA是一个不能不参加比赛的选手。有如此多的人热爱他,他们中的一半人是因为JAVA的面向对象特性以及良好的跨平台特性。而另一半人纯粹就是因为JAVA不姓”微(软)”,这就是意识形态在程序员头脑中对某种语言的注释。单纯从语言元素上来说,我还是比较喜欢JAVA的。因为他的语法干净、简洁。环境也好。虽然用虚拟机系统(JVM)的做法来实现跨平台特性并非什么了不得的创意(像不像30年前的BASIC解释器?别跟我说什么中间代码?几乎所有的解释器都是把语言因素翻译成中间代码的,JVM不过是分成2步来实现罢了,但从运行机制上应该是差不多的。),但JVM仍然将JAVA的跨平台特性做到了前所未有的地步。而且JVM是一个很干净的系统,让人用起来赏心悦目。说到这里我忍不住想提一下J2EE企业应用框架了。不知道有多少人能够看懂SUN出的J2EE的”理论著作”?满纸充斥着各种生造的概念,洋溢着溢美之词。JAVA的企业应用框架实在是比较复杂的东西,虽然赶不上后来的.NET框架,但足以让大多数初学者望而却步。一句话,东西太多了。事实上JAVA的企业级应用并没有想象的成功,iPlanet就随着电子商务概念的全面垮台而渐渐淡出。现在换了个名叫“SUNONE”――SUN公司员工原话。

我们回到JAVA的语言元素上来说,实际上JAVA可以被理解为被纯化的C++。JAVA去除了C++为了兼容C而增加的一些”非面向对象特质”,用其他的一些变通办法实现C++直接实现的功能,比如:多继承。在实现机制上,JAVA的程序会先编译成.CLASS文件,然后这种跨平台的中间代码就可以”一次编译,到处运行”了。当然必须运行在有JVM虚拟机的环境中,连图形什么的都可以照搬。换句话说,你用JAVA程序在PC屏幕上画一个圆,在JAVA-PDA上它还是圆的。

我在本次测试中,写了下面的代码,用JAVA做了同样的测试,测试中实际上用到了:JAVA的文件流类,运行了循环、条件判断、数组操作等基本的语言因素。环境是J2SE1.3.1-06。JAVA程序做1500万行的文本扫描用了8分21秒18。应该说是几种语言中最慢的,基本上和纯解释的PERL是在同一水准。J2EE的JVM环境还是经过优化的所谓HOTSPOT。

import java.io.*;
public class langtest
{
public static void main(String[] args)
{
String filename = “d:/access.log_”;
try
{
count(filename);
}
catch (IOException e)
{
System.err.println(e.getMessage());
};
}public static void count(String filename) throws IOException
{
long count=0;
long len;
String strline = “”;
char hit[] = {‘H’,’I’,’T’};//要搜索的字符串
char buff[] = new char[2100];

Reader in = new FileReader(filename);//用FileReader类构造产生一个Reader类对象
LineNumberReader line = null;//生成一个空指针
try
{
line = new LineNumberReader(in);//建立LineNumberReader类对象
while((strline = line.readLine()) != null)
{
//到这里已经读出一行了,用下面的代码分析这行有几个HIT
int i=0,j=0,max=0,flag=0;
buff = strline.toCharArray();//转换成字符数组
max = strline.length();

for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else

{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
}
System.out.println(“Count : “+count);
}
catch (IOException e)
{
System.err.println(e.getMessage());
}
finally
{
try
{
if(in != null) in.close();
}
catch (IOException e)
{
}
}
}
}

候捷先生翻译的宏篇巨著《JAVA编程思想》一书中第67页说到:”使用最原始的JAVA解释器,JAVA大概比C慢上20到50倍”之说法我在阅读的时候就心存疑虑,心想要是这样,JAVA完全没有存或与世间的必要了。在亲自动手试验过后,我觉得说JAVA在J2EE环境下,比C慢上2-3倍还是比较可靠的说法的。况且,目前越来越多的硬件JVM的诞生,也给JAVA越来越多的机会。不过我担心的正是这点,JVM的多厂家多样化很可能会造成某些兼容性方面的问题。例如我见过一篇文章就是讨论某种JAVA程序在IBM-JVM可用而在SUN-JVM上不可用之事例。但愿的,JAVA能健康成长。

总结

事实上,本文有两个基本的意义传递给初做程序员的读者:

一、 抛开你的意识形态好恶,选择最合适的编程语言来完成你的工作。每种流行的语言都有自己存在的意义。

二、 在编程中,有想法就自己做一做,你会得出自己的结论。

至此,你应该明白,前面的所有测试结果其实并不重要,重要的是你了解了这些语言的特质,也许在今后的编程生涯中会因此增加一点点”经验”呢。

后记

本来笔者还打算继续测试一下另外的一种颇为流行的解释语言Python和新贵C#以及在Linux平台完成这些测试,但终究还是被懒惰瓦解了斗志。好在的,Python和Perl比较相似,而C#和JAVA有异曲同工之妙。也可以略略做一点参考。

事实上,本文测试中有一个大大的不公平之处,相信仔细的读者已经发现了:其中C和ASM都是使用缓冲区直读的办法,不管三七二十一就进行判断(最后用指针检查缓冲区边界)。而C++等其他的语言虽然用了非常方便的流按行读出,但是多做了很多事情:每一个字符都要判断其是不是回车换行符,而按行读近来,每次缓冲的也要少很多。因此其他几种语言就大大的吃亏了。不过这并不影响结论性的东西,因为测试本身就说明越方便就效率越低。事情总是要有人做,不是吗?

2003-05-17■作者:阿谭■出处: yesky

只能说明文件的I/O操作是最费时的,其实应该说java和C的效率是有数量级的区别的,C代码中80%以上的时间都花费在I/O上了,C++除了I/O之外在流的处理上也发费了交多的时间,这是本次测试中C++比C慢就慢在这儿,很显然java的运行期消耗已经和I/O处于相当的数量级,因此实际消耗时间是C的3到4倍.perl的运行期原理不是很清楚.

http://blog.csdn.net/worldpharos/article/details/3931483

电商导购过冬:蘑菇街酝酿出售 美丽说转型时尚

电商导购过冬:蘑菇街酝酿出售 美丽说转型时尚

腾讯科技讯 王可心 10月24日消息

一度红火的第三方导购网站正因为阿里巴巴过山车式的态度而呈现出急剧下滑的轨迹。

2011年,以美丽说、蘑菇街为代表的导购网站蜂拥而起。彼时,两者还能频繁出现在阿里巴巴的官方活动中。然而,2012年5月,阿里巴巴集团董事局主席马云在内部讲话时表示不扶持上游导购网站继续做大。此后,蘑菇街们就开始了“被打压”下的生存。据导购行业知情人士透露,阿里巴巴一直在收紧对导购网站开放的API接口。

据悉,前段时间阿里巴巴对蘑菇街等大型导购网站的佣金接口进行了限制,这将直接影响导购网站的营收来源。

不过,阿里巴巴并非要将导购网站“置于死地”。今年9月16日,淘宝网宣布,其导购类分享平台优站将正式向第三方导购网站开放。同时,阿里巴巴设立了5亿美元的基金,用于投资并扶持第三方导购网站等外部合作伙伴。

这笔基金将首先应用在收编蘑菇街。据丁香园技术负责人冯大辉透露,阿里巴巴正在与蘑菇街洽谈收购。腾讯科技从多处投资人士了解到,双方已达成初步意向,但尚未达成最终交易。阿里巴巴集团和蘑菇街双方也未对此事给予否认。

从打压到收购,有业内人士将其称为“打一巴掌,给个甜枣”。“从业务上看,阿里巴巴确实需要蘑菇街带来优质的流量,但又要对其掌握控制权;从估值来看,先打压,可降低蘑菇街的估值。”

如果难以独立发展,阿里巴巴也是蘑菇街较好的归宿。蘑菇街是“阿里系”创业团队,四位联合创始人中的三位(陈琪、岳旭强、李研珠)皆来自阿里巴巴,收购后在文化融合上不存在什么障碍。

打压

自从去年5月份,马云在内部发表“不扶持上游导购网站继续做大,阿里巴巴的流量入口应该是草原而不是森林;不扶持返利类网站”观点后,阿里巴巴对淘宝客政策进行了调整。

今年以来,有导购网站对腾讯科技透露,阿里巴巴对导购网站的政策进一步收紧,限制导购网站对API接口的调用次数,导致无法抓取更多的数据,无法扩大规模。

“就像一个水龙头,以前水龙头里的水是无限使用的,现在受到诸多限制。”上述人士称。

据某大型导购网站相关人士透露,淘宝开放的接口一直在缩减。目前,除了一些最基础的接口,技术类接口如商品评价接口、过往价格接口等都做了很多限制。

最近,淘宝对相关佣金接口进行了限制。有淘宝卖家对腾讯科技透露,之前的淘宝宝贝上架,名称中有“蘑菇街”的内容无法发布成功。

“限制佣金接口,意味着营收来源被切断,这对以佣金为主要收入来源的导购网站是致命的。”上述导购行业人士称。

但阿里巴巴方面表示并未封杀美丽说、蘑菇街,而是邀请其加入优站。今年9月份,淘宝网推出优站,优站是淘宝官方导购平台,由多家第三方导购、达人等提供导购内容,共同汇聚成一个社会化的导购平台。

优站与第三方导购的不同之处在于,优站可把握用户访问这些导购网站的数据。例如,以前,用户访问某导购网站,数据从导购网站,再到淘宝。导购网站加入优站之后,数据则从淘宝,到导购网站,再到淘宝完成交易。

财猫浏览器、好贷网创始人李明顺(微博)认为,淘宝对这些数据拥有控制权,实际上成为了导入“入口的入口”,“这对第三方导购是‘改良式的围剿’。”

据悉,淘宝网与蘑菇街此前曾就优站合作进行洽谈,但蘑菇街并没有入驻。“如果加入优站,需要按照优站的体系来做。而蘑菇街的用户习惯已经形成,如果从优站访问,会有一些不适应。”

收购

对于最近爆出的阿里巴巴欲收购蘑菇街的消息,李明顺认为,“这是一个意料之外,情理之中的事。”

先打压后收购的逻辑是什么?

李明顺认为,阿里巴巴对导购市场的态度是“打一棒子,给点糖吃”,这对于阿里巴巴来说,更有谈判权、控制权,也是压低估值的做法。

传闻称,阿里巴巴收购蘑菇街出资2亿美元。由于最终交易尚未完成,这一数字尚无从考证。不过可以比较的是,这一数字或不及蘑菇街第三轮融资时的估值。

此前,蘑菇街共进行过三轮融资:2011年,蘑菇街进行过两轮共计2000万美元的融资,A轮投资方中有贝塔斯曼,B轮投资方为启明创投。2012年10月,完成C轮融资,IDG领投。有消息称,蘑菇街第三轮融资时估值超过2亿美元。蘑菇街公关负责人徐达也未否认低价收购一事。

另一方面,从业务上来看,阿里巴巴收购蘑菇街可弥补社区产品短板。阿里巴巴对于外部优质流量有两种态度:一是渴求,二是担心不可控。因此,既要获得优质流量,又想可控,最好的办法是入股或收购。”互联网评论人士洪波(微博)说。

从打压到收购,洪波认为,最大的原因在于“压不住”。“对于阿里巴巴集团商户来说,内部流量买不起,有寻求外部廉价流量的需求。阿里巴巴很难真正封的住。”

除了入股新浪微博、收购蘑菇街等外部优质流量,阿里巴巴集团内部也一直在探索社会化产品,如推出类Pinterest产品哇哦、购物分享平台爱逛街、图片互动社区顽兔、兴趣图谱网站圈子等,但流量不及美丽说、蘑菇街等第三方导购站。

洪波认为,阿里巴巴自己难做成社会化产品的原因在于,缺少用户日常的关系,更多的是买卖关系。因此,阿里巴巴自己的社会化平台往往变成营销专家活跃的地方,而不是用户活跃的地方。

整体来看,阿里巴巴仍在加大对社区产品的资源投入力度,淘宝优站、爱淘宝皆在新版淘宝网首页呈现。

突围

经历了辉煌期和低迷期,蘑菇街的发展是导购行业的一个缩影。

导购业态的天花板明显。华平投资顾问黄若在其《我看电商》一书中指出,导购是一个主要依赖于淘宝环境生存的业态,其经营土壤,全仰仗阿里巴巴。“人家只要阀门一关,这个业态就集体下岗。”

在这个大环境下,导购网站正寻求突围。要么转型,要么多平台发展。

据了解,蘑菇街竞争对手美丽说已经淡化导购色彩,定位时尚媒体,做时尚杂志与互联网的结合体。一方面引导潮流,另一方面用户产生内容。除了导购收入,媒体可以带来的是影响力和广告收入。

“美丽说与蘑菇街的性质已经不同了。虽然在外部看来,美丽说还有导购的模式,但不管是内部架构还是未来策略,都在慢慢弱化导购的概念。”一位接近美丽说的人士说。

突围的另一种方式是提升其他电商平台的比例,如加大和京东、易迅、1号店、苏宁等平台的合作。

不过,在淘宝占据中国网购市场70%市场份额、导购需求主要集中在服饰护肤、母婴、家居等垂直领域的背景下,现阶段这一转变并不容易实现。

谷歌推出反网络审查软件uProxy:不支持微软IE

“uProxy”目前还在测试阶段,初期将仅支持谷歌Chrome、火狐Firefox

【TechWeb报道】10月22日消息,据国外媒体报道,谷歌日前在纽约展示了一款名为“uProxy”网络代理软件,该软件旨在帮助用户绕过网络审查,自由访问互联网。

“uProxy”项目由华盛顿大学和非功利组织Brave New Software研发,由谷歌提供资金资助。该软件的工作原理是,通过将两个用户以加密的形式连接起来,构成一个虚拟网络,最终绕开审查。

“uProxy”目前还在测试阶段,初期将仅支持谷歌Chrome、火狐Firefox,不支持微软Internet Explorer。(小峰)

http://www.chinaz.com/news/2013/1022/323139.shtml

微信游戏成功的秘密:经典的产品推进布局

截至九月底,微信“游戏中心”一共推出了5款游戏:经典飞机大战、天天连萌、天天爱消除、节奏大师、天天跑酷。

自从微信推出“经典飞机大战”开始,手游界便遭腾讯横扫。微信的“游戏中心”使得腾讯由一家移动端游戏偏弱的公司,转瞬之间成为中国移动端游戏玩家数量最多(约1亿),收入最高的“手游公司”。经典飞机大战成为“全民游戏”,而最新推出的游戏“天天跑酷”仅用时一天半便在Appstore收入榜上正式超越《我叫MT》成为第一,日入500万元。

微信游戏的成功,并不是简单一句“用户多,再加入点社交”所能概括的,微信游戏的崛起蕴藏着腾讯深邃的产品观,值得探究。微信“游戏中心”推出的五款游戏,本质上可以分为三类,体现出了腾讯细腻的产品逻辑,以及稳健的产品推进大局观。

1.掀起全民游戏:经典飞机大战

其实,从微信推出游戏的名称中,便可以窥探出微信游戏战略推进的蛛丝马迹。经典飞机大战,主打的是“经典”怀旧,飞机大战这个游戏类型脱胎于最早红白机射击游戏《小蜜蜂》,被很多游戏继承过,只要对电子产品有所接触,从60后直到00后,对这个模式游戏都不陌生,这为“打飞机”成为全民游戏打下了基础。

而微信也刻意营造着“打飞机”的话题性,从微信5.0进入页面推荐“打飞机”,到让人遐想的名称,再到先推出iOS版刻意的饥渴营销,使得打飞机”话题性一时无双,人人欲打之而后快。

“话题性”+“经典怀旧”+“社交排名”,在三重力场的挤压之下,几乎所有微信用户都对打飞机这款游戏进行了尝试。不管“打飞机”这款游戏后续会如何,在战略意义上“打飞机”是极其成功的,它本身是作为微信游戏“攻城”之作:让所有人知道微信游戏,让大部分人尝试微信游戏。毫无疑问,“打飞机”出色的完成了任务。

2.碎片化标准手游:天天连萌、天天爱消除、天天跑酷

“天天系列”都是标准的手游类型游戏:易上手、符合碎片时间、轻操作。微信游戏在用“打飞机”制造出足够大的动静之后,需要用这些典型手游去留住“标准的”手游玩家。

手机游戏目前的主要功能还是供人们消遣,打发碎片时间用,远未达到端游或街机高粘性和深度游戏文化水准。微信游戏现在祭出三款“天天系列”手游,是标准的“守势”。“天天连萌”、“天天爱消除”游戏时长被限定在1分钟之内,以及易上手的操作方式,都是普通用手游来打发碎片时间用户的最好选择。一批玩家将被沉淀进入“天天系列”中,天天去玩一玩微信中的手游打发时间。

3.窥探手游的边界之重:节奏大师

“节奏大师”是微信游戏对现代手游形态天花板的探究之作,一探“手游不能承受之重”。“节奏大师”作为一款MUG(音乐游戏),其闯关模式、后期难度,游戏时长(平均2分半钟)、以及操作方式都过于“沉重”,不符合目前手游“用来打发时间”的游戏形态。与“节奏大师”相类似的MUG游戏如“太鼓达人”、“跳舞机”都是街机游戏中的经典,虽然不可能是最受欢迎的游戏,却能黏住一部分核心玩家,形成良好的“玩家圈子”和“游戏文化”,这也是目前手游所欠缺,所需要突破的瓶颈。

节奏大师的“重”,是腾讯期望“突破”手游轻形态的试水之作,但是目前看来,情况并不乐观。例如,有的玩家吐槽微信游戏每周清空排行榜,根本就不利于节奏大师这样的“重型游戏”核心玩家刷存在感,自己“闯了100关的记录就这么没了”——改变手游轻形态并没有那么容易,因为“轻”是手游现在的基本基因,手游设计者潜意识就这么认为,很难突破。

最后

微信游戏在产品布局上的逻辑,是根据现有手游产业产品形态进行三步推进:攻城(打飞机)→守势(天天系列)→试图突围(节奏大师)。这三步棋走得十分稳健并且具备紧密的逻辑性,攻守兼备的同时也让腾讯很好的探究到了手游的边界。目前看来,手游迈向“重型游戏”还有难以突破的藩篱。

关于作者

如果你也关注创业创新领域,那么请关注韦物主义(微信号:weiwuzy)

http://www.chinaz.com/start/2013/0925/319308.shtml

2012高考学弟学妹们,加油!

清明时节

万物美 水呵护

让你成为幸福的女人

执子之手 与子偕老

谢谢你们的陪伴

http://im.qq.com/culture/qqbanner.shtml?ADUIN=0&ADSESSION=0&ADTAG=CLIENT.QQ.3847_LoginLogo.0

猜灯谜 闹元宵

九九重阳节 关爱老人行

祝福祖国62岁生日

中秋节 你和家人团聚了吗

感念师恩 教师节快乐

如果你不明白爱情

【你和一个人越亲密,会越多看到他的疲惫】

  你爱上一个人,因为他脱俗的气质,因为他运筹帷幄的魄力。我们常常像崇拜明星一样,钟情于一个人。那时候觉得他很有力量,似乎能拯救你,能带你进入想要的生活。这种最初的崇拜,却往往会把我们带进阴沟。请注意,无论一个人是花魁、总统、财阀还是行业大拿,只要你成为他亲密的人儿,你会更多看到他不为人知、不善伪装的一面。
  她工作的时候光鲜亮丽,但可能私下里非常邋遢、懒惰,常常疲倦得大脑短路;他看起来魅力十足,是社交大人。但可能回到家就疲惫的只会睡觉;他在圈子里是有名的攻坚人才,但在你的身边却十分软弱,事业上的一点失利都会令他心情烦躁,极易发脾气……如果你一直迷恋着他闪耀的部分,当你发现,他呈现给你的更多是疲惫的话,那你注定失望,而且是彻底的失望。
这种失望摔下来很疼,而且很有杀伤力。所以我们常看到一个男子疯狂追求一个女孩,但在得到后就弃如敝履;一个女孩迷恋一个优秀的男子,拥有他后却发现不是自己要的幸福……所以有人说婚姻是爱情的坟墓,就是因为他们没有意识到这一点——刚谈恋爱时,我们往往会像“打了鸡血”一样,拼上精力去让对方爱上自己,所以那时候表现出的“努力”常会随着两个人关系的稳定而逐渐软下来。这时候,不是他的爱减弱了,而是你们的默契增加了,他不必再强撑着装有精神给你看了——疲惫常常是一个人最安全时,所表达出来的常态,再耀眼的人,也需要暗淡来休憩。如果他对你黯淡,说明他真的信任你,不要再要求他也像对外界时一样“强撑精力”的对你。
  我们都有体验,我们自己也常常感到无力,常常在车上瞌睡,常常不想说话,不是嘛?
  你若只爱他的精彩,那你还不爱他.假若你也尊重他的疲惫,就像尊重自己的。我想你就会获得长久的爱情。走入更精彩的人生,而非落入坟墓。

感念师恩之留守儿童教育

感念师恩之特殊教育

爱在民工子女教育教师

修改WordPress语言包的方法和工具

修改或者制作主题的时候,要做到个性十足、称心如意,就需要对Wordpress的语言包进行调整修改。在主题的目录下面语言包文件夹中我们会找到两个文件:zh_CN.po和zh_CN.mo,其中前者是需要编译的中文语言包,在经过编译后生成的文件就是后者,其中.mo文件才是Wordpress可以直接调用的中文语言包。但是.mo文件不能用记事本类的工具直接修改编辑,这时就要用到po-edit了。
这在我们制作中文主题时必备的工具:

1、找到目录的语言包,位置在(这里以inove主题风格为例):

zh_CN.po的位置: \wp-content\themes\inove\languages
zh_CN.mo的位置:\wp-content\themes\inove

2、下载并安装po-edit软件:

软件官方网站:http://www.poedit.net/
安装以后首先是设定软件界面语言,选择chinese(simplified)即可。

3、从软件中选择打开zh_CN.po文件,然后就可以看到各个条目的信息了,找到你想编辑的修改即可。

4、修改后,点击“另存为”找到合适的地方存储即可(po-edit会自动创建生成zh_CN.mo文件)。

5、将这两个文件分别上传到对应的目录(步骤1中所示)覆盖原文件,然后刷新主页看一下吧。

信阳光山大美女,信阳网络电视台美女记者

信阳光山大美女,信阳网络电视台美女记者
中共信阳市第四次代表大会第三次全体代表大会召开_信阳网络电视台…
2011年9月2日…信阳网络电视台9月2日讯 (记者 程芳) 9月2日上午,中国共产党信阳市第四次代表大会第三次全体会议在百花之声会议中心召开。 大会执行主席王铁、郭…
www.xytv.cn/NEWS/shizhengyaowen/2011/0902 … 2011-9-2 – 百度快照
台湾金门高粱酒落户信阳_信阳网络电视台 信阳电视网 视频 新闻 信…
2011年7月12日…开业盛况 信阳电视网(记者 程芳)讯 7 月 9 日上午,金门高粱 两岸飘香台湾金门高粱酒信阳旗舰店开业典礼在行政路经典花园隆重举行,并正面向县区诚征…
www.xytv.cn/NEWS/shizhengxinwen/2011/0709 … 2011-7-12 – 百度快照
中共信阳市第四次代表大会主席团第六次会议召开_信阳网络电视台 …
2011年9月2日…信阳网络电视台9月2日讯 (记者 程芳) 9月2日上午,中国共产党信阳市第四次代表大会主席团第六次会议在百花之声召开。 主席团成员应到 70 名,因…
www.xytv.cn/NEWS/shizhengyaowen/2011/0902 … 2011-9-2 – 百度快照
更多资料请点击信阳网络电视台
芳芳

wordpress模板各文件函数解析

修改主题时发现好多WordPress主题函数都不了解,网上摘抄了一份放在自己博客上,便于以后好找。
在WordPress中如何按你的意愿显示页面,关键看你是否了解WordPress主题模板页面。这里所说的主题文件是指显示页面的主题文件,而非实现评论、侧边栏等功能的主题文件。大多数用户不使用WordPress安装时自带的默认主题,他们会在互联网上下载免费主题。这是一种设计博客版式的好方法,但不是所有主题开发者都用相同的方式编写主题。主题的表现很大程度上取决于开发者用在主题上的开发时间和对WordPress的了解。
下面我会为大家介绍设计主题页面的所有相关知识,通过这些下面的信息你甚至可以开始为自己设计一个主题。除非你是专家级的主题开发者,否则都可以从这里学到些新的东西。
WordPress如何工作
首先需要了解的是WordPress的模板层级,或者说是“WordPress调用页面的顺序”。 “index.php”是唯一一个所有WordPress主题的PHP文件中都必须具备的文件。“index.php”可以执行WordPress的所有单独功能。
每当有WordPress页面被调用时,WordPress的“引擎”会判断(通过排除法)页面的类型。 这类似于询问“我在哪儿?”。 WordPress回答“我在…类型的页面上”,然后以特定顺序调用页面。 WordPress找不到需要的PHP文件时,会使用“index.php”文件来代替所需文件。 WordPress首先会寻找以下九种基本页面:
首页
如果WordPress判断是在首页上,会先调用“home.php”文件然后再调用“index.php”。
日志页
如果是(单篇)日志页,首先调用“single.php”然后默认调用“index.php”。
“页面”页
如果是静态页面或“页面型”页面(应用了模板的页面),WordPress首先调用“pagetemplate.php”然后默认调用“index.php”。
“分类”页
如果WordPress判断是分类页,则首先调用该类别编号的页面,例如“category-7.php”。找不到相应文件时可以查找“category.php”(category.php可以用于所有类别页)。如果没有“category.php”则继续查找“archive.php”,最后默认调用“index.php”。
标签页
如果WordPress判断是标签页,会首先加载“tag-slug.php”文件,以具体的slug(别名)为标签名。如果标签是“wordpress hacks”,那么标签别名页就是“tag-wordpress-hacks.php”。如果加载不成,WP会继续查找“tag.php”文件,该文件可用于所有标签页,然后调用“archive.php”,最后默认调用 “index.php”。
作者页
博客拥有多个作者时,WP会首先寻找“author.php”文件以显示作者详情。如果没有“author.php”则继续查找“archive.php”,最后默认调用“index.php”。
存档页
WP为之前的日志加载信息页面时,同时也加载了存档页。 WP首先加载“date.php”,其次“archive.php”,最后默认加载“index.php”。
搜索页或404页
若WP判断是在搜索结果页或404(页面未找到)页,会尝试加载search.php或404.php文件。如果无法加载search.php或404.php,WP仍然默认加载“index.php”。
附件页
附件页是所有WordPress主题模板页面中使用次数最少的一种页面类型。 WordPress通常用这些特殊的附件页来加载若干信息,这些信息解释首先查找“image.php”, “audio.php”, “video.php”, 以及“application.php”的原因。然后WP查找“attachment.php”或“single.php”,如果这两个文件不可用,默认查找“index.php”。
WP主题模板内部运行情况
可以用单独的index.php文件来调用以上九种类型的页面,这在上面也提到过。也可以在一些条件标签中编写代码,我在这篇文章的结尾部分会告诉大家如何操作。一个页面中可能含有很多代码,有时甚至有些混乱,这样我们要修改代码来进行设计就不太方便了。
不过凑巧的是,就像WordPress查找九种基本页面一样,每个主题模板页面也包含九种基本的WordPress元素:
调用页眉
开启the loop(主循环)
调用永久链接与(若干)meta
用以通知WordPress应获取的信息的调用
用以获取获取文章内容或摘要的调用
(可能有)更多的meta
关闭the loop(主循环)
调用侧边栏
调用页脚
这是WordPress元素,能让这些元素运行的PHP代码分布在不同的地方,让你的主题版面和平面设计保持正常工作。下面我要详细介绍一下这些元素,以便大家进一步了解如何设计主题模板页面。
调用页眉,侧边栏以及页脚
这三种元素基本类似。 当你在模板中看到以下代码:
<?php get_header(); ?>
表明WordPress打开了“header.php”文件。 get_sidebar() (sidebar.php) 和 get_footer() (footer.php)也是同样的道理。你可能会有很多页眉、页脚和侧边栏,这时可以点击上面的“条件标签”查看相关内容。
开启the loop(主循环)
“Wordpress Loop”会在数据库中持续调用文件,直到WordPress终止调用。 “the loop”的结构随显示页面类型而变,WordPress尝试加载的每个基本类型页面都有一个“loop”。
下面是开启the loop的代码:
<?php if ( have_posts() ) : <?php if ( have_posts() ) : the_post(); ?>
我们可以看到,代码被拆分开来,have_posts用以定义条件标签,while和the_post则各成一部分,但这仍然是the loop,在所有页面中基本都是这样。 多行loop时的一个用法是:用query_posts在“if have_posts”和代码的剩余部分之间放置一个参数,用来显示单篇文章、某一时段的文章、最近一篇文章或者某一类别中的文章,也可以改变the loop中迭代文章的顺序。
调用永久链接与(若干)meta
通过the loop的每次迭代,开放The loop的最后部分(the_post)能够激活元素数据。这里的个体数据通常是指“post meta”,尤其是永久链接(URL)、标题、时间这样的meta。大多数主题会在单篇文章内容前显示一些信息,然后在文章内容后也显示一些信息——比如文章类别和标签。
下面是一些你可以在post meta中调用的内容: the_permalink, the_ID, the_title, the_time, the_author, the_author_email, the_author_posts_link, the_category, single_cat_title, the_tags, single_tag_titls, edit_post_link, comments_popup_link, comments_rss_link
下面是Post meta的代码示例:
<div class=”post” id=”post-<?php the_ID(); ?>”>
<h2><a href=”<?php the_permalink() ?>” rel=”bookmark”><?php the_title(); ?></a></h2>
</div>
用以通知WordPress应获取的信息的调用
之后WordPress会决定所显示的单篇文章内容的详细程度。文章详细程度取决于你的主题使用的是“the_content”(显示全文)或“the_excerpt”(显示摘要)。
(可能有)更多的meta
上面提到过,文章下方都有指定的类别或标签,有时你还可能看到“edit”链接。 一些主题甚至在文章内容后添加了date published meta。
关闭the loop(主循环)
代码如下:
<?php else : ?>
<?php endif; ?>
这是一个多行代码,你可以在其中添加其它信息,例如“Sorry, we didn’t find anything”。你可以在侧边栏之后、调用侧边栏和页脚之前找到“next”“previous”导航链接。
Loops
大多数loops与我在上面所举的例子都差不多,但这并不表示你不能随意修改这些loops。 推荐大家阅读WP Codex上的文章The Loop in Action,文章中列举了存档、类别以及单篇文章以及静态首页中的the loop。
WP Codex上the loop中也有一些在同一页面上放置多个loop的示例。 Perishable Press上有一篇关于多loop,多栏内容的精彩教程。 Perishable Press上还有一些很好的loop模板,以及一篇关于两栏水平序列文章的教程。
(一)WordPress基本模板文件,一套完整的WordPress模板包括如下文件,但是只有index.php和style.css是不能缺少的:
style.css : CSS(样式表)文件,不可缺少版权部分,真正CSS样式表可以放在其他文件;
index.php : 主页模板,不可缺少;
archive.php : Archive/Category模板,如果缺少,默认为index.php的显示;
404.php : Not Found 错误页模板,如果缺少,默认为index.php的显示;
comments.php : 留言/回复模板,不可缺少;
footer.php : Footer模板,可合并到index.php;
header.php : Header模板,可合并到index.php;
sidebar.php : 侧栏模板,可合并到index.php;
page.php : 内容页(Page)模板,如果缺少,默认为index.php的显示;
single.php : 内容页(Post)模板,如果缺少,默认为index.php的显示;
searchform.php : 搜索表单模板,可合并到index.php;
search.php : 搜索结果模板,如果缺少,默认为index.php的显示;
(二)基本条件判断Tag
is_home() : 是否为主页
is_single() : 是否为内容页(Post)
is_page() : 是否为内容页(Page)
is_category() : 是否为Category/Archive页
is_tag() : 是否为Tag存档页
is_date() : 是否为指定日期存档页
is_year() : 是否为指定年份存档页
is_month() : 是否为指定月份存档页
is_day() : 是否为指定日存档页
is_time() : 是否为指定时间存档页
is_archive() : 是否为存档页
is_search() : 是否为搜索结果页
is_404() : 是否为 “HTTP 404: Not Found” 错误页
is_paged() : 主页/Category/Archive页是否以多页显示
(三)Header部分常用到的PHP函数
<?php bloginfo(’name’); ?> : 博客名称(Title)
<?php bloginfo(’stylesheet_url’); ?> : CSS文件路径
<?php bloginfo(’pingback_url’); ?> : PingBack Url
<?php bloginfo(’template_url’); ?> : 模板文件路径
<?php bloginfo(’version’); ?> : WordPress版本
<?php bloginfo(’atom_url’); ?> : Atom Url
<?php bloginfo(’rss2_url’); ?> : RSS 2.o Url
<?php bloginfo(’url’); ?> : 博客 Url
<?php bloginfo(’html_type’); ?> : 博客网页Html类型
<?php bloginfo(’charset’); ?> : 博客网页编码
<?php bloginfo(’description’); ?> : 博客描述
<?php wp_title(); ?> : 特定内容页(Post/Page)的标题
(四)模板常用的PHP函数及命令
<?php get_header(); ?> : 调用Header模板
<?php get_sidebar(); ?> : 调用Sidebar模板
<?php get_footer(); ?> : 调用Footer模板
<?php the_content(); ?> : 显示内容(Post/Page)
<?php if(have_posts()) : ?> : 检查是否存在Post/Page
<?php while(have_posts()) : the_post(); ?> : 如果存在Post/Page则予以显示
<?php endwhile; ?> : While 结束
<?php endif; ?> : If 结束
<?php the_time(’字符串’) ?> : 显示时间,时间格式由“字符串”参数决定,具体参考PHP手册
<?php comments_popup_link(); ?> : 正文中的留言链接。如果使用 comments_popup_script() ,则留言会在新窗口中打开,反之,则在当前窗口打开
<?php the_title(); ?> : 内容页(Post/Page)标题
<?php the_permalink() ?> : 内容页(Post/Page) Url
<?php the_category(’,’) ?> : 特定内容页(Post/Page)所属Category
<?php the_author(); ?> : 作者
<?php the_ID(); ?> : 特定内容页(Post/Page) ID
<?php edit_post_link(); ?> : 如果用户已登录并具有权限,显示编辑链接
<?php get_links_list(); ?> : 显示Blogroll中的链接
<?php comments_template(); ?> : 调用留言/回复模板
<?php wp_list_pages(); ?> : 显示Page列表
<?php wp_list_categories(); ?> : 显示Categories列表
<?php next_post_link(’%link’); ?> : 下一篇文章链接
<?php previous_post_link(’%link’); ?> : 上一篇文章链接
<?php get_calendar(); ?> : 日历
<?php wp_get_archives() ?> : 显示内容存档
<?php posts_nav_link(); ?> : 导航,显示上一篇/下一篇文章链接
<?php include(TEMPLATEPATH . ‘/文件名’); ?> : 嵌入其他文件,可为定制的模板或其他类型文件
(五)与模板相关的其他函数
<?php the_search_query(); ?> 搜索表单的值
<?php _e(’Message’); ?> : 输出相应信息
<?php wp_register(); ?> : 显示注册链接
<?php wp_loginout(); ?> : 显示登录/注销链接
<?php wp_meta(); ?> 显示管理员的相关控制信息(为插件API HOOK用)
<!–next page–> : 将当前内容分页
<!–more–> : 将当前内容截断,以不在主页/目录页显示全部内容
<?php timer_stop(1); ?> : 网页加载时间(秒)
<?php echo get_num_queries(); ?> : 网页加载查询量
显示最新文章
<?php query_posts(’showposts=5′); ?>
<ul>
<?php while (have_posts()) : the_post(); ?>
<li><a href=”<?php the_permalink() ?>”><?php the_title(); ?></a></li>
<?php endwhile;?>
</ul>
显示最新评论
<?php global $wpdb; $sql = “SELECT DISTINCT ID, post_title, post_password, comment_ID, comment_post_ID, comment_author, comment_date_gmt, comment_approved, comment_type,comment_author_url, SUBSTRING(comment_content,1,30) AS com_excerpt FROM $wpdb->comments LEFT OUTER JOIN $wpdb->posts ON ($wpdb->comments.comment_post_ID = $wpdb->posts.ID) WHERE comment_approved = ‘1′ AND comment_type = ” AND post_password = ” ORDER BY comment_date_gmt DESC LIMIT 10″; $comments = $wpdb->get_results($sql); $output = $pre_HTML; $output .= “\n<ul>”; foreach ($comments as $comment) { $output .= “\n<li>”.strip_tags($comment->comment_author) .”:” . “<a href=\”” . get_permalink($comment->ID) . “#comment-” . $comment->comment_ID . “\” title=\”on ” . $comment->post_title . “\”>” . strip_tags($comment->com_excerpt) .”</a></li>”; } $output .= “\n</ul>”; $output .= $post_HTML; echo $output;?>
显示热评文章
<?php $result = $wpdb->get_results(”SELECT comment_count,ID,post_title FROM $wpdb->posts ORDER BY comment_count DESC LIMIT 0 , 10″); foreach ($result as $topten) { $postid = $topten->ID; $title = $topten->post_title; $commentcount = $topten->comment_count; if ($commentcount != 0) { ?>
<li><a href=”<?php echo get_permalink($postid); ?>” title=”<?php echo $title ?>”><?php echo $title ?></a></li>
<?php } } ?>
显示文章分类
<h2>Categories</h2>
<ul><?php wp_list_cats(’sort_column=name’); ?></ul>
显示归档
<h2>Archives</h2>
<ul><?php wp_get_archives(’type=monthly’); ?></ul>
在侧栏显示页面列表
<h2>Pages</h2>
<ul><?php wp_list_pages(’title_li=’); ?></ul>
调用Gravatar(只适应2.5以上)
<?php if(function_exists(’get_avatar’)){ echo get_avatar($comment, ‘50?);} ?>
显示友情链接
<ul><?php get_links_list(); ?></ul>
显示管理员链接
<ul><?php wp_register(); ?>
<li><?php wp_loginout(); ?></li>
<li><a href=”http://www.wordpress.org/”>WordPress</a></li>
<?php wp_meta(); ?>
<li><a href=”http://validator.w3.org/check?uri=referer”>XHTML</a></li>
</ul>
在侧栏显示页面的子页面
<?php$children = wp_list_pages(’title_li=&child_of=’.$post->ID.’&echo=0′);if ($children) { ?>
<ul><?php echo $children; ?></ul>
<?php } ?>
显示Wordpress标签
<?php the_tags(); ?>
显示Wordpress标签云
<?php wp_tag_cloud(’smallest=8&largest=36&’); ?>
模板标题
<?php ?>
动态标题标签
<title><?phpif (is_home()) { echo bloginfo(’name’); } elseif (is_404()) { echo ‘404 Not Found’; } elseif (is_category()) { echo ‘Category:’; wp_title(”); } elseif (is_search()) { echo ‘Search Results’; } elseif ( is_day() || is_month() || is_year() ) { echo ‘Archives:’; wp_title(”); } else { echo wp_title(”); } ?></title>
在独立页面中运行PHP
<?php if ( is_home() ) { include (’file.php’); } ?>
结论
只要掌握了一点这方面的知识,你就可以随意修改任何WordPress主题模板页面了。现在你已经充分了解了WordPress的页面和the loop的运行,就可以征服任何难题了。现在就开始你的博客主题设计之旅吧!

感念师恩 爱在乡村教师

第26届大运会今日开幕

第26届大运会今日开幕

第二期:七夕,我们在一起

第一期:纪念安徒生逝世136年

windows下Mysql解决启动1067和Fatal error: Can’t open and lock privilege tables: Table ‘mysql.host’ doesn’t exist

Can’t open and lock privilege tables: Table ‘mysql.host’ doesn’t exist

2011年02月26日 星期六 01:43

关于手动修改mysql目录下的my.ini的数据库保存路径后,在启动的时候出错。

查看错误日志后,发现Can’t open and lock privilege tables: Table ‘mysql.host’ doesn’t exist

原错误日志内容:

110226 1:20:46 [Note] Plugin ‘FEDERATED’ is disabled.

C:\Program Files\MySQL\MySQL Server 5.1\bin\mysqld: Table ‘mysql.plugin’ doesn’t exist

110226 1:20:46 [ERROR] Can’t open the mysql.plugin table. Please run mysql_upgrade to create it.

InnoDB: The first specified data file D:\mysite\database\ibdata1 did not exist:

InnoDB: a new database to be created!

110226 1:20:47 InnoDB: Setting file D:\mysite\database\ibdata1 size to 10 MB

InnoDB: Database physically writes the file full: wait…

110226 1:20:47 InnoDB: Log file .\ib_logfile0 did not exist: new to be created

InnoDB: Setting log file .\ib_logfile0 size to 10 MB

InnoDB: Database physically writes the file full: wait…

110226 1:20:47 InnoDB: Log file .\ib_logfile1 did not exist: new to be created

InnoDB: Setting log file .\ib_logfile1 size to 10 MB

InnoDB: Database physically writes the file full: wait…

InnoDB: Doublewrite buffer not found: creating new

InnoDB: Doublewrite buffer created

InnoDB: Creating foreign key constraint system tables

InnoDB: Foreign key constraint system tables created

110226 1:20:48 InnoDB: Started; log sequence number 0 0

110226 1:20:48 [ERROR] Fatal error: Can’t open and lock privilege tables: Table ‘mysql.host’ doesn’t exist

从网上查了半天都是linux下的解决方法,没有windows的。不过仔细看了看linux下的解决方法,发现是因为

windows下Mysql解决启动1067和Fatal error: Can't open and lock

这个文件闹得,从日志上来看是因为不能够打开私有权限的表格。

这个文件闹得,从日志上来看是因为不能够打开私有权限的表格。

解决方法直接把这个文件夹全都复制到你要修改的目录下,重启mysql就ok了。

如果你真的长大了,懂得了什么是爱……

如果你真的长大了,懂得了什么是爱,请你一定要看这篇日志,尤其是农村的孩子!!!!看完后请深思!!!

 

你二十出头 喜欢用票子挥霍青春的时代
你,在KTV里唱着虚拟的情与爱…用所谓的“流行”来演绎一场不属于你们的行为艺术…
你,在网吧里打着两块一局的暴力与满足…用游戏里的繁华虚无来诠释你们所追求的虚荣…
你,喝着便宜的大理或者几百的茅台…打着想成为李白的幌子…用喝酒来忘记现实…

你,和那些所谓的朋友通电话,一说就长达很多个10分钟,而却很少往回打电话,即使接到父母的电话也是嫌他们啰嗦,很快就挂掉……

有一个关心你的恋人,你却不懂珍惜,深夜在外玩,打电话给你却嫌有人管束丢面子……

你可以任性,一句没有共同语言,就和朝夕相处的恋人分别,随便找个人结婚,只是一个谈得来……
这就是你…这就是你所谓的青春年华…你可曾考虑过任性冲动造成的结果……
他们…在干涸的土地上烤着如火的太阳…心里着急的是能够让自己的子女过上更好的生活…
他们…在工地上提着沙灰…偶尔被沙子迷了眼睛或者被砖头砸到了脚…

都只会揉揉被灰覆盖了的眼眸与沾满灰色的睫毛…

隔着鞋子揉揉被砸到的脚被然后躬身继续前行…因为他们要为我们这些自私的子女创造一个更好的未来…
他们…在嘈杂的菜市场与别人争着一毛钱的白菜…当然…即使降了价的猪肉他们仍然舍不得买上半斤…
这就是你们的父母…那对可怜得高贵的父母…
每一次,当你在电话里跟父母说自己没有钱用的时候…
你是否听到那头的他们轻轻的一声叹息…当你拿着那些钱在网吧的冷气下打着游戏的时候你是否会想起你的父母还在田里顶着大太阳劳作…当你拿着那些钱在餐馆大快朵颐时…是否会想起他们的餐桌上究竟有几个菜…当你喝着百事娃哈哈时…你是否会想起在田里劳作的他们嘴唇早已干裂…当你带着女朋友逛街时…你是否想过给你的母亲买点什么…当你站在你农村的父母用肩膀为你撑起的的云层上时…你是否注意到你脚下父母的汗水已经流到了眼眶…
是,你现在才是二十出头…或许没有太大的力量做些什么…可是…你们至少做些力所能及的事…少打点游戏…少玩点不该玩的 …少买些衣服…少吃几次烧烤火锅…多想想他们的辛苦…多给他们打几个电话…
那么单纯的他们只需要一点点你散落天涯海角的爱心里就能幸福得开出花来…也许你在想…以后我要多么有成就多么有钱给他们买豪宅买名牌…可是你想过没有…还没等你赚到豪宅的一个角落时…他们…或许就已经不在了…况且…他们要并不是这些…
人愈老愈会感觉孤单…而你就成了他们的世界里唯一的亮光…请不要让这盏灯熄灭…
二十出头,责任!父爱、母爱是世界最伟大、最无私、最…

祝天下父母亲,永远健健康康…