官方文档:https://eggjs.org/zh-cn
登陆插件:https://www.npmjs.com/package/egg-passport
微信登陆相关:https://github.com/liyunde/egg-passport-workweixin
官方文档:https://eggjs.org/zh-cn
登陆插件:https://www.npmjs.com/package/egg-passport
微信登陆相关:https://github.com/liyunde/egg-passport-workweixin
对于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
对于PhysicalNamingStrategyStandardImpl有DefaultNamingStrategy的效果,
对于SpringPhysicalNamingStrategy有ImprovedNamingStrategy的效果。
法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>
安装可参考:https://docs.microsoft.com/zh-cn/sql/linux/sql-server-linux-setup-red-hat
1.下载sql server的源,便于通过yum命令来安装
curl https://packages.microsoft.com/config/rhel/7/mssql-server.repo > /etc/yum.repos.d/mssql-server.repo
2.安装
yum install -y mssql-server
3.配置
sqlservr-setup 或 mssql-conf setup
安装客户端工具
可以参考:https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools
1.设置防火墙
要连接数据库,首先要打开防火墙上1433端口,也就是,增加tcp端口1433到公共区域,并且永久生效
2.下载客户端工具的源
curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/msprod.repo
3.安装客户端工具
yum install mssql-tools unixODBC-devel
4.连接sql sever
sqlcmd -S localhost -U sa
you must configure in a separate build directory
必须在单独的生成目录中配置
当编译glibc 2.14 时候会出现上面的问题
只要新建一个文件夹,使用绝对路径编译就可以了
好了歌,中国著名古典章回体小说《红楼梦》中经典诗词,小说中为跛足道人所做,甄士隐彻悟后进行进一步注解,表现了作者现实主义和宗教思想。文中还有与之相和的《好了歌注》,承接并引申了《好了歌》的思想。诗歌内容隐射小说情节,表达了作者对现实的愤懑和失望,以及对自由的追求和向往!

能有一项长期爱好,看电影或者听歌收集旧书都行。这些爱好说白了就是让无聊的日子看起来有那么一点意义。
不要担心长时间不读书,你刷的微博,是在刷新信息量。看的电影也能充斥知识量。
看书看电影无须在意评论家说什么,有你喜欢的作家或演员就行。
过了三十岁,不管在什么地方吵架,都挺二的,尤其在网上。
五毛从来不值一提,更不配顺口一骂。
无论是谁,一有空就随便抓个人骂来骂去的,都不是什么好人。
别轻易贬低别人,同一个世界,同一个泥潭,无论黑衬衫白衬衫都带着污渍。
接下来说点小事 :))
不要占用别人车位,临时占用要留电话,否则车被划了轮胎被扎了都不好意思嚷嚷。
记得排队,记得上扶梯站右边,记得打车时别抢到别人前面,记得让自己方便的时候别给别人制造不快。
飞机没停稳何苦打开行李舱拿着背包就往外冲呢?我们不是最讨厌拥挤吗?
既然你改变不了什么,就改改自己的暴脾气,顺便去做一个正常人,当正常人越来越多,活着就没那么纠结,就会舒服一些,这就是改变。
别人给的伤害不能让你变强大,但你要学会躲避他们,不是说你惹不起,是你浪费不起那时间。把时间浪费在那种人身上,与自残无异。
关于朋友这件事,合得来就多加珍惜,不合拍就别浪费精力浪费时间辛苦维持了。
说到爱情,别为此寻死觅活,也别要求对方爱自己爱一辈子,那就是一段经历而已。
别问朋友借钱,除非是治病救人。欠钱到期还不了,要提前说,别羞于启齿错过时间等朋友问起时再支支吾吾闹得彼此都不痛快。
过去会越来越多,未来会越来越少,直到没有。当过去变成记忆,就变成身体的一部分,你忘不掉的。
当你感到自己真笨,这也干不好,那也做不好的时候,你已经是聪明人了。
不要担心这个致癌,那个致癌,这个不吃那个不吃,放心,我们最后都要死于癌症。
别人让你点菜,不会点就老老实实说不会点,别一张嘴就出来俩字:随便。
只要活着就总有那么一段时间,充满灰暗,躁动不安,可是除了面对,好像也没有别的办法。
当你感到时光漫长,实际上已经没有多少时间了。
老人倒了要扶,当年的坏人变老了,但不是所有的老人都变坏了。相信我,只有看热闹的越来越少,我们的下半生才有希望。
你会臃肿,你会白头,你将来也会倒在路边,但你不会去诬赖那个扶你的人。
你不会,我相信。
最后,
移民不是真正的理想,真正的理想是留在这个国家,管好自己并力所能及的帮助他人,以及,参与它的每一次改变。
来源:http://www.myexception.cn/other/1768786.html
Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配置就能够实现分布式服务调用,也就是说服务提供方(Provider)发布的服务可以天然就是集群服务,比如,在实时性要求很高的应用场景下,可能希望来自消费方(Consumer)的调用响应时间最短,只需要选择Dubbo的Forking Cluster模式配置,就可以对一个调用请求并行发送到多台对等的提供方(Provider)服务所在的节点上,只选择最快一个返回响应的,然后将调用结果返回给服务消费方(Consumer),显然这种方式是以冗余服务为基础的,需要消耗更多的资源,但是能够满足高实时应用的需求。
有关Dubbo服务框架的简单使用,可以参考我的其他两篇文章(《基于Dubbo的Hessian协议实现远程调用》,《Dubbo实现RPC调用使用入门》,后面参考链接中已给出链接),这里主要围绕Dubbo分布式服务相关配置的使用来说明与实践。
Dubbo服务集群容错
假设我们使用的是单机模式的Dubbo服务,如果在服务提供方(Provider)发布服务以后,服务消费方(Consumer)发出一次调用请求,恰好这次由于网络问题调用失败,那么我们可以配置服务消费方重试策略,可能消费方第二次重试调用是成功的(重试策略只需要配置即可,重试过程是透明的);但是,如果服务提供方发布服务所在的节点发生故障,那么消费方再怎么重试调用都是失败的,所以我们需要采用集群容错模式,这样如果单个服务节点因故障无法提供服务,还可以根据配置的集群容错模式,调用其他可用的服务节点,这就提高了服务的可用性。
首先,根据Dubbo文档,我们引用文档提供的一个架构图以及各组件关系说明,如下所示:

上述各个组件之间的关系(引自Dubbo文档)说明如下:
我们也简单说明目前Dubbo支持的集群容错模式,每种模式适应特定的应用场景,可以根据实际需要进行选择。Dubbo内置支持如下6种集群模式:
配置值为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。这种模式称为快速失败模式,调用只执行一次,失败则立即报错。这种模式适用于非幂等性操作,每次调用的副作用是不同的,如写操作,比如交易系统我们要下订单,如果一次失败就应该让它失败,通常由服务消费方控制是否重新发起下订单操作请求(另一个新的订单)。
配置值为failsafe。失败安全模式,如果调用失败, 则直接忽略失败的调用,而是要记录下失败的调用到日志文件,以便后续审计。
配置值为failback。失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
配置值为forking。并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
配置值为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种负载均衡策略,如下所示:
在实际使用中,只需要选择合适的负载均衡策略值,配置即可,下面是上述四种负载均衡策略配置的示例:
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
此地址使用了一个通常用于网络浏览以外的端口。出于安全原因,Firefox 取消了该请求
FirFox打开80以外的端口,会弹出以下提示:
“此地址使用了一个通常用于网络浏览以外的端口。出于安全原因,Firefox 取消了该请求。”。
其实是使用了不安全的端口,解决方法如下:
1.在Firefox地址栏输入about:config
2.在右键新建一个字符串键 network.security.ports.banned.override
将需访问网站的端口号添加到,值就是那个端口号即可。
如有多个,就半角逗号隔开,例:81,88,98
在能保证安全的前提下,还简化成这样写1025-65535。
这样,就可以浏览指定范围任意端口的网站了。
chrome 参考:http://my.liyunde.com/chrome-err_unsafe_port/
1、问题描述:
Update Struts dependencies to 2.5.
Remove the following plugin dependencies because they were dropped and aren’t supported anymore.
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.
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" |
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"/> |
The <s:div> tag was dropped.
Replace <s:div> with plain HTML <div> tag.
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.
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:
<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:
<!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.
<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.
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语言提供了八种基本类型。六种数字类型(四个整数型(默认是int 型),两个浮点型(默认是double 型)),一种字符类型,还有一种布尔型。
byte:
short:
int:
long:
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,不过我们无法直接对它们进行操作。
常量就是一个固定值。它们不需要计算,直接代表相应的值。
常量指不能改变的量。 在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) |
参考:https://issues.apache.org/jira/browse/LOG4J2-411
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:
<?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>
WordPress更新或是上传插件或主题错误时出现“无法安装这个包。PCLZIP_ERR_MISSING_FILE (-4) : Missing archive file”错误在某些配置不够完善的主机中可能会出现这种情况。这是因为空间中temp目录没有设置访问权限的问题,需要空间商为你设置目录访问权限,一般这种要求他们是不会理的,所以我们只能改变WordPress的上传临时目录。
解决方法如下:
1. 通过网站FTP打开WordPress根目录下的 wp-config.php 文件,找到如下代码:
/** WordPress 目录的绝对路径。 */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
2. 在下面增加如下代码即可:
/** 指定WordPress的临时目录 */
define('WP_TEMP_DIR', ABSPATH . 'wp-content/temp');
3. 最后在 /wp-content/ 文件夹下新建个一个名为 temp 的文件夹,然后再重新上传或者更新下载插件和主题就可以了。
最好叫 temp 权限和系统中一致为777
BNF
巴科斯范式(BNF: Backus-Naur Form 的缩写)是由 John Backus 和 Peter Naur 首先引入的用来描述计算机语言语法的符号集。现在,几乎每一位新编程语言书籍的作者都使用巴科斯范式来定义编程语言的语法规则。
在BNF中,双引号中的字(“word”)代表着这些字符本身。而double_quote用来代表双引号。
在双引号外的字(有可能有下划线)代表着语法部分。
< > : 内包含的为必选项。
[ ] : 内包含的为可选项。
{ } : 内包含的为可重复0至无数次的项。
| : 表示在其左右两边任选一项,相当于”OR”的意思。
::= : 是“被定义为”的意思
“…” : 术语符号
[…] : 选项,最多出现一次
{…} : 重复项,任意次数,包括 0 次
(…) : 分组
| : 并列选项,只能选一个
斜体字: 参数,在其它地方有解释
下面是是用BNF来定义的Java语言中的For语句的实例:
FOR_STATEMENT ::=
"for" "(" ( variable_declaration |
( expression ";" ) | ";" )
[ expression ] ";"
[ expression ] ";"
")" statement
ABNF
RFC2234 定义了扩展的巴科斯范式(ABNF)。近年来在Internet的定义中 ABNF 被广泛使用。ABNF 做了更多的改进。扩充巴科斯-瑙尔范式(ABNF)基于了巴科斯-瑙尔范式(BNF),但由它自己的语法和推导规则构成。这种元语言的发起原则是描述作为通信协议(双向规范)的语言的形式系统。它建档于 RFC 4234 中通常充当 IETF 通信协议的定义语言。
ABNF 规定是一组推导规则,写为:
规则 = 定义 ; 注释 CR LF
这里的规则是大小写敏感的非终止符,定义由定义这个规则的符号序列,一个文档注释组成,并结束于回车换行。
规则名字是大小写不敏感的: <rulename>, <Rulename>, <RULENAME> 和 <rUlENamE> 都提及同一个规则。规则名字由开始于一个字母的字母、数字和连字符组成。不要求用尖括号(“<”, “>”) (如 BNF 那样)包围规则名字。但是它们可以用来界定规则名字,比如在冗文中识别出规则名字的时候。ABNF 使用 7-位 ASCII 编码,在 8-位域中把高位置零。
终结符由一个或多个数值字符指定。数值字符可以指定为跟随着基数(b = 二进制, d = 十进制, x = 十六进制)的一个百分号“%”,随后是这个数值,或数值的串联(用“.” 来指示)。例如回车可以指定为十进制的 %d13 或十六进制的 %x0D。回车换行可以指定为 %d13.10。
文字正文通过使用包围在引号(")中字符串来指定。这些字符串是大小写不敏感的,使用的字符集是 US-ASCII。所以字符串“abc”将匹配“abc”, “Abc”, “aBc”, “abC”, “ABc”, “AbC”, “aBC” 和 “ABC”。对于大小写敏感匹配,必须定义明确的字符: 要匹配 “aBc” 定义将是 %d97 %d66 %d99。
操作符
空白被用来分隔定义的各个元素: 要使空格被识别为分割符则必须明确的包含它。
串联
规则1 规则2
规则可以通过列出一序列的规则名字来定义。
要匹配字符串“aba”可以使用下列规则:
fu = %x61; a bar = %x62; b mumble = fu bar fu
选择
规则1 / 规则2
规则可以通过用反斜杠(“/”)分隔的多选一规则来定义。
要接受规则 <fu> 或规则 <bar> 可构造如下规则:
fubar = fu / bar
递增选择
规则1 =/ 规则2
可以通过使用在规则名字和定义之间的“=/”来向一个规则增加补充选择。
规则
ruleset = alt1 / alt2 / alt3 / alt4 / alt5
等价于
ruleset = alt1 / alt2 ruleset =/ alt3 ruleset =/ alt4 / alt5
值范围
%c##-##
数值范围可以通过使用连字符(“-”)来指定。
规则
OCTAL = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7"
等价于
OCTAL = %x30-37
序列分组
(规则1 规则2)
元素可以放置在圆括号中来组合定义中的规则。
要匹配“elem fubar snafu”或“elem tarfu snafu”可以构造下列规则:
group = elem (fubar / tarfu) snafu
要匹配“elem fubar”或“tarfu snafu”可以构造下列规则:
group = elem fubar / tarfu snafu group = (elem fubar) / (tarfu snafu)
可变重复
n*n规则
要指示一个元素的重复可以使用形式 <a>*<b> 元素。可选的 <a> 给出要包括的元素的最小数目,缺省为 0。可选的 <b> 给出要包括的元素的最大数目,缺省为无穷。
对零或多个元素使用 *元素,对一或多个元素使用 1*元素,对二或三个元素使用 2*3元素。
特定重复
n规则
要指示明确数目的元素可使用形式 <a> 元素,它等价于 <a>*<a>元素。
使用 2DIGIT 得到两个数字,使用 3DIGIT 得到三个数字。(DIGIT 在下面的核心规则中定义)。
可选序列
[规则]
要指示可选元素下列构造是等价的:
[fubar snafu] *1(fubar snafu) 0*1(fubar snafu)
注释
; 注释
分号(“;”)开始一个注释并持续到此行的结束。
操作符优先级
上述操作符有从最紧绑定(binding)到最松绑定的给定优先级:
与串联一起使用选择操作符可以造成混淆,建议使用分组来做明确串联分组。
核心规则
核心规则定义于 ABNF 标准中。
| 规则 | 形式定义 | 意义 |
|---|---|---|
| ALPHA | %x41-5A / %x61-7A | 大写和小写 ASCII 字母 (A-Z a-z) |
| DIGIT | %x30-39 | 数字 (0-9) |
| HEXDIG | DIGIT / “A” / “B” / “C” / “D” / “E” / “F” | 十六进制数字 (0-9 A-F a-f) |
| DQUOTE | %x22 | 双引号 |
| SP | %x20 | 空格 |
| HTAB | %x09 | 水平tab |
| WSP | SP / HTAB | 空格和水平tab |
| LWSP | *(WSP / CRLF WSP) | 线性空白(晚于换行) |
| VCHAR | %x21-7E | 可见(打印)字符 |
| CHAR | %x01-7F | 任何 7-位 US-ASCII 字符,不包括 NUL |
| OCTET | %x00-FF | 8 位数据 |
| CTL | %x00-1F / %x7F | 控制字符 |
| CR | %x0D | 回车 |
| LF | %x0A | 换行 |
| CRLF | CR LF | 互联网标准换行 |
| BIT | “0” / “1” |
例子
在巴科斯范式(BNF)条目中的邮政地址的例子可以被指定为:
postal-address = name-part street zip-part
name-part = *(personal-part SP) last-name [SP suffix] CRLF
name-part = / personal-part CRLF
personal-part = first-name / (initial ".")
first-name = *ALPHA
initial = ALPHA
last-name = *ALPHA
suffix = ("Jr." / "Sr." / 1*("I" / "V" / "X"))
street = [apt SP] house-num SP street-name CRLF
apt = 1*4DIGIT
house-num = 1*8(DIGIT / ALPHA)
street-name = 1*VCHAR
zip-part = town-name "," SP state 1*2SP zip-code CRLF
town-name = 1*(ALPHA / SP)
state = 2ALPHA
zip-code = 5DIGIT ["-" 4DIGIT]
引用
参考
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包下增加了下面四个异步通道:
其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。
BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。
先来个例子理解一下概念,以银行取款为例:
Java对BIO、NIO、AIO的支持:
BIO、NIO、AIO适用场景分析:
另外,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模型。
本文转自:云栖社区云
【摘要】对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的完整列表可以在这里找到。
| 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 类型):
可能会存在一种特殊情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上 “_” 以显式表达意图,比如 “findByUser_AddressZip()” 或者 “findByUserAddress_Zip()”。
在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:
在实际的项目开发中(使用Git版本控制),在所难免会遇到没有切换分支开发、需要在另一个分支修改bug然后合并到当前分支的情况。之前遇到这种第一反应就是将分支合并过去来解决问题。如果你那些提交当中也穿插了其他人的提交而且他们的提交不可以合并到另一个分支,那么使用分支的合并将明显变得困难。下面分享给大家一个非常好用Git的命令Cherry-Pick来处理这些情况,从而提高开发的效率。
git Cherry-Pick 命令可以选择某一个分支中的一个或几个commit(s)来进行操作。你可以理解merge的个性定制版本
多次使用时要按提交的顺序进行合并,不然会导致某些文件发生冲突。这也是 容易 踩的坑。
今天Go 团队很高兴地宣布Go 1.8发布了。现已提供下载。整个标准库有了显著的性能提升和变化。该版本主要的更新内容如下:
sort.Slice(s,func(i,j int)bool {return s [i] .Name <s [j] .Name})
更多新版本的添加、改进和修复内容,以及上面列出的改进的详细信息请查看Go 1.8发行说明。
为了庆祝发布,世界各地的Go用户组都在本周举办发布会,这已经成为Go社区的一个传统,所以如果你错过了这一次,那么请在 GO 1.9 发布前留意。
via:https://blog.golang.org/go1.8