you must configure in a separate build directory
必须在单独的生成目录中配置
当编译glibc 2.14 时候会出现上面的问题
只要新建一个文件夹,使用绝对路径编译就可以了
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 implements ChatRoomOnlineUserCounterService { |
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 |
< bean class = "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 = new ClassPathXmlApplicationContext(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 = new ClassPathXmlApplicationContext( "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 |
< transformer implementation = "org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> |
19 |
< transformer implementation = "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
来源: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的一部分,因此该功能可能随时被修改。
原创地址:https://my.oschina.net/u/3039671/blog/836750
就像我们在自己电脑上搭建了一个论坛的网站,应用程序(例如Apache服务器)、数据库等都部署在我们自己的电脑上的。就可以正常运行了。
我们的论坛越来越受欢迎,用户越来越多,论坛也十分越活。但是面临的问题是数据库中的信息越来越多,存储不够了。这个时候我们又多弄了几台服务器,应用程序(Apache服务器)、数据库和保存用户上传的文件(图片)单独部署在不同的服务器上。
应用服务器处理大量的业务逻辑,所以需要更好的CPU
数据库服务器需要完成数据的快速查询,所以需要更大的硬盘和内存
文件服务器保存用户上传的图片等文件,所以需要更大的硬盘
我们的论坛用户继续快速增涨,我们发现访问速度越来越慢,原因就是很多请求都要访问数据库(例如,读取用户的个人信息,打个不恰当的比喻,每次进入一个话题,该话题中的每一个发言用户的信息都要从数据库中读取)。这个时候如果我们能缓存这些用户信息,每次从缓存中读取,这样对数据库的压力会大大降低,并且读取的性能也提升了很多。
使用缓存后,又出现问题了,在论坛使用高峰的时候,单一应用服务器处理请求连接有限,这个时候就需要部署应用服务器集群,然后在使用一个负载均衡服务器(例如Nginx,apache Server)。
用户继续增加,使用缓存后,虽然大部分读的操作都不会直接访问数据库,但是还是有一些读操作(缓存未命中,缓存过期)和全部的写操作还是必须操作数据库。当用户达到一定规模,数据库又成了系统的性能瓶颈。
在原来单一数据库的模式上,设置一个主数据库和从数据库,写操作的时候写入主数据库,然后从主数据库同步到从数据库中。读操作就在从数据库中读。当然我们需要一个数据访问的模块来处理这些逻辑
论坛用户反应,打开你们的论坛速度太慢了,再不改善我就不用了。
原因也很简单,一个访问请求中,也许存在很多静态资源(CSS,图片)等,又或者用户的使用的联通,我们的服务器在电信。适应反向代理和CDN技术可以大大改善用户请求的响应速度
虽然数据库进行读写分离以后,但是在我们论坛疯狂增涨下,任何强大的单一服务器的性能都是有限的,只有使用分布式系统,才能在业务不断增涨进行横向扩展。这个是我们最后手段了,使用之前应该先考虑能否根据业务不同来拆分数据库。例如我们论坛的包括了不同主题(汽车、房子、以及你懂的话题),如果按照这些主题来区分数据库,也是好的选择。(注意这个虽然也是要使用多个数据库,但和分布式数据库的概念是有很大区别的)
论坛中,要搜索一些帖子,如果每次进行数据库查询,在数据量十分大的情况,显然是不可取的。还有就是对数据存储的要求,需要使用NoSql。
为了以后以后的发展,我们的业务需要扩展,我们需要增加即时通讯的业务(类似微信),知识问答(类似知乎)。但是这些业务是分开开发的,如果和原有的论坛业务耦合在一起,在代码发布的时候,就会十分麻烦。这个时候,根据不同的业务,分别进行部署和发布。
虽然按照业务进行拆分以后,虽然不同业务之间的管理隔离开来,但是问题又出现了,但我们部署了上万台服务器的时候,每个服务器都保持有与数据库的连接,这样会导致数据集的连接资源不够。而且还有个问题就是对一些基础功能每个业务之间有重复开发的问题。
所以,我们把一些基础业务功能提取出来,做成基础服务独立部署(登录服务、用户信息管理,日志功能等)。这样上层只用关系自己的业务逻辑,调用底层统一的基础服务。
在发布最后一个维护版本更新之后,Linux 稳定版内核维护者 Greg Kroah-Hartman 宣布 Linux Kernel 3.18 分支走到了生命的尽头。而 3.18 LTS 原计划于今年 1 月终止支持。
Linux Kernel 3.18.48 LTS 是该分支的最后版本,根据短日志显示该版本共计调整了 50 个文件,插入 159 处删除 351 处。升级网络堆栈的同时改善了 Bluetooth, Bridge, IPv4, IPv6, CAIF 和 Netfilter,并升级了 USB, SCSI, ATA, media, GPU, ATM, HID, MTD, SPI 和网络(有线和无线)驱动。新发布的 3.18.48 还修正了 3.18.47 和 3.18.27 中的一个 bug。
LTS 版通常会提供大约两年的支持时间,3.18 是在 2014 年 12 月发布的。如果你当前还在使用该内核分支,那么现在你应该升级至更新的 LTS 版本,例如 Linux Kernel 4.9 或者 4.4,这两个版本要比 3.18 更加的安全和强悍。不过 Linux Kernel 3.18 主要被 Google 和其他供应商应用于一些 Android 设备、部分 Chromebook 上,Kroah-Hartman 建议用户拒绝购买仍然使用 3.18 LTS 的供应商的设备。如果无法升级内核开发者也提供了一些建议。
“如果你在使用 Linux Kernel 3.18 中有困难,那么我可以给你提供一些帮助。首先,你需要和硬件供应商反馈,要求尽快升级否则不再购买他们的产品。如果供应商还是不升级,请致信我们让我们出面和厂商进行沟通,出现这个问题的肯定不止你一个人。”
来源:http://www.oschina.net/news/81799/linux-kernel-3-18-48-released