月度归档:2018年08月

消息队列常见问题

1、消息异常处理

1、1 处理机制:死信队列和异常消息队

死信队列:由于某种原因无法传递的消息都放置在死信队列上,主要触发点(MCA 如果远程消息不能交付,MCA 发送的消息不能进行数据转换,Trigger Monitor 出发消息失败)。

回退队列:在外部应用在消费队列中消息时,如果发生异常,会发送回滚命令,回滚后的消息始终会放在队列的顶部,不断被处理和回滚,导致队陷入死循环状态,为了解决这个问题,MQ提供一种机制,选中队列右键--》属性--》存储器,设置回退队列和阀值,如设置队列为q1,阀值为2,则在rollback两次后将消息转入q1。

两者都为消息的可靠传输提供一种机制来处理异常。

两者的区别:

1. 起作用的阶段,死信队列主要在发送阶段,回退队列主要在消息的消费阶段生成。

2. 死信队列由系统触发, 回退队列主要由应用发送rollback触发。

3. 死信队列一个队列管理器只有一个,而回退队列可以为每个队列设置一个。

1.2处理手段:

首先要根据消息的类型,然后再根据是死信和回退队列进行处理。

a) 针对死信队列,需要分析具体原因,重新发起,最后做成自动恢复

b)针对回退队列,如果是能容忍部分丢弃的消息,那么将对应的回退队列的信息持久化即可;如果不能容忍丢失,那么需要人工介入处理。

从最终用户的角度来说,系统应该做到不感知到死信,针对回退的信息,人工介入进行处理,处理的过程中,是否不允许进行同样的业务,需要根据业务的需求来做。

 

当正常的消息到了业务队列后,消费者监听这个队列进行处理,在消费者处理的过程中,难免会有一些消息处理失败,因为业务的种种原因,但是这些消息一旦失败,那么就会影响性能和后面的消息的消费,这时候就需要一个死信队列,来存放这个消费不了的消息,进入死信队列后,在进行其他处理.
消息消费失败处理方式:
一 进入死信队列(进入死信的三种方式)
1.消息被拒绝(basic.reject or basic.nack)并且requeue=false
2.消息TTL过期
3.队列达到最大长度

工作的项目中使用了消息队列,需要注意几个关键问题:

  • 消息的顺序问题
  • 消息的重复问题
  • 事务消息

2、顺序消息

消息有序指的是可以按照消息的发送顺序来消费。例如:一笔订单产生了 3 条消息,分别是订单创建、订单付款、订单完成。消费时,要按照顺序依次消费才有意义。与此同时多笔订单之间又是可以并行消费的。首先来看如下示例:

假如生产者产生了2条消息:M1、M2,要保证这两条消息的顺序,应该怎样做?你脑中想到的可能是这样:

假定M1发送到S1,M2发送到S2,如果要保证M1先于M2被消费,那么需要M1到达消费端被消费后,通知S2,然后S2再将M2发送到消费端。

这个模型存在的问题是,如果M1和M2分别发送到两台Server上,就不能保证M1先达到MQ集群,也不能保证M1被先消费。换个角度看,如果M2先于M1达到MQ集群,甚至M2被消费后,M1才达到消费端,这时消息也就乱序了,说明以上模型是不能保证消息的顺序的。如何才能在MQ集群保证消息的顺序?一种简单的方式就是将M1、M2发送到同一个Server上:

这样可以保证M1先于M2到达MQServer(生产者等待M1发送成功后再发送M2),根据先达到先被消费的原则,M1会先于M2被消费,这样就保证了消息的顺序。

这个模型也仅仅是理论上可以保证消息的顺序,在实际场景中可能会遇到下面的问题:

网络延迟问题

只要将消息从一台服务器发往另一台服务器,就会存在网络延迟问题。如上图所示,如果发送M1耗时大于发送M2的耗时,那么M2就仍将被先消费,仍然不能保证消息的顺序。即使M1和M2同时到达消费端,由于不清楚消费端1和消费端2的负载情况,仍然有可能出现M2先于M1被消费的情况。

那如何解决这个问题?将M1和M2发往同一个消费者,且发送M1后,需要消费端响应成功后才能发送M2。

聪明的你可能已经想到另外的问题:如果M1被发送到消费端后,消费端1没有响应,那是继续发送M2呢,还是重新发送M1?一般为了保证消息一定被消费,肯定会选择重发M1到另外一个消费端2,就如下图所示。

保证消息顺序的正确姿势

这样的模型就严格保证消息的顺序,细心的你仍然会发现问题,消费端1没有响应Server时有两种情况,一种是M1确实没有到达(数据在网络传送中丢失),另外一种消费端已经消费M1且已经发送响应消息,只是MQ Server端没有收到。如果是第二种情况,重发M1,就会造成M1被重复消费。也就引入了我们要说的第二个问题,消息重复问题,这个后文会详细讲解。

回过头来看消息顺序问题,严格的顺序消息非常容易理解,也可以通过文中所描述的方式来简单处理。总结起来,要实现严格的顺序消息,简单且可行的办法就是:

保证生产者 - MQServer - 消费者是一对一对一的关系

这样的设计虽然简单易行,但也会存在一些很严重的问题,比如:

  1. 并行度就会成为消息系统的瓶颈(吞吐量不够)
  2. 更多的异常处理,比如:只要消费端出现问题,就会导致整个处理流程阻塞,我们不得不花费更多的精力来解决阻塞的问题。

但我们的最终目标是要集群的高容错性和高吞吐量。这似乎是一对不可调和的矛盾,那么阿里是如何解决的?

世界上解决一个计算机问题最简单的方法:“恰好”不需要解决它!—— 沈询

有些问题,看起来很重要,但实际上我们可以通过合理的设计或者将问题分解来规避。如果硬要把时间花在解决问题本身,实际上不仅效率低下,而且也是一种浪费。从这个角度来看消息的顺序问题,我们可以得出两个结论:

  1. 不关注乱序的应用实际大量存在
  2. 队列无序并不意味着消息无序

所以从业务层面来保证消息的顺序而不仅仅是依赖于消息系统,是不是我们应该寻求的一种更合理的方式?

最后我们从源码角度分析RocketMQ怎么实现发送顺序消息。

RocketMQ通过轮询所有队列的方式来确定消息被发送到哪一个队列(负载均衡策略)。比如下面的示例中,订单号相同的消息会被先后发送到同一个队列中:
// RocketMQ通过MessageQueueSelector中实现的算法来确定消息发送到哪一个队列上
// RocketMQ默认提供了两种MessageQueueSelector实现:随机/Hash
// 当然你可以根据业务实现自己的MessageQueueSelector来决定消息按照何种策略发送到消息队列中
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Integer id = (Integer) arg;
int index = id % mqs.size();
return mqs.get(index);
}
}, orderId);
在获取到路由信息以后,会根据MessageQueueSelector实现的算法来选择一个队列,同一个OrderId获取到的肯定是同一个队列。
private SendResult send() {
// 获取topic路由信息
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
if (topicPublishInfo != null && topicPublishInfo.ok()) {
MessageQueue mq = null;
// 根据我们的算法,选择一个发送队列
// 这里的arg = orderId
mq = selector.select(topicPublishInfo.getMessageQueueList(), msg, arg);
if (mq != null) {
return this.sendKernelImpl(msg, mq, communicationMode, sendCallback, timeout);
}
}
}

3、消息重复

上面在解决消息顺序问题时,引入了一个新的问题,就是消息重复。那么RocketMQ是怎样解决消息重复的问题呢?还是“恰好”不解决。

造成消息重复的根本原因是:网络不可达。只要通过网络交换数据,就无法避免这个问题。所以解决这个问题的办法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?

  1. 消费端处理消息的业务逻辑保持幂等性
  2. 保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现

第1条很好理解,只要保持幂等性,不管来多少条重复消息,最后处理的结果都一样。第2条原理就是利用一张日志表来记录已经处理成功的消息的ID,如果新到的消息ID已经在日志表中,那么就不再处理这条消息。

第1条解决方案,很明显应该在消费端实现,不属于消息系统要实现的功能。第2条可以消息系统实现,也可以业务端实现。正常情况下出现重复消息的概率其实很小,如果由消息系统来实现的话,肯定会对消息系统的吞吐量和高可用有影响,所以最好还是由业务端自己处理消息重复的问题,这也是RocketMQ不解决消息重复的问题的原因。

RocketMQ不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重。

4、事务消息

RocketMQ除了支持普通消息,顺序消息,另外还支持事务消息。首先讨论一下什么是事务消息以及支持事务消息的必要性。我们以一个转帐的场景为例来说明这个问题:Bob向Smith转账100块。

在单机环境下,执行事务的情况,大概是下面这个样子:

单机环境下转账事务示意图

当用户增长到一定程度,Bob和Smith的账户及余额信息已经不在同一台服务器上了,那么上面的流程就变成了这样:

集群环境下转账事务示意图

这时候你会发现,同样是一个转账的业务,在集群环境下,耗时居然成倍的增长,这显然是不能够接受的。那如何来规避这个问题?

大事务 = 小事务 + 异步

将大事务拆分成多个小事务异步执行。这样基本上能够将跨机事务的执行效率优化到与单机一致。转账的事务就可以分解成如下两个小事务:

小事务+异步消息

图中执行本地事务(Bob账户扣款)和发送异步消息应该保证同时成功或者同时失败,也就是扣款成功了,发送消息一定要成功,如果扣款失败了,就不能再发送消息。那问题是:我们是先扣款还是先发送消息呢?

首先看下先发送消息的情况,大致的示意图如下:

事务消息:先发送消息

存在的问题是:如果消息发送成功,但是扣款失败,消费端就会消费此消息,进而向Smith账户加钱。

先发消息不行,那就先扣款吧,大致的示意图如下:

事务消息-先扣款

存在的问题跟上面类似:如果扣款成功,发送消息失败,就会出现Bob扣钱了,但是Smith账户未加钱。

可能大家会有很多的方法来解决这个问题,比如:直接将发消息放到Bob扣款的事务中去,如果发送失败,抛出异常,事务回滚。这样的处理方式也符合“恰好”不需要解决的原则。

这里需要说明一下:如果使用Spring来管理事物的话,大可以将发送消息的逻辑放到本地事物中去,发送消息失败抛出异常,Spring捕捉到异常后就会回滚此事物,以此来保证本地事物与发送消息的原子性。

RocketMQ支持事务消息,下面来看看RocketMQ是怎样来实现的。

RocketMQ实现发送事务消息

RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态。

细心的你可能又发现问题了,如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事物消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认,Bob的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

那我们来看下RocketMQ源码,是如何处理事务消息的。客户端发送事务消息的部分(完整代码请查看:rocketmq-example工程下的com.alibaba.rocketmq.example.transaction.TransactionProducer):

// =============================发送事务消息的一系列准备工作========================================
// 未决事务,MQ服务器回查客户端
// 也就是上文所说的,当RocketMQ发现`Prepared消息`时,会根据这个Listener实现的策略来决断事务
TransactionCheckListener transactionCheckListener = new TransactionCheckListenerImpl();
// 构造事务消息的生产者
TransactionMQProducer producer = new TransactionMQProducer("groupName");
// 设置事务决断处理类
producer.setTransactionCheckListener(transactionCheckListener);
// 本地事务的处理逻辑,相当于示例中检查Bob账户并扣钱的逻辑
TransactionExecuterImpl tranExecuter = new TransactionExecuterImpl();
producer.start()
// 构造MSG,省略构造参数
Message msg = new Message(......);
// 发送消息
SendResult sendResult = producer.sendMessageInTransaction(msg, tranExecuter, null);
producer.shutdown();
接着查看sendMessageInTransaction方法的源码,总共分为3个阶段:发送Prepared消息、执行本地事务、发送确认消息。

// ================================事务消息的发送过程=============================================
public TransactionSendResult sendMessageInTransaction(.....) {
// 逻辑代码,非实际代码
// 1.发送消息
sendResult = this.send(msg);
// sendResult.getSendStatus() == SEND_OK
// 2.如果消息发送成功,处理与消息关联的本地事务单元
LocalTransactionState localTransactionState = tranExecuter.executeLocalTransactionBranch(msg, arg);
// 3.结束事务
this.endTransaction(sendResult, localTransactionState, localException);
}endTransaction方法会将请求发往broker(mq server)去更新事务消息的最终状态:

  1. 根据sendResult找到Prepared消息 ,sendResult包含事务消息的ID
  2. 根据localTransaction更新消息的最终状态

如果endTransaction方法执行失败,数据没有发送到broker,导致事务消息的 状态更新失败,broker会有回查线程定时(默认1分钟)扫描每个存储事务状态的表格文件,如果是已经提交或者回滚的消息直接跳过,如果是prepared状态则会向Producer发起CheckTransaction请求,Producer会调用DefaultMQProducerImpl.checkTransactionState()方法来处理broker的定时回调请求,而checkTransactionState会调用我们的事务设置的决断方法来决定是回滚事务还是继续执行,最后调用endTransactionOnewaybroker来更新消息的最终状态。

再回到转账的例子,如果Bob的账户的余额已经减少,且消息已经发送成功,Smith端开始消费这条消息,这个时候就会出现消费失败和消费超时两个问题,解决超时问题的思路就是一直重试,直到消费端消费消息成功,整个过程中有可能会出现消息重复的问题,按照前面的思路解决即可。

消费事务消息

这样基本上可以解决消费端超时问题,但是如果消费失败怎么办?阿里提供给我们的解决方法是:人工解决。大家可以考虑一下,按照事务的流程,因为某种原因Smith加款失败,那么需要回滚整个流程。如果消息系统要实现这个回滚流程的话,系统复杂度将大大提升,且很容易出现Bug,估计出现Bug的概率会比消费失败的概率大很多。这也是RocketMQ目前暂时没有解决这个问题的原因,在设计实现消息系统时,我们需要衡量是否值得花这么大的代价来解决这样一个出现概率非常小的问题,这也是大家在解决疑难问题时需要多多思考的地方。

因此,消息的事物是个 非常  诱人的陷阱, 一旦消费端 出现 问题, 怎么通知 生产端, 生产端已经 脱离了原来的回滚条件, 因此无法处理, 那么 消费端 在简单的系统中还可以, 若是在一个  复杂的系统中, 他可能不知道 如何做补偿处理。  跟可怕的是 消费端已经出现问题了, 但是他还不知道, 例如消息出现了死信没被消费, 并且没人发现, 然后又过期了!!!  这真是噩梦一样!! 金融系统中强烈建议别用消息的 事物的功能!

要知道一个消息, 可能关心这些消息的人有很多, 那么三个和尚没水吃, 当和尚多了后, 出现问题后, 应该由那个和尚去解决??

很可能众多和尚中仅仅有一个和尚知道如何补偿业务但是他自己未必知道就是他, 并且可能会被选为不是他去处理。

另外还可能是多个消息都要补偿这个事物这些都是问题!

为什么会出现这些问题?

本人粗浅的理解如下:

1. 若是这些相关操作必须在一个事物中进行, 那么说明他们是一个强耦合的关系, 那么拆散到很多系统是不合适的, 不符合 强内聚, 低耦合的设计方式。

2. 若是这些操作确实需要拆分为 多个系统进行, 那么说明 耦合不太高, 那么应该想办法取消 消息事物, 通过明确的多系统补偿的办法进行。 由于是主动的明确的补偿, 并且这些补偿是容易测到的 可能会更方便些!

20160321补充:在3.2.6版本中移除了事务消息的实现,所以此版本不支持事务消息,具体情况请参考rocketmq的issues:

https://github.com/alibaba/RocketMQ/issues/65

https://github.com/alibaba/RocketMQ/issues/138

https://github.com/alibaba/RocketMQ/issues/156

===============

关于事务消息,还有别的解决方案, 转载另一篇文章:

说到分布式事务,就会谈到那个经典的”账号转账”问题:2个账号,分布处于2个不同的DB,或者说2个不同的子系统里面,A要扣钱,B要加钱,如何保证原子性?

一般的思路都是通过消息中间件来实现“最终一致性”:A系统扣钱,然后发条消息给中间件,B系统接收此消息,进行加钱。

但这里面有个问题:A是先update DB,后发送消息呢? 还是先发送消息,后update DB?

假设先update DB成功,发送消息网络失败,重发又失败,怎么办?
假设先发送消息成功,update DB失败。消息已经发出去了,又不能撤回,怎么办?

所以,这里下个结论: 只要发送消息和update DB这2个操作不是原子的,无论谁先谁后,都是有问题的。

那这个问题怎么解决呢??

错误的方案0

有人可能想到了,我可以把“发送消息”这个网络调用和update DB放在同1个事务里面,如果发送消息失败,update DB自动回滚。这样不就保证2个操作的原子性了吗?

这个方案看似正确,其实是错误的,原因有2:

(1)网络的2将军问题:发送消息失败,发送方并不知道是消息中间件真的没有收到消息呢?还是消息已经收到了,只是返回response的时候失败了?

如果是已经收到消息了,而发送端认为没有收到,执行update db的回滚操作。则会导致A账号的钱没有扣,B账号的钱却加了。

(2)把网络调用放在DB事务里面,可能会因为网络的延时,导致DB长事务。严重的,会block整个DB。这个风险很大。

基于以上分析,我们知道,这个方案其实是错误的!

方案1–业务方自己实现

假设消息中间件没有提供“事务消息”功能,比如你用的是Kafka。那如何解决这个问题呢?

解决方案如下:
(1)Producer端准备1张消息表,把update DB和insert message这2个操作,放在一个DB事务里面。

(2)准备一个后台程序,源源不断的把消息表中的message传送给消息中间件。失败了,不断重试重传。允许消息重复,但消息不会丢,顺序也不会打乱。

(3)Consumer端准备一个判重表。处理过的消息,记在判重表里面。实现业务的幂等。但这里又涉及一个原子性问题:如果保证消息消费 + insert message到判重表这2个操作的原子性?

消费成功,但insert判重表失败,怎么办?关于这个,在Kafka的源码分析系列,第1篇, exactly once问题的时候,有过讨论。

通过上面3步,我们基本就解决了这里update db和发送网络消息这2个操作的原子性问题。

但这个方案的一个缺点就是:需要设计DB消息表,同时还需要一个后台任务,不断扫描本地消息。导致消息的处理和业务逻辑耦合额外增加业务方的负担。

方案2 – RocketMQ 事务消息

为了能解决该问题,同时又不和业务耦合,RocketMQ提出了“事务消息”的概念。

具体来说,就是把消息的发送分成了2个阶段:Prepare阶段和确认阶段。

具体来说,上面的2个步骤,被分解成3个步骤:
(1) 发送Prepared消息
(2) update DB
(3) 根据update DB结果成功或失败,Confirm或者取消Prepared消息。

可能有人会问了,前2步执行成功了,最后1步失败了怎么办?这里就涉及到了RocketMQ的关键点:RocketMQ会定期(默认是1分钟)扫描所有的Prepared消息,询问发送方,到底是要确认这条消息发出去?还是取消此条消息?

具体代码实现如下:

也就是定义了一个checkListener,RocketMQ会回调此Listener,从而实现上面所说的方案。
// 也就是上文所说的,当RocketMQ发现`Prepared消息`时,会根据这个Listener实现的策略来决断事务
TransactionCheckListener transactionCheckListener = new TransactionCheckListenerImpl();
// 构造事务消息的生产者
TransactionMQProducer producer = new TransactionMQProducer("groupName");
// 设置事务决断处理类
producer.setTransactionCheckListener(transactionCheckListener);
// 本地事务的处理逻辑,相当于示例中检查Bob账户并扣钱的逻辑
TransactionExecuterImpl tranExecuter = new TransactionExecuterImpl();
producer.start()
// 构造MSG,省略构造参数
Message msg = new Message(......);
// 发送消息
SendResult sendResult = producer.sendMessageInTransaction(msg, tranExecuter, null);
producer.shutdown();
public TransactionSendResult sendMessageInTransaction(.....) {
// 逻辑代码,非实际代码
// 1.发送消息
sendResult = this.send(msg);
// sendResult.getSendStatus() == SEND_OK
// 2.如果消息发送成功,处理与消息关联的本地事务单元
LocalTransactionState localTransactionState = tranExecuter.executeLocalTransactionBranch(msg, arg);
// 3.结束事务
this.endTransaction(sendResult, localTransactionState, localException);
}
总结:对比方案2和方案1,RocketMQ最大的改变,其实就是把“扫描消息表”这个事情,不让业务方做,而是消息中间件帮着做了。
至于消息表,其实还是没有省掉。因为消息中间件要询问发送方,事物是否执行成功,还是需要一个“变相的本地消息表”,记录事物执行状态。

人工介入

可能有人又要说了,无论方案1,还是方案2,发送端把消息成功放入了队列,但消费端消费失败怎么办?

消费失败了,重试,还一直失败怎么办?是不是要自动回滚整个流程?

答案是人工介入。从工程实践角度讲,这种整个流程自动回滚的代价是非常巨大的,不但实现复杂,还会引入新的问题。比如自动回滚失败,又怎么处理?

对应这种极低概率的case,采取人工处理,会比实现一个高复杂的自动化回滚系统,更加可靠,也更加简单。

本部分来源:https://blog.csdn.net/u012422829/article/details/70248286

 

5你也许并不需要消息队列

消息队列是一个能让你获得容错性,分布式,解耦等架构能力的系统。纸上谈兵的话,它看起来还不错。

或许消息列队在你的应用中有不少适用的场景。你可以看下这篇关于消息队列优点的文章,看看到底有哪些合适的场景。但可不要因为说"能解耦那太好了”就轻易使用它。我们来看一个例子——你希望你的邮件发送和订单处理互相解耦。

因此你发送一个消息到消息队列里,然后邮件处理系统取出这个消息并发送邮件。那你在一个独立的单classpath的应用中怎么实现呢?让你的订单处理服务依赖于一个邮件服务,然后调用sendEmail()方法,而不是sendToMQ()方法。如果你使用了消息队列,你需要定义一个两个系统都能识别的消息格式 ;如果你不使用消息队列,那么你得定义一个方法签名。它们有什么本质的区别吗?其实没有。

不过你可能还有别的消费者想要对某个指定的消息进行额外的处理?这的确是可能发生的,而并不只是针对我们这里说到的这个项目而已。尽管确有可能,但相比添加另一个方法调用而言,它可能并不值当。耦合?是的。不过这个耦合并没有什么不方便的。

那我应该如何处理峰值流量?你可以通过消息队列将请求放到一个持久化队列中,然后再一并处理它们。这是一个非常有用的特性,不过它也受限于几个 因素——你的请求是在UI后台处理,还是需要即时响应?serlvet容器的线程池某种程度上可以当作是一个队列,用户最终会拿到响应,但是得需要等待(如果线程的超时时间过短的话,请求可能会丢失)。

你可以使用一个内存队列来存储那些较重的请求(得在UI后台进行处理)。不过注意了,你的队列并不是默认高可用的。比如说,如果一个消息队列节点挂掉了,你的消息就丢失了。因此,不去使用应用节点内的内存队列,而是去使用一个消息队列,这可能并没有什么优势。

消息队列使得我们可以进行异步处理——这的确是个有用的特性。你不希望在用户等待的时候做一些很重的操作。不过你也可以使用一个内存队列,或者简单地启动一个新的线程(比如Spring的@Async注解)。这样又有另一个问题——如果消息丢失的话是否有问题?如果你应用处理请求的节点挂了,你可以进行恢复吗?你会发现这事会经常发生,如果不保证所有消息都处理到的话,很难保证功能的正确性。因此,仅将较重的调用进行异步处理是比较可取的。

把消息放到队列以便让另一个组件来进行处理,对于这个场景,如果消息丢失是无法接受的 ,这也有一个很简单的解决方案——数据库。你可以把一条processed=false的数据存储到数据库中。然后再运行一个调度作业,将所有未处理的记录挑选出来,异步地进行处理。当处理完成的时候,将标记设为true。我经常用这个方法,包括在一些大型的线上系统中,它也工作得挺好的。

这样你还能不断地对你的应用节点进行扩展,只要它们的内存中没有任何的持久化状态的话。不管你是否使用了消息队列都可以(临时的内存处理队列并不属于持久化状态)。

为什么我要给经常用到的消息队列提供一些备选方案?因为如果你由于不恰当的原因选择了它,那么消息队列可能会成为一个负担。它们并非如想像中那样容易使用。首先,它有一个学习曲线。一般来说,你集成的组件切分得越多,就越容易出现问题。其次,还有一个设置及配置的成本。比如说,当消息队列需要在一个集群中运行的话,比如说多个数据中心,那么这就变得复杂了。

高可用性并不是上来就有的——默认它是不会打开的。还有就是你的应用节点如何连接到消息队列?通过一个刷新的连接池,或者使用短生命周期的DNS记录,还是通过一个负载均衡器?你的队列可能还有许多配置项,大小是多少,行为是怎样的(消费者需不需要确认接受,要不要通知处理失败,多个消费者能够取到同一个消息吗,消息有没有TTL,等等)同时还有网络及消息传递的开销,尤其是现在大家都喜欢用XML或者JSON来传输消息。如果你过度地使用了消息队列,那么它会增加你系统的延时。

最后一点,但并不是最次要的——如果出现问题的话,使用消息队列会让问题跟踪变得异常困难你没法在IDE中看到所谓的调用层次,因为一旦你发送消息到队列里了,你就得自己去查找它在哪里处理的了。这可不是听起来那么简单的。你看到了吧,它会给你增加许多的复杂性,以及许多需要注意的东西。

通常而言,在某些上下文中,消息队列还是非常有用的。当它们的确适合的话,我也会在项目中使用它们——比方说,我们不想丢失消息,但又希望能快速地进行处理。我也见过它在一些不太常见的场景中使用的情况,比如说只有一个应用节点来进行消费,不管是哪个节点投递过来的消息。你还可以看下stackoverflow上的这个问题。还有一些使用场景就是,或许你的确需要进行多语言间的通信,又或者你的数据流已经过于复杂了,不使用新的消息消费者而是增加新方法调用的话代价会很大。

我想说的是那句老掉牙的真理“杀鸡焉用牛刀”。如果你不是很确定已经没有别的更容易管理和维护的方法,一定要使用消息队列的话,最好不要使用它。不要因为”万一它有用呢“而去用它——只有你确实觉得需要的话再去使用。因为很有可能,就像这里说到的这个项目一样,消息队列其实是没有必要的。

 

6. 下面是消息的另外一些问题

针对rocketMQ,大型软件公司也可以抽出人手对rocketMQ进行定制化开发,毕竟国内有能力改JAVA源码的人,还是相当多的。至于kafka,根据业务场景选择,如果有日志采集功能,肯定是首选kafka了。具体该选哪个,看使用场景。

 如何保证消息队列是高可用的?

分析:在第二点说过了,引入消息队列后,系统的可用性下降。在生产中,没人使用单机模式的消息队列。因此,作为一个合格的程序员,应该对消息队列的高可用有很深刻的了解。

如果面试的时候,面试官问,你们的消息中间件如何保证高可用的?你的回答只是表明自己只会订阅和发布消息,面试官就会怀疑你是不是只是自己搭着玩,压根没在生产用过。请做一个爱思考,会思考,懂思考的程序员。

回答:这问题,其实要对消息队列的集群模式要有深刻了解,才好回答。

以rcoketMQ为例,他的集群就有多master 模式、多master多slave异步复制模式、多 master多slave同步双写模式。多master多slave模式部署架构图:

其实博主第一眼看到这个图,就觉得和kafka好像,只是NameServer集群,在kafka中是用zookeeper代替,都是用来保存和发现master和slave用的。通信过程如下:

Producer 与 NameServer集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Broker Master 建立长连接,且定时向 Broker 发送心跳。Producer 只能将消息发送到 Broker master,但是 Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。

那么kafka呢,为了对比说明直接上kafka的拓补架构图

如上图所示,一个典型的Kafka集群中包含若干Producer(可以是web前端产生的Page View,或者是服务器日志,系统CPU、Memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。
至于rabbitMQ,也有普通集群和镜像集群模式,自行去了解,比较简单,两小时即懂。

要求,在回答高可用的问题时,应该能逻辑清晰的画出自己的MQ集群架构或清晰的叙述出来。

如何保证消息不被重复消费?

分析:这个问题其实换一种问法就是,如何保证消息队列的幂等性?这个问题可以认为是消息队列领域的基本问题。换句话来说,是在考察你的设计能力,这个问题的回答可以根据具体的业务场景来答,没有固定的答案。

回答:先来说一下为什么会造成重复消费?

其实无论是那种消息队列,造成重复消费原因其实都是类似的。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。只是不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念,简单说一下(如果还不懂,出门找一个kafka入门到精通教程),就是每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知道自己已经消费过了。那造成重复消费的原因?,就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。

如何解决?这个问题针对业务场景来答分以下几点

(1)比如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
(2)再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。
(3)如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

如何保证消费的可靠性传输?

分析:我们在使用消息队列的过程中,应该做到消息不能多消费,也不能少消费。如果无法做到可靠性传输,可能给公司带来千万级别的财产损失。同样的,如果可靠性传输在使用过程中,没有考虑到,这不是给公司挖坑么,你可以拍拍屁股走了,公司损失的钱,谁承担。还是那句话,认真对待每一个项目,不要给公司挖坑。

回答:其实这个可靠性传输,每种MQ都要从三个角度来分析:生产者弄丢数据、消息队列弄丢数据、消费者弄丢数据

RabbitMQ

(1)生产者丢数据
从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。
transaction机制就是说,发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。

然而缺点就是吞吐量下降了。因此,按照博主的经验,生产上用confirm模式的居多。一旦channel进入confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,rabbitMQ就会发送一个Ack给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了.如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。处理Ack和Nack的代码如下所示:

channel.addConfirmListener(new ConfirmListener() {  
                @Override  
                public void handleNack(long deliveryTag, boolean multiple) throws IOException {  
                    System.out.println("nack: deliveryTag = "+deliveryTag+" multiple: "+multiple);  
                }  
                @Override  
                public void handleAck(long deliveryTag, boolean multiple) throws IOException {  
                    System.out.println("ack: deliveryTag = "+deliveryTag+" multiple: "+multiple);  
                }  
            });  

(2)消息队列丢数据
处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

那么如何持久化呢,这里顺便说一下吧,其实也很容易,就下面两步

1、将queue的持久化标识durable设置为true,则代表是一个持久的队列
2、发送消息的时候将deliveryMode=2

这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据

(3)消费者丢数据
消费者丢数据一般是因为采用了自动确认消息模式。这种模式下,消费者会自动确认收到信息。这时rahbitMQ会立即将消息删除,这种情况下如果消费者出现异常而没能处理该消息,就会丢失该消息。
至于解决方案,采用手动确认消息即可。

kafka

这里先引一张kafka Replication的数据流向图
Producer在发布消息到某个Partition时,先通过ZooKeeper找到该Partition的Leader,然后无论该Topic的Replication Factor为多少(也即该Partition有多少个Replica),Producer只将该消息发送到该Partition的Leader。Leader会将该消息写入其本地Log。每个Follower都从Leader中pull数据。

针对上述情况,得出如下分析
(1)生产者丢数据
在kafka生产中,基本都有一个leader和多个follwer。follwer会去同步leader的信息。因此,为了避免生产者丢数据,做如下两点配置

第一个配置要在producer端设置acks=all。这个配置保证了,follwer同步完成后,才认为消息发送成功。

在producer端设置retries=MAX,一旦写入失败,这无限重试

(2)消息队列丢数据
针对消息队列丢数据的情况,无外乎就是,数据还没同步,leader就挂了,这时zookpeer会将其他的follwer切换为leader,那数据就丢失了。针对这种情况,应该做两个配置。

replication.factor参数,这个值必须大于1,即要求每个partition必须有至少2个副本

min.insync.replicas参数,这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系

这两个配置加上上面生产者的配置联合起来用,基本可确保kafka不丢数据

(3)消费者丢数据
这种情况一般是自动提交了offset,然后你处理程序过程中挂了。kafka以为你处理好了。再强调一次offset是干嘛的
offset:指的是kafka的topic中的每个消费组消费的下标。简单的来说就是一条消息对应一个offset下标,每次消费数据的时候如果提交offset,那么下次消费就会从提交的offset加一那里开始消费。
比如一个topic中有100条数据,我消费了50条并且提交了,那么此时的kafka服务端记录提交的offset就是49(offset从0开始),那么下次消费的时候offset就从50开始消费。
解决方案也很简单,改成手动提交即可。

ActiveMQ和RocketMQ

大家自行查阅吧

 如何保证消息的顺序性?

分析:其实并非所有的公司都有这种业务需求,但是还是对这个问题要有所复习。

回答:针对这个问题,通过某种算法,将需要保持先后顺序的消息放到同一个消息队列中(kafka中就是partition,rabbitMq中就是queue)。然后只用一个消费者去消费该队列。

有的人会问:那如果为了吞吐量,有多个消费者去消费怎么办?

这个问题,没有固定回答的套路。比如我们有一个微博的操作,发微博、写评论、删除微博,这三个异步操作。如果是这样一个业务场景,那只要重试就行。比如你一个消费者先执行了写评论的操作,但是这时候,微博都还没发,写评论一定是失败的,等一段时间。等另一个消费者,先执行写评论的操作后,再执行,就可以成功。

总之,针对这个问题,我的观点是保证入队有序就行,出队以后的顺序交给消费者自己去保证,没有固定套路。

 

最后, 消息是个好东西, 但是充满了陷阱, 不小心连  是否出问题了, 你都不知道!

java和maven环境变量设置

JAVA_HOME、CLASSPATH、PATH设置详解

  Windows下JAVA用到的环境变量主要有3个,JAVA_HOME、CLASSPATH、PATH。

     JAVA_HOME 指向的是JDK的安装路径,如C:\jdk1.5.0_06,在这路径下你应该能够找到bin、lib等目录。

( 今晚使用新系统配置,不知道怎么的需要加bin才可以在CMD运行,JAVA_HOME=C:\jdk1.5.0_06\bin; 下面是网上整理实践可使用贴上的,注意JDK和Eclipse必须同位数Bit才可以使用,不能是不同位数的程序,已实践,不然要报错无法使用;如需卸载JDK,JDK不能直接删除文件夹,要使用卸载方式,不然无法再使用JDK或无法卸载,只有重新安装OS才能使用了,以前遇过 )

JAVA_HOME=C:\jdk1.5.0_06

PATH 环境变量原来Windows里面就有,你只需修改一下,使他指向JDK的bin目录,这样你在控制台下面编译、执行程序时就不需要再键入一大串路径了。设置方法是保留原来的PATH的内容,并在其中加上%JAVA_HOME%\bin

(注,如果你对DOS批处理不了解,你可能不明白%%引起来的内容是什么意思;其实这里是引用上一步设定好的环境变量JAVA_HOME,你写成x:\JDK_1.4.2也是可以的;你可以打开一个控制台窗口,输入echo %JAVA_HOME%来看一下你的设置结果)

PATH=%JAVA_HOME%\bin;%PATH%
同样,%PATH%是引用以前你设置的PATH环境变量,你照抄以前的值就行了。

CLASSPATH 环境变量我放在最后面,是因为以后你出现的莫名其妙的怪问题80%以上都可能是由于CLASSPATH设置不对引起的,所以要加倍小心才行。
CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
首先要注意的是最前面的".;",如果你看不清,我给你念念——句点分号。这个是告诉JDK,搜索CLASS时先查找当前目录的CLASS文件——为什么这样搞,这是由于LINUX的安全机制引起的,LINUX用户很明白,WINDOWS用户就很难理解(因为WINDOWS默认的搜索顺序是先搜索当前目录的,再搜索系统目录的,再搜索PATH环境变量设定的) 。
为什么CLASSPATH后面指定了tools.jar这个具体文件?不指定行不行?显然不行,行的话我还能这么罗索嘛!:) 这个是由java语言的import机制和jar机制决定的,你可以查资料解决。
具体的设定方法: 右键点击我的电脑->属性->高级->环境变量,修改下面系统变量那个框里的值就行了。

用户修改txt文本属性auto.bat文件,在其末尾加入:

set JAVA_HOME=C:\jdk1.5.0_06
set PATH=%JAVA_HOME%\bin;%PATH%
set CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

 

二 Maven的安装与环境配置

 想要安装 Apache Maven在Windows 系统上, 需要下载 Maven 的 zip 文件,并将其解压到你想安装的目录,并配置 Windows

环境变量。

所需工具 :

1.JDK

2.Maven

3.Windows 7

Maven 3.2 要求 JDK 1.6 或以上版本, 而 Maven 3.0/3.1 需要 JDK 1.5 或以上。

 

1.JDK 和 JAVA_HOME

确保已安装JDK,并 将“JAVA_HOME” 变量已加入到 Windows 环境变量中。

我们可以打开Windows的命令行,运行如下的命令来检查Java的安装:

上述命令首先检查环境变量JAVA_HOME是否指向了正确的JDK目录,然后运行了java命令,如果无法执行命令,

或者无法找到JAVA_HOME环境变量,就需要检查Java是否安装了,或者环境变量是否设置正确。

 

2.下载Apache Maven

下载地址:http://maven.apache.org/download.html,打开后找到下载链接,如图:

小Alan是老以前下载的3.2.2的版本,用着也没什么问题,所以也懒得下载最新的版本,这里大家可以下载最新的版本下来用。

当然,如果你对Maven的源代码感兴趣并想自己构建Maven,也可以下载apache-maven-3.3.9-src.zip。

将下载的安装包解压到特定的目录下,假设你解压缩到文件夹 –  D:\apache-maven-3.2.2,如图:

 

 

3.添加 M2_HOME 和 MAVEN_HOME

打开系统属性面板(右击“计算机”>"属性"),单击高级系统设置,再单击环境变量,在系统变量中新建一个变量,变量名为M2_HOME,变量值为Maven的安装目录,如图:

 

M2_HOME 或 MAVEN_HOME

Maven 说只是添加 M2_HOME , 但一些项目仍引用 Maven 的文件夹 MAVEN_HOME, 因此,为了安全也把它添加进去。

小Alan使用的是M2_HOME。

 

4.添加到环境变量 - PATH

编辑 PATH 变量,添加 Maven bin 文件夹到 PATH 的最后,如: %M2_HOME%\bin, 这样就可以在命令中的任何目录下运行

Maven 命令了。

 

5.验证

在命令行中输入:echo %M2_HOME%以及mvn –version或-v,出现下面这个界面,说明我们的maven已经安装成功。

第一条命令echo %M2_HOME%用来检查M2_HOME是否指向了正确的Maven安装目录,mvn -v用来检查Windows

是否能够找到正确的mvn执行脚本。

 

6.升级

在Windows上更新Maven非常简单,只需要下载新的文件解压至本地目录,然后更新M2_HOME环境变量指向的目录即可,

降级也是同理,不做过多介绍。

 

可爱博主:AlanLee

博客地址:http://www.cnblogs.com/AlanLee

本文出自博客园,欢迎大家加入博客园。

eclipse菜单解释及中英对照

在使用Eclipse作为开发工具的时候,建议使用英文版本的(直接百度从官网下就行,这里不详细描述,如果有问题,咱们私聊)。虽然中文版本的对于和我一样对英文是小白的看起来特别爽,但是公司大多是英文版本的,所以清醒些,该怎么办?

在Eclipse工作台的上方提供了菜单栏,该菜单栏包含了实现Eclipse各项功能的命令,并且与编辑器相关,即菜单栏中的菜单项与当前编辑器内打开的文件是关联的。例如,编辑器内没有打开任何文件,那么,将显示如图1.2所示的菜单栏。如果打开了一个JSP文件,那么将显示如图1.3所示的菜单栏。

但是用英文版的,那些英文单词怎么办,连建立个java类,都不知道从何下手,有没有和我一样的。

在这里我就做一次搬运工,整理了一些关于eclipse的中英对照的资料,及一些快捷键的用法,希望对自己和大家都有帮助。如果感觉有帮助请叫我雷锋

以下是关于eclipse的中英对照,如果遇到不懂的地方可以做参考

 

「File」菜单

这个菜单可以建立、储存、关闭、打印、汇入及汇出工作台资源以及结束工作台本身。

名称功能
New(新建)建立Java元素或新资源。配置哪些元素会显示在「Window」→「Preferences」的子菜单中。在Java视景中,依预设,会提供项目、套件、类别、接口、来源数据夹、实时运算簿、档案和数据夹的建立动作。
Close(关闭)关闭现行编辑器。如果编辑器中有资料尚未储存,则会显示一个储存要求对话框。
Close All(全部关闭)关闭所有编辑器。如果编辑器中有资料尚未储存,则会显示一个储存要求对话框。
Save(储存)储存现行编辑器的内容。如果编辑器中没有未储存的变更,则会停用。
Save As(另存新檔)以新名称储存现行编辑器中的内容。
Save All(全部储存)储存所有编辑器内容以及未储存的变更。如果没有编辑器中有未储存的变更,则会停用。
Revert(回复)将现行编辑器的内容回复成已储存档案中的内容。如果编辑器中没有未储存的变更,则会停用。
Move(移动)移动资源。如果是Java元素则会停用。如果要移动Java元素,请使用「Refactor」→「Move」(如此会更新档案的所有参照),或使用「Edit」→「Cut/Paste」(如此不会更新参照)。
Rename(重新命名)将资源重新命名。如果是Java元素则会停用。如果要重新命名Java元素,请使用「Refactor」→「Rename」(如此会更新档案的所有参照)。
Refresh(重新整理)以本端档案系统来重新整理所选元素的内容。如果不是从特定选项启动,这个指令会重新整理所有项目。
Print(打印)打印现行编辑器的内容。会在编辑器成为焦点时启用。
Switch workspace(切换工作区)这个指令可以切换至不同的工作区这会重新启动工作台
Open external file(开启外部档案)这个指令可以在文字编辑器中开启不在工作区中的档案
Import(汇入)开启汇入精灵对话框。JDT不会提供任何汇入精灵。
Export(汇出)开启汇出精灵对话框。JDT会提供JAR档汇出精灵和Javadoc产生精灵。
Properties(内容)开启所选元素的「内容」页面。依据Java项目开启Java建置路径页面,且可使用Javadoc位置页面。如果是JAR保存文件,请在这个配置JAR的程序文件附加与Javadoc位置。
Recent file list(最近使用的档案清单「File底端维护了一份最近在工作台中存取的档案的清单只要选取文件名,就可以从「File开启这其中的任何档案。
Exit(结束)结束Eclipse

 

「Edit」菜单

这个菜单可协助操作编辑器区域中的资源

名称功能
Undo(复原)回复成编辑器中的前一次变更
Redo(重做)回复已取消的变更
Cut(剪下)将目前所选取的文字或元素复制到剪贴簿中,并移除元素。就元素而言,在贴到剪贴簿前不会移除。
Copy(复制)将目前所选取的文字或元素复制到剪贴簿中。
Paste (贴上)将目前的内容当成文字贴到编辑器中,或当成同层级或下层元素,贴到目前所选的元素中。
Delete(删除)删除目前的文字或元素选项。
Select All(全选)选取所有的编辑器内容。
Find / Replace(寻找/取代)开启「寻找/取代」对话框。限编辑器。
Find Next(寻找下一个)寻找目前所选文字下一个搜寻结果。限编辑器。
Find Previous(寻找上一个)寻找目前所选文字上一个搜寻结果。限编辑器。
Incremental Find Next(增量寻找下一个)启动增量寻找模式。在呼叫后,请按照状态列中的指示来输入搜寻文字。限编辑器。
Incremental Find Previous(增量寻找上一个)启动增量寻找模式。在呼叫后,请按照状态列中的指示来输入搜寻文字。限编辑器。
Add Bookmark(新增书签)为目前的文字选项或所选取的元素新增书签。
Add Task(新增作业)为目前的文字选项或所选取的元素新增使用者定义的作业。
Expand Selection to(展开选项至)n         含括元素:选取程序代码中的含括表示式、区块、方法。这个动作会注意Java语法。如果程序代码的语法有错,可能无法运作正常。(上移键)n         下一个元素:选取现行与下一个元素。(右移键)

n         上一个元素:选取现行与上一个元素(左移键)

n         还原前次的选择:在呼叫展开选项至之后,还原先前的选项。(下移键)

Show Tooltip Description(显示工具提示说明)以浮动说明方式显示出现在现行光标位置上的值。对话框可以卷动,因而不会缩短说明。
Content Assist(内容辅助)在现行光标位置开启一个内容辅助对话框,以显示Java程序代码的辅助提议与范本。请参阅「模板」喜好设定页面,以取得可用的模板(「Window」→「Preferences」→「Java」→「Editor」→「Templates」),然后移至「编辑器」喜好设定页面(「Window」→「Preferences」→「Java」→「Editor」→「Code Assist」),来配置程序代码辅助的行为。
Quick Fix(快速修正)如果光标位于有出现问题指示之处,则这个动作会在现行光标处开启一个内容辅助对话框,以提供可能的更正动作。
Parameter Hints(参数提示)如果光标位于方法参照的参数规格处,这个动作会以浮动说明的方式显示参数类型信息。现行光标处的参数会以粗体字显示。
Encoding(编码)切换目前所示文字内容的编码。

 

「Source」菜单

名称功能
Toggle Comment(批注)标注出内含现行选择项的所有字行。
Add Block Comment(批注区块)标注出内含现行选择项的区块。
Remove Block Comment(解除批注区块)取消标注内含现行选择项的区块。
Shift Right(向右移位)增加目前所选字行的内缩层次。只有在选择项涵盖多行或一整行时才会启用。
Shift Left(向左移位)减少目前所选字行的内缩层次。只有在选择项涵盖多行或一整行时才会启用。
Format(格式)可使用程序代码格式制作器,来设定目前文字选择项的格式。格式设定选项是在「Code Formatter」喜好设定页面(「Window」→「Preferences」→「Java」→Code Formatter))中配置
Format Element(格式成员)格式化成员
Sort Members(排序成员)「Window」→「Preferences」→「Java」→「Appearance」→「Members Sort Order」中指定的排序次序,来排序类型中的成员
Organize Imports(组织汇入)组织目前开启或所选编译单元中的汇入宣告。会移除不必要的汇入宣告,且会按照「Organize Import」喜好设定页面(「Window」→「Preferences」→「Java」→「Organize Import」)中的指定,来排列必要的汇入宣告。「Organize Import」可执行于不完整的程序文件上,并且会在所参照的类型名称无法唯一对映至现行项目中的某个类型时提示。也可以组织多个编译单元,其做法是对某个套件呼叫动作,或选取一组编译单元。
Add Import(新增汇入)为目前所选的类型参照建立一项汇入宣告。如果类型参照完整,则会移除资格。如果所参照的类型名称无法唯一对映至现行项目中的某个类型,将会提示指定正确的类型。「Add Import」会试着遵循「Organize Import」喜好设定页面中指定的汇入顺序。
Override/Implement Methods(置换/实作方法)会开启「Override Method」对话框,可以置换或实作现行类型中的方法。适用于类型或类型中的某个文字选择项。
Generate Getter and Setter(产生Getter和Setter)开启「Generate Getter and Setter」对话框,可以为现行类型中的字段,建立Getter和Setter。适用于字段与类型或类型中的某个文字选择项。
Generate Delegate Methods(产生委派方法)开启「Generate Delegate Methods」对话框,可以为现行类型中的字段建立方法委派。可用在字段。
Add Constructor from Superclass(新增Super类别中的建构子)为目前所选的类型新增Super类别中所定义的建构子。适用于类型或类型中的某个文字选择项。
Surround with try/catch(以try/catch包覆)针对所选的陈述式,评估所有必须捕捉到的异常状况。这些表示式会包覆try catch区块。可以使用编辑菜单中的展开选项至,以取得有效的选项范围。
Externalize Strings(将字符串提出)开启「Externalize Strings」精灵。这个精灵可以藉由会存取内容档的陈述式,来更换程序代码中的所有字符串。
Find Strings to Externalize(寻找要提出的字符串)会出现一个对话框,其中显示未提出字符串数目的摘要。适用于项目、来源资料夹与套件。
Convert Line Delimiters To(将行定界字符转换成)在目前开启的编辑器中,变更所有行定界字符,而采用下列操作系统中所用的行定界字符:n         CRLF(Windows)

n         LF(Unix、MacOSX)

n         CR(传统MacOS)

Java编辑器容许混合使用行定界字符。不过,其它某些工具会要求使用和OS一致的行定界字符,或者要求至少行定界字符要一致。

 

「Refactor」菜单

重构指令也可以在一些视图的快速菜单与Java编辑器中找到。

名称功能
Undo(复原)「Undo」前次的重构作业。重构复原缓冲区,共在执行重构后程序文件未变更的状况下有效。
Redo(重做)重做前次复原的重构作业。重构复原/重做缓冲区的有效期,仅限于执行重构后到没有其它程序文件变更的这段时间。
Rename(重新命名)启动「Rename Refactoring」对话框:重新命名所选的元素,并且(如果有启用的话)更正元素的(以及其它档案中的)所有参照。适用于方法、字段、区域变量、方法参数、类型、编译单元、套件、来源数据夹、项目,并且适用于可解析成这些元素类型之一的文字选项。
Move(移动)启动「Move」重构对话框:移动所选的元素,并(如果有启用的话)更正元素的(以及其它档案中的)所有参照。可套用至一或多个Static方法、Static字段、类型、编译单元、套件、来源数据夹与项目,并且套用于可解析成这些元素类型之一的文字选择项。
Change Method Signature(变更方法签章)启动「Change Method Signature」重构对话框。变更参数名称、参数类型、参数顺序,并更新对应方法的所有参照。此外,可以移除或新增参数,也可以变更方法传回类型及其可见性。这个重构作业可套用至方法或套用在解析成方法的文字选项。
Convert Anonymous Class to Nested(将匿名类别转换成巢状)启动「Convert Anonymous Class to Nested」重构对话框。协助将匿名内部类别转换成成员类别。这个重构作业可套用至匿名内部类别。
Convert Nested Type to Top Level(将巢状类型转换成最上层)启动「Convert Nested Type to Top Level」重构对话框。为所选成员类型建立新的Java编译单元,同时依需要更新所有参照。对于非static成员类型,将新增一个字段,以容许存取先前含括的实例。这个重构作业可套用至成员类型或解析成成员类型的文字。
Push Down(下推)启动「Push Down」重构对话框。将类别中的一组方法和字段移至它的子类别。这个重构作业可套用至一个或多个以相同类型宣告的方法和字段,或套用在字段或方法内的文字选项。
Pull Up(上拉)启动「Pull Up」重构精灵。将字段或方法移至其宣告类别的Super类别,或(如果是方法)将方法宣告成Super类别中的abstract。这个重构作业可套用至一个或多个以相同类型宣告的方法、字段和成员类型,或套用在字段、方法或成员类型内的文字选项。
Extract Interface(撷取界面)启动「Extract Interface」重构对话框。以一组方法建立新的接口,并使所选类别实作接口,同时选择性将类别参照变更为新接口(在可能的情况下)。这个重构作业可套用至类型。
Use Supertype Where Possible(适当时使用Super类型)启动「Use Supertype Where Possible」重构对话框。在识别所有可能发生这个取代的位置后,将出现的类型换成其Super类型之一。这个重构作业可用在类型之上。
Inline(列入)启动「Inline」重构对话框。列入区域变量、方法或常数。这个重构作业可用在方法、static final字段,以及解析为方法、static final字段或区域变量的文字选项。
Extract Method(撷取方法)启动「Extract Method」重构对话框。会建立一个内含目前所选之陈述式或表示式的新方法,并将选择项换成新方法的参照。可以使用编辑菜单中的展开选项至,以取得有效的选项范围。
这项特性非常适合用来清除冗长、杂乱和太复杂的方法。
Extract Local Variable(撷取区域变量)启动「Extract Local Variable」重构对话框。会建立一个新变量,以指定给目前所选的表示式,并将选择项换成新变量的参照。这个重构作业可用在解析为区域变量的文字选项。可以使用编辑菜单中的展开选项至,以取得有效的选项范围。
Extract Constant(撷取常数)启动「Extract Constant」重构对话框。从所选表示式中建立static final字段并替代字段参照,以及选择性地重新写入其它出现相同表示式的位置。这个重构作业可用在static final字段,以及解析为static final字段的文字选项。
Convert Local Variable to Field(将区域变量转换成字段)启动「Convert Local Variable to Field」重构对话框。将区域变量转换成字段。如果在建立时已起始设定变量,则作业会将起始设定移至新字段的宣告,或移至类别的建构子。这个重构作业可用在解析为区域变量的文字选项。
Encapsulate Field(封装字段)启动「Encapsulate Field」重构对话框。会将字段的所有参照换成getting与setting方法。适用于所选的字段或可解析成字段的文字选择项。

 

「Navigate」菜单

这个菜单可以寻找及导览工作台中显示的资源及其它成品。

名称功能
Go Into(进入)将视图输入设定在目前所选的元素上。「套件浏览器」视图可支持这项。
Go To(移至)n         上一页:将视图输入设定在历程中的上一个输入上:必须有历程,才能用到这项(已使用「Go Into」)n         下一页:将视图输入设定在历程中的下一个输入上:必须有历程,才能用到这项(已使用「Go Into」、「Go Into」→「Back」)

n         往上移一层:将现行视图的输入设定在其输入的母元素上。

n         参照测试:浏览以找出所有参照目前选取之类型的JUnit测试

n         类型:浏览以找出类型,并在现行视图中显示它。「Package Explorer」视图支援这项。

n         套件:浏览以找出套件,并在现行视图中显示它。「Package Explorer」视图支援这项。

n         资源:浏览以找出资源,并在现行视图中显示它。

Open(开启)试着解析现行程序代码选项上所参照的元素,并开启宣告该参照的档案。
Open Type Hierarchy(开启类型阶层)试着解析现行程序代码选项上所参照的元素,并在「Type Hierarchy」视图中开启该元素。针对元素呼叫,并开启元素的类型阶层。显示Java元素的Java编辑器与视图中可支持这项。
Open Call Hierarchy(开启呼叫阶层)试着开启呼叫现行程序代码选项上所参照的元素,并在「Call Hierarchy」视图中开启该元素。
Open Super Implementation(开启super实作)开启一个编辑器,以显示目前所选方法或现行光标位置旁之方法的super实作。如果未选取方法,或者方法没有super实作,则不会开启编辑器。
Open External Javadoc(开启外部Javadoc)开启目前所选元素或文字选项的Javadoc文件。JAR或项目的Javadoc位置是在项目或JAR的「Javadoc Location」内容页面中指定。请注意,这个外部Javadoc文件可能未以现行程序代码中指定的Javadoc加以更新。可以使用Javadoc汇出精灵,在Java项目中为程序文件建立Javadoc文件。
Open Type(开启类型)显示「Open Type」选择对话框,以便在编辑器中开启一个类型。「开启类型」选择对话框中显示工作区中的所有现有类型。
Open Type In Hierarchy(在「阶层」中开启类型)显示「Open Type」选择对话框,以便在编辑器与「Type Hierarchy」视图中开启一个类型。「Open Type」选择对话框中显示工作区中的所有现有类型。
Show in → Package Explorer(显示在→套件浏览器)在「Package Explorer」视图中显示目前所选的元素(或现行光标位置旁的元素)。
Quick Outline(显示概要)为目前选取的类型开启小型概要器。
Quick Type Hierarchy(显示类型阶层)为目前选取的类型开启小型类型阶层器。
Next Annotation (移至下一个问题)选取下一个问题。Java编辑器中支持这项。
Previous Annotation (移至上一个问题)选取上一个问题。Java编辑器中支持这项。
Go to Last Edit Location(移至前次编辑位置)显示前次发生编辑的位置。
Go to Line(移至指定行号)开启对话框,以输入指示编辑器应移至的行号。限编辑器。
Back(向后)这个指令会导览至之前在编辑器中检视的前一个资源。这个指令和Web浏览器的上一页按钮相同。
Forward(向前这个指令会导览并复原之前的上一页指令所造成的效果。这个指令和Web浏览器的下一页按钮相同。

 

「Search」菜单

名称功能
Search...(搜寻...)开启搜寻对话框
File...(档案...)针对「档案搜寻」页面开启搜寻对话框
Java...(Java...)针对「Java搜寻」页面开启搜寻对话框
References(参照)寻找所选Java元素的所有参照
Declarations(宣告)寻找所选Java元素的所有宣告
Implementors(实作者)寻找所选接口的所有实作者。
Read Access(读取权)寻找所选字段的所有读取权
Write Access(写入权)寻找所选字段的所有写入权
Referring Tests...()寻找所选Java元素的所有测试参照
Occurrences in File(档案中的搜寻结果)寻找所选Java元素在其档案中的所有出现项目
Exception Occurrences(抛出例外中的搜寻结果)寻找所选Java元素在其抛出例外中的所有出现项目

Search Scopes Submenu(搜寻范围子菜单):

 

范围可用性说明
Workspace(工作区)所有元素在整个工作区中搜寻
Project(专案)所有元素在含括所选元素的项目中进行搜寻
Hierarchy(阶层)类型和成员在类型的阶层中搜寻
Workings Set(工作集)所有元素在工作集中搜寻

工作集对话框可以储存并命名范围。「搜寻范围」子菜单中亦会显示工作集的现有实例。

可在下列视图中透过所选资源与元素的快速菜单,来执行Java搜寻:

n         「Package Explorer」

n         「Outline」视图

n         「Search Result」视图

n         「Hierarchy」视图

n         「Browsing」视图

Java编辑器中亦提供「Search」快速菜单。目前所选文字必须可解析成Java元素,才能执行搜寻。

所选Java元素的类型会定义所能使用的「Search」快速菜单。Java编辑器不会根据选项而限制可用的Java搜寻项清单。

 

「Project」菜单

「项目」菜单可以对工作台中的项目执行动作(建置或编译)。

名称功能
Open Project(开启专案)显示对话框,可以选取开启已关闭的项目
Close Project(关闭专案)关闭目前所选取的项目
Build All(全部建置)这个指令会对工作台中的所有项目执行增量(incremental)建置。也就是说,它会建置(编译)自从前次增量建置后,工作台中受到任何资源变更所影响的所有资源。自动建置关闭时,才可使用这个指令。
Build Project(建置专案)这个指令会对目前选取的项目执行增量(incremental)建置。也就是说,它会建置(编译)自从前次建置后,受到任何资源变更所影响的项目中的所有资源。自动建置关闭时,才可使用这个指令。
Build Workings Set(重新建置工作集)这个菜单可以在工作集上执行增量(incremental)建置。也就是说,它会建置(编译)前次建置之后,受到任何资源变更所影响之工作集中的所有资源。自动建置关闭时,才可使用这个指令。
Clean(清除)这个指令会舍弃先前的所有建置结果。如果自动建置是开启的,这会呼叫完整的建置。
Build Automatically(自动建置)自动建置工作区中的所有项目。这个指令可以切换自动建置喜好设定。
Generate Javadoc...(产生Javadoc...)对目前选取的项目开启「Generate Javadoc」精灵。
Properties(内容)对目前选取的项目开启内容页面。

 

「Run」菜单

名称功能
Toggle Line Breakpoint(切换行岔断点)这个指令可在目前于作用中Java编辑器中所选之行处,新增或移除Java行岔断点。
Toggle Method Breakpoint(切换方法岔断点)这个指令可针对目前的二进制方法,新增或移除方法岔断点。可在Java类别档编辑器的来源中选取二进制方法,或在其它任何视图中选取(像是「Outline」视图)。
Toggle Watchpoint(切换监视点)这个指令可针对目前的Java字段,新增或移除字段监视点。可在Java编辑器的来源中选取字段,或在其它任何视图中选取(像是「Outline」视图)。
Skip All Breakpoints(忽略所有的岔断点)这个指令可以忽略所有的岔断点
Add Java Exception Breakpoint(新增Java异常状况岔断点)这个指令可建立一个异常状况岔断点。可藉由指定异常状况岔断点,而在掷出异常状况时,暂停执行绪或VM的执行。可设为在未捕捉到或捕捉到(或两者)异常状况时暂停执行。
Add Class Load Breakpoint这个指令可让以建立一个Class Load岔断点。
Run Last Launched(执行前一次的启动作业)这个指令可在执行模式下迅速重复最近一次的启动作业(如果有支持该模式的话)。
Debug Last Launched(除错前一次的启动作业)这个指令可在除错模式下迅速重复最近一次的启动作业(如果有支持该模式的话)。
Run History(执行历程)呈现在执行模式下启动的启动配置之最近历程的子菜单
Run As(执行为)呈现所登录之执行启动快捷方式的子菜单。启动快捷方式可支持工作台或作用中编辑器选项的感应式启动。
Run...(执行...)这个指令会了解启动配置对话框,以管理执行模式下的启动配置。
Debug History(除错历程)呈现在除错模式下启动的启动配置之最近历程的子菜单
Debug As(除错为)呈现所登录之除错启动快捷方式的子菜单。启动快捷方式可支持工作台或作用中编辑器选项的感应式启动。
Debug...(除错...)这个指令会了解启动配置对话框,以管理除错模式下的启动配置。
Inspect(视察)当执行绪暂停时,这个指令会使用「表示式」视图,显示在该执行绪之堆栈框或变量的环境定义下,视察所选表示式或变量的结果。
Display(显示)当执行绪暂停时,这个指令会使用「Display」视图,显示在该执行绪之堆栈框或变量的环境定义下,评估所选表示式的结果。如果目前作用中的部分是「Java Snippet Editor(Java片段编辑器)」,则会在其中显示结果。
Execute(执行)执行
Step into Selection这些指令可逐步执行所要除错的程序代码。
Externakl Tools(外部工具)外部工具

 

「Windows」菜单

这个菜单可以显示、隐藏,以及另行在工作台中操作各种视图、视景和动作。

名称功能
New Window(开新窗口)这个指令会开启一个新的工作台窗口,其中含有与现行视景相同的视景。
Open Perspective(开启视景)这个指令会在此工作台窗口中开启新视景。可以在「Window」→「Preferences」→「Workbench」→「Perspectives」页面中变更这个喜好设定。在工作台窗口内开启的所有视景都会显示在快捷方式列上。
Show View(显示视图)这个指令会在现行视景中显示选取的视图。可以在「Window」→「Preferences」→「Workbench」→「Perspectives」页面中配置开启视图的方式。可能会想开启的视图会最先列示;这份清单视现行视景而定。从其它... 子菜单中,可以开启任何视图。视图会依照「Show View」对话框中的各个种类来排序。
Customize Perspective(自订视景)每个视景包含一组预先定义的动作,可以从菜单列和工作台工具列存取这些动作。 
Save Perspective As(另存新视景)这个指令可以储存现行视景,以及建立自己的自订视景。储存视景之后,可以使用「Window」→「Show View」→「Other...」菜单项目来开启更多这类型的视景。
Reset Perspective(重设视景)这个指令会将现行视景的布置变更为其原始的配置。
Close Perspective(关闭视景)这个指令会关闭作用中的视景。
Close All Perspectives(关闭所有视景)这个指令会关闭工作台窗口中的所有已开启视景。
Navigation(导览)       这个子菜单包含用于在工作台窗口中的视图、视景及编辑器之间导览的按键。n         显示系统菜单:显示用来重新调整大小、关闭或固定现行视图或编辑器的菜单。

n         显示视图菜单:显示可在作用中视图的工具列中存取的下拉菜单。

n         将作用中的视图或编辑器最大化:使作用中的部分占用整个画面,如果已占用整个画面,就使它返回原始状态。

n         启动编辑器:使现行编辑器作用中。

n         下一个编辑器:启动最近使用的编辑器清单中的下一个开启的编辑器。

n         上一个编辑器:启动最近使用的编辑器清单中的上一个开启的编辑器。

n         切换至编辑器:显示一个对话框,用来切换到已开启的编辑器。显示一个对话框,用来切换到已开启的编辑器。

n         下一个视图:启动最近使用的视图清单中的下一个开启的视图。

n         上一个视图:启动最近使用的编辑器清单中的上一个开启的编辑器。

n         下一个视景:启动最近使用的视景清单中的下一个开启的视景。

n         上一个视景:启动最近使用的视景清单中的上一个开启的视景。

Preferences(喜好设定)这个指令可以指出在使用工作台时的喜好设定。其中有各式各样的喜好设定可用来配置工作台及其视图的外观,以及用来自订在工作台中安装的所有工具的行为。

 

「Help」菜单

这个指令提供有关使用工作台的说明。

 

 

下面是一些快捷键的用法:

下面是一些快捷键的用法:

名称功能
Welcome(欢迎使用)这个指令会开启欢迎使用内容。
Help Contents(说明内容)这个指令显示说明视图。说明视图含有说明书籍、主题,以及与工作台和已安装特性的相关信息。
Tips and Tricks(要诀和技巧)这个指令会开启可能尚未探索之有兴趣的生产力特性的清单。
Cheat Sheets(提要)这个指令会开启选取提要的对话框。
Software Updates(软件更新)这个指令群组可以更新产品以及下载及安装新特性。
About Eclipse Platform(关于Eclipse平台)这个指令显示产品、已安装特性及可用外挂程序的相关信息。 

 

 

 

快捷键的用法请看下篇文章:

Nexus3.x搭建Maven私服环境

Maven介绍

Apache Maven是一个创新的软件项目管理和综合工具。
Maven提供了一个基于项目对象模型(POM)文件的新概念来管理项目的构建,可以从一个中心资料片管理项目构建,报告和文件。
Maven最强大的功能就是能够自动下载项目依赖库。
Maven提供了开发人员构建一个完整的生命周期框架。开发团队可以自动完成项目的基础工具建设,Maven使用标准的目录结构和默认构建生命周期。
在多个开发团队环境时,Maven可以设置按标准在非常短的时间里完成配置工作。由于大部分项目的设置都很简单,并且可重复使用,Maven让开发人员的工作更轻松,同时创建报表,检查,构建和测试自动化设置。
Maven项目的结构和内容在一个XML文件中声明,pom.xml 项目对象模型(POM),这是整个Maven系统的基本单元。

Maven提供了开发人员的方式来管理:
1)Builds
2)Documentation
3)Reporting
4)Dependencies
5)SCMs
6)Releases
7)Distribution
8)mailing list
概括地说,Maven简化和标准化项目建设过程。处理编译,分配,文档,团队协作和其他任务的无缝连接。
Maven增加可重用性并负责建立相关的任务。
Maven最初设计,是以简化Jakarta Turbine项目的建设。在几个项目,每个项目包含了不同的Ant构建文件。 JAR检查到CVS。
Apache组织开发Maven可以建立多个项目,发布项目信息,项目部署,在几个项目中JAR文件提供团队合作和帮助。

Maven主要目标是提供给开发人员:
1)项目是可重复使用,易维护,更容易理解的一个综合模型。
2)插件或交互的工具,这种声明性的模式。

私服介绍
私服是指私有服务器,是架设在局域网的一种特殊的远程仓库,目的是代理远程仓库及部署第三方构建。有了私服之后,当 Maven 需要下载构件时,直接请求私服,私服上存在则下载到本地仓库;否则,私服请求外部的远程仓库,将构件下载到私服,再提供给本地仓库下载。

Nexus介绍
Nexus是一个强大的Maven仓库管理器,它极大地简化了本地内部仓库的维护和外部仓库的访问。
如果使用了公共的Maven仓库服务器,可以从Maven中央仓库下载所需要的构件(Artifact),但这通常不是一个好的做法。
正常做法是在本地架设一个Maven仓库服务器,即利用Nexus私服可以只在一个地方就能够完全控制访问和部署在你所维护仓库中的每个Artifact。
Nexus在代理远程仓库的同时维护本地仓库,以降低中央仓库的负荷,节省外网带宽和时间,Nexus私服就可以满足这样的需要。
Nexus是一套“开箱即用”的系统不需要数据库,它使用文件系统加Lucene来组织数据。
Nexus使用ExtJS来开发界面,利用Restlet来提供完整的REST APIs,通过m2eclipse与Eclipse集成使用。
Nexus支持WebDAV与LDAP安全身份认证。
Nexus还提供了强大的仓库管理功能,构件搜索功能,它基于REST,友好的UI是一个extjs的REST客户端,它占用较少的内存,基于简单文件系统而非数据库。

为什么要构建Nexus私服?
如果没有Nexus私服,我们所需的所有构件都需要通过maven的中央仓库和第三方的Maven仓库下载到本地,而一个团队中的所有人都重复的从maven仓库下载构件无疑加大了仓库的负载和浪费了外网带宽,如果网速慢的话,还会影响项目的进程。很多情况下项目的开发都是在内网进行的,连接不到maven仓库怎么办呢?开发的公共构件怎么让其它项目使用?这个时候我们不得不为自己的团队搭建属于自己的maven私服,这样既节省了网络带宽也会加速项目搭建的进程,当然前提条件就是你的私服中拥有项目所需的所有构件。

总之,在本地构建nexus私服的好处有:
1)加速构建;
2)节省带宽;
3)节省中央maven仓库的带宽;
4)稳定(应付一旦中央服务器出问题的情况);
5)控制和审计;
6)能够部署第三方构件;
7)可以建立本地内部仓库;
8)可以建立公共仓库
这些优点使得Nexus日趋成为最流行的Maven仓库管理器。

Maven的安装
下载地址:http://maven.apache.org/download.cgi
提前在服务器上安装jdk环境(参考:Centos中yum方式安装java)
[root@master-node ~]# cd /usr/local/src/
[root@master-node src]# wget http://mirrors.hust.edu.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz
[root@master-node src]# tar -zvxf apache-maven-3.3.9-bin.tar.gz
[root@master-node src]# mv apache-maven-3.3.9 /usr/local/maven

接着配置系统环境变量,在/etc/profile文件底部添加如下内容:
[root@master-node src]# java -version
openjdk version "1.8.0_111"
OpenJDK Runtime Environment (build 1.8.0_111-b15)
OpenJDK 64-Bit Server VM (build 25.111-b15, mixed mode)
[root@master-node src]# vim /etc/profile
.....
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk                                //java的环境变量设置
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$PATH:$JAVA_HOME/bin

export MAVEN_HOME=/usr/local/maven                                                 //maven的环境变量设置
export PATH=$PATH:$MAVEN_HOME/bin
[root@master-node src]# source /etc/profile

最后验证是否安装成功,出现如下信息,说明安装成功
[root@master-node src]# mvn --version                   # 最好按照java jdk
Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00)
Maven home: /usr/local/maven
Java version: 1.8.0_111, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.111-2.b15.el7_3.x86_64/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-327.el7.x86_64", arch: "amd64", family: "unix"

Nexus安装
Nexus的安装有两种实现方式:
1)war包安装方式
下载地址:https://sonatype-download.global.ssl.fastly.net/nexus/oss/nexus-2.14.2-01.war
直接将war包放在tomcat的根目录下,启动tomcat就可以用了

2)源码安装方式(之前在用的是2.14.4版本,这里是新版本)
下载地址:https://www.sonatype.com/download-oss-sonatype           (云盘下载:http://pan.baidu.com/s/1miKFm5a)
[root@master-node ~]# cd /usr/local/src/
[root@master-node src]# wget https://sonatype-download.global.ssl.fastly.net/nexus/3/nexus-3.2.0-01-unix.tar.gz
[root@master-node src]# tar -zvxf nexus-3.2.0-01-unix.tar.gz
[root@master-node src]# mv nexus-3.2.0-01 /usr/local/nexus

启动nexus(默认端口是8081)
[root@master-node src]# /usr/local/nexus/bin/nexus
WARNING: ************************************************************
WARNING: Detected execution as "root" user. This is NOT recommended!
WARNING: ************************************************************
Usage: /usr/local/nexus/bin/nexus {start|stop|run|run-redirect|status|restart|force-reload}
[root@master-node src]# /usr/local/nexus/bin/nexus start
WARNING: ************************************************************
WARNING: Detected execution as "root" user. This is NOT recommended!
WARNING: ************************************************************
Starting nexus
上面在启动过程中出现告警:不推荐使用root用户启动。这个告警不影响nexus的正常访问和使用。
去掉上面WARNING的办法:
[root@master-node src]# vim /etc/profile
......
export RUN_AS_USER=root
[root@master-node src]# source /etc/profile
[root@master-node src]# lsof -i:8081            //nexus服务启动成功后,需要稍等一段时间,8081端口才起来
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 1486 root 859u IPv4 23504303 0t0 TCP *:tproxy (LISTEN)

在部署机上的iptables里打开8081端口
[root@master-node src]# vim /etc/sysconfig/iptables
....
-A INPUT -p tcp -m state --state NEW -m tcp --dport 8081 -j ACCEPT
[root@master-node src]# /etc/init.d/iptables restart

访问nexus,即http://localhost:8081    (如果出现404,就访问http://localhost:8081/nexus)

出现上述页面,说明配置nexus成功!
点击右上角“Log in”,
输入默认用户名(admin)和默认密码(admin123)登录

可以点击上面的“设置”图标,在“设置”里可以添加用户、角色,对接LDAP等的设置,如下:

可以在“管理”里查看nexus的系统信息

注意下面几点说明:
1.component name的一些说明:
1)maven-central:maven中央库,默认从https://repo1.maven.org/maven2/拉取jar
2)maven-releases:私库发行版jar
3)maven-snapshots:私库快照(调试版本)jar
4)maven-public:仓库分组,把上面三个仓库组合在一起对外提供服务,在本地maven基础配置settings.xml中使用。
2.Nexus默认的仓库类型有以下四种:
1)group(仓库组类型):又叫组仓库,用于方便开发人员自己设定的仓库;
2)hosted(宿主类型):内部项目的发布仓库(内部开发人员,发布上去存放的仓库);
3)proxy(代理类型):从远程中央仓库中寻找数据的仓库(可以点击对应的仓库的Configuration页签下Remote Storage Location属性的值即被代理的远程仓库的路径);
4)virtual(虚拟类型):虚拟仓库(这个基本用不到,重点关注上面三个仓库的使用);
3.Policy(策略):表示该仓库为发布(Release)版本仓库还是快照(Snapshot)版本仓库;
4.Public Repositories下的仓库
1)3rd party: 无法从公共仓库获得的第三方发布版本的构件仓库,即第三方依赖的仓库,这个数据通常是由内部人员自行下载之后发布上去;
2)Apache Snapshots: 用了代理ApacheMaven仓库快照版本的构件仓库
3)Central: 用来代理maven中央仓库中发布版本构件的仓库
4)Central M1 shadow: 用于提供中央仓库中M1格式的发布版本的构件镜像仓库
5)Codehaus Snapshots: 用来代理CodehausMaven 仓库的快照版本构件的仓库
6)Releases: 内部的模块中release模块的发布仓库,用来部署管理内部的发布版本构件的宿主类型仓库;release是发布版本;
7)Snapshots:发布内部的SNAPSHOT模块的仓库,用来部署管理内部的快照版本构件的宿主类型仓库;snapshots是快照版本,也就是不稳定版本
所以自定义构建的仓库组代理仓库的顺序为:Releases,Snapshots,3rd party,Central。也可以使用oschina放到Central前面,下载包会更快。
5.Nexus默认的端口是8081,可以在etc/nexus-default.properties配置中修改。
6.Nexus默认的用户名密码是admin/admin123
7.当遇到奇怪问题时,重启nexus,重启后web界面要1分钟左右后才能访问。
8.Nexus的工作目录是sonatype-work(路径一般在nexus同级目录下)
[root@master-node local]# pwd
/usr/local
[root@master-node local]# ls nexus/
bin deploy etc lib LICENSE.txt NOTICE.txt public system
[root@master-node local]# ls sonatype-work/
nexus3
[root@master-node local]# ls sonatype-work/nexus3/
backup blobs cache db elasticsearch etc generated-bundles health-check instances keystores lock log orient port tmp

Nexus仓库分类的概念:
1)Maven可直接从宿主仓库下载构件,也可以从代理仓库下载构件,而代理仓库间接的从远程仓库下载并缓存构件
2)为了方便,Maven可以从仓库组下载构件,而仓库组并没有时间的内容(下图中用虚线表示,它会转向包含的宿主仓库或者代理仓库获得实际构件的内容).

Nexus的web界面功能介绍

1.Browse Server Content

1.1  Search
这个就是类似Maven仓库上的搜索功能,就是从私服上查找是否有哪些包。
注意:
1)在Search这级是支持模糊搜索的,如图所示:

2)如果进入具体的目录,好像不支持模糊搜索,如图所示:

1.2  Browse

1)Assets
这是能看到所有的资源,包含Jar,已经对Jar的一些描述信息。
2)Components
这里只能看到Jar包。

2.Server Adminstration And configuration

看到这个选项的前提是要进行登录的,如上面已经介绍登陆方法,右上角点击“Sign In”的登录按钮,输入admin/admin123,登录成功之后,即可看到此功能,如图所示:

2.1 Blob Stores
文件存储的地方,创建一个目录的话,对应文件系统的一个目录,如图所示:

2.2 Repositories

1)Proxy
这里就是代理的意思,代理中央Maven仓库,当PC访问中央库的时候,先通过Proxy下载到Nexus仓库,然后再从Nexus仓库下载到PC本地。
这样的优势只要其中一个人从中央库下来了,以后大家都是从Nexus私服上进行下来,私服一般部署在内网,这样大大节约的宽带。
创建Proxy的具体步骤
1--点击“Create Repositories”按钮

2--选择要创建的类型

3--填写详细信息
Name:就是为代理起个名字
Remote Storage: 代理的地址,Maven的地址为: https://repo1.maven.org/maven2/
Blob Store: 选择代理下载包的存放路径

2)Hosted
Hosted是宿主机的意思,就是怎么把第三方的Jar放到私服上。
Hosted有三种方式,Releases、SNAPSHOT、Mixed
Releases: 一般是已经发布的Jar包
Snapshot: 未发布的版本
Mixed:混合的
Hosted的创建和Proxy是一致的,具体步骤和上面基本一致。如下:

注意事项:
Deployment Pollcy: 需要把策略改成“Allow redeploy”。

3)Group
能把两个仓库合成一个仓库来使用,目前没使用过,所以没做详细的研究。

2.3 Security
这里主要是用户、角色、权限的配置(上面已经提到了在这里添加用户和角色等)

2.4 Support
包含日志及数据分析。

2.5 System
主要是邮件服务器,调度的设置地方
这部分主要讲怎么和Maven做集成,集成的方式主要分以下种情况:代理中央仓库、Snapshot包的管理、Release包的管理、第三方Jar上传到Nexus上。

代理中央仓库
只要在PMO文件中配置私服的地址(比如http://192.168.1.14:8081)即可,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<repositories>
        <repository>
            <id>maven-central</id>
            <name>maven-central</name>
            <url>http://192.168.1.14:8081/repository/maven-central/</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>

Snapshot包的管理
1)修改Maven的settings.xml文件,加入认证机制

1
2
3
4
5
6
<servers>
<server>
      <id>nexus</id>
      <username>admin</username>
      <password>admin123</password>
     </server>

2)修改工程的Pom文件

1
2
3
4
5
6
7
8
9
10
11
12
<distributionManagement>
        <snapshotRepository>
            <id>nexus</id>
            <name>Nexus Snapshot</name>
            <url>http://192.168.1.14:8081/repository/maven-snapshots/</url>
        </snapshotRepository>
        <site>
            <id>nexus</id>
            <name>Nexus Sites</name>
            <url>dav:http://192.168.1.14:8081/repository/maven-snapshots/</url>
        </site>
    </distributionManagement>

注意事项:

上面修改的Pom文件如截图中的名字要跟/usr/local/maven/conf/settings.xml文件中的名字一定要对应上。

3)上传到Nexus上

1--项目编译成的jar是Snapshot(POM文件的头部)

1
2
3
4
<groupId>com.woasis</groupId>
<artifactId>test-nexus</artifactId>
<version>1.0.0-<span style="color: #ff0000;">SHAPSHOT</span></version>
<packaging>jar</packaging>

2--使用mvn deploy命令运行即可(运行结果在此略过)

3--因为Snapshot是快照版本,默认他每次会把Jar加一个时间戳,做为历史备份版本。

Releases包的管理

1)与Snapshot大同小异,只是上传到私服上的Jar包不会自动带时间戳
2)与Snapshot配置不同的地方,就是工程的PMO文件,加入repository配置

1
2
3
4
5
6
<distributionManagement>
        <repository>
            <id>nexus</id>
            <name>Nexus Snapshot</name>
            <url>http://192.168.1.14:8081/repository/maven-releases/</url>
        </repository>

3)打包的时候需要把Snapshot去掉

1
2
3
4
<groupId>com.woasis</groupId>
    <artifactId>test-nexus</artifactId>
    <version>1.0.0</version>
<packaging>jar</packaging>

第三方Jar上传到Nexus

[root@master-node src]# mvn deploy:deploy-file -DgroupId=org.jasig.cas.client -DartifactId=cas-client-core -Dversion=3.1.3 -Dpackag
注意事项:
-DrepositoryId=nexus 对应的就是Maven中settings.xml的认证配的名字。

来源:https://www.cnblogs.com/kevingrace/p/6201984.html

随着Nexus Repository Manager OSS 3的发布(目前更新至3.2.1),虽然目前还是Nexus 2和Nexus 3并行的状态,但是Nexus 3在很多方面已经显现出很大的优势,等到Nexus 3在Maven方面的支持稳定之后就应该是Nexus 3的全面使用之时。

理由1:安装更加简单

安装变得更加方便,详细可以参看如下链接。

安装参照URLhttp://books.sonatype.com/nexus-book/3.0/reference/install.html?__hstc=239247836.f7854f6edce31b386d0c10d0555205f0.1487887540518.1487887540518.1489490179025.2&__hssc=239247836.3.1489490179025&__hsfp=285730640

理由2:官方Docker镜像

使用官方Docker镜像使得更加容易的导入Repository Manager.

项目详细
官方镜像https://hub.docker.com/r/sonatype/nexus3/
Easypack镜像https://github.com/liumiaocn/easypack/tree/master/containers/standard/nexus

理由3:REST API

使用Nexus提供的API使得集成更容易进行。

项目详细
APIhttp://books.sonatype.com/nexus-book/3.0/reference/scripting.html?__hstc=239247836.f7854f6edce31b386d0c10d0555205f0.1487887540518.1487887540518.1489490179025.2&__hssc=239247836.3.1489490179025&__hsfp=285730640

理由4:用户界面

同Nexus 2相比,Nexus 3的界面增加了一些现代的元素,多多少少使人稍稍有些眼前一亮的感觉,虽说跟Artifactory还是明显有些差距,但是性价比也完全不在一个级别上。

理由5:性能

据说性能依然很好,像其标榜的那样。

理由6:Docker 私库

现在可以用Nexus 来管理Docker 私库了,统一管理,是不是很具有吸引力。

项目详细
参照内容https://www.sonatype.com/concepts-benefits-repo-management?__hstc=239247836.f7854f6edce31b386d0c10d0555205f0.1487887540518.1487887540518.1489490179025.2&__hssc=239247836.3.1489490179025&__hsfp=285730640

理由7:npm与bower

支持npm和bower的package管理,对前端工程师造成了很大的诱惑,目前此项优势继续保持中。

理由8:Raw repositories

在Nexus 3中支持一种新的方式:raw repositories。利用这种方式,任何文件都可以像Maven管理对象文件那样被管理起来,对所有的artifacts进行统一集成管理。

理由9:NuGet repositories

支持NuGet repositories,对于.Net开发者来说,这无疑是一个福音。

理由10:支持检索

对于Nexus所支持的任何类型都支持检索功能,这使得无论任何情况下我们都能利用这些功能进行精确定位。

理由11:支持浏览

支持对其仓库的内容进行浏览,非常方便。

理由12:检查机制

对Maven/NuGet/npm仓库,支持安全以及license的检查,使得使用起来更无后顾之忧。

总结

Nexus物美价廉,又提供功能全面的oss版,加之支持种类众多的倚赖管理,又可以统一管理docker镜像,界面也在慢慢好看起来,这些不禁给了我们对Nexus 3更多的期待。

参照文档

http://www.sonatype.org/nexus/2016/04/13/a-dozen-reasons-why-nexus-repository-3-0-kicks-ass/

Nexus3.0.0+Maven的使用

1、Nexus介绍

     Nexus是一个强大的Maven仓库管理器,它极大地简化了自己内部仓库的维护和外部仓库的访问。利用Nexus你可以只在一个地方就能够完全控制访问 和部署在你所维护仓库中的每个Artifact。Nexus是一套“开箱即用”的系统不需要数据库,它使用文件系统加Lucene来组织数据。Nexus 使用ExtJS来开发界面,利用Restlet来提供完整的REST APIs,通过m2eclipse与Eclipse集成使用。Nexus支持WebDAV与LDAP安全身份认证。

2、安装部署

2.1  下载地址

http://www.sonatype.com/download-oss-sonatype

2.2  安装

1、  解压安装包

tar –zxvf nexus-3.0.0-03-unix.tar.gz

2、  修改Jdk路径

Nexus3.0.0的版本需要JDK1.8的版本

a)、cd /data/nexus-3.0.0-03/bin

b)、vi nexus

c)、添加JDK配置

INSTALL4J_JAVA_HOME_OVERRIDE=/data/jdk1.8.0_91

3、  修改使用的用户

a)、vi nexus.rc

b)、添加配置

run_as_user=root

4、  启动nexus服务

./ nexus

2.3 成功验证

访问http://XXX:8081,看是否能跳转到Nexus页面

因为Nexus3.0.0与Nexus2.X系列的差别很大,所以本章节我大概讲解下Nexus3.0.0的功能使用。

1、功能介绍

1.1  Browse Server Content

1.1.1  Search

这个就是类似Maven仓库上的搜索功能,就是从私服上查找是否有哪些包。

注意:

1、在Search这级是支持模糊搜索的,如图所示:

2、如果进入具体的目录,好像不支持模糊搜索,如图所示:

1.1.2  Browse

1.1.1.1 Assets

这是能看到所有的资源,包含Jar,已经对Jar的一些描述信息。

1.1.1.1 Components

这里只能看到Jar包。

2.1  Server Adminstration And configuration

看到这选项是要进行登录的,在右上角点击“Sign In”的登录按钮,输入admin/admin123,登录成功之后,即可看到此功能,如图所示:

 

2.1.1  Repository

2.1.1.1 Blob Stores

文件存储的地方,创建一个目录的话,对应文件系统的一个目录,如图所示:

2.1.1.1 Repositories

2.1.1.1.1Proxy

这里就是代理的意思,代理中央Maven仓库,当PC访问中央库的时候,先通过Proxy下载到Nexus仓库,然后再从Nexus仓库下载到PC本地。

这样的优势只要其中一个人从中央库下来了,以后大家都是从Nexus私服上进行下来,私服一般部署在内网,这样大大节约的宽带。

创建Proxy的具体步骤

1、  点击“Create Repositories”按钮

2、  选择要创建的类型

3、  填写详细信息

Name:就是为代理起个名字

Remote Storage: 代理的地址,Maven的地址为: https://repo1.maven.org/maven2/

Blob Store: 选择代理下载包的存放路径

2.1.1.1.2Hosted

Hosted是宿主机的意思,就是怎么把第三方的Jar放到私服上。

Hosted有三种方式,Releases、SNAPSHOT、Mixed

Releases: 一般是已经发布的Jar包

Snapshot: 未发布的版本

Mixed:混合的

Hosted的创建和Proxy是一致的,具体步骤可参考2.1.1.1.1

注意事项:

Deployment Pollcy: 我们需要把策略改成“Allow redeploy”。

2.1.1.1.3Group

能把两个仓库合成一个仓库来使用,目前没使用过,所以没做详细的研究。

2.1.2  Security

这里主要是用户、角色、权限的配置

2.1.3  Support

包含日志及数据分析。

2.1.4  System

主要是邮件服务器,调度的设置地方

这章主要讲怎么和Maven做集成,集成的方式主要分以下种情况:代理中央仓库、Snapshot包的管理、Release包的管理、第三方Jar上传到Nexus上

1  代理中央仓库

只要在PMO文件中配置私服的地址即可,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<repositories>
        <repository>
            <id>maven-central</id>
            <name>maven-central</name>
            <url>http://10.0.1.42:8081/repository/maven-central/</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>

2  Snapshot包的管理

1、  修改Maven的settings.xml文件,加入认证机制

1
2
3
4
5
6
<servers>
<server>
      <id>nexus</id>
      <username>admin</username>
      <password>admin123</password>
     </server>

2、  修改工程的Pom文件

1
2
3
4
5
6
7
8
9
10
11
12
<distributionManagement>
        <snapshotRepository>
            <id>nexus</id>
            <name>Nexus Snapshot</name>
            <url>http://10.0.1.42:8081/repository/maven-snapshots/</url>
        </snapshotRepository>
        <site>
            <id>nexus</id>
            <name>Nexus Sites</name>
            <url>dav:http://10.0.1.42:8081/repository/maven-snapshots/</url>
        </site>
    </distributionManagement>

注意事项:

截图中的名字要跟apache-maven-3.0.5-nexus\conf\settings.xml的名字一定要对应上。

3、  上传到Nexus上

a)、项目编译成的jar是Snapshot(POM文件的头部)

1
2
3
4
<groupId>com.woasis</groupId>
<artifactId>test-nexus</artifactId>
<version>1.0.0-SHAPSHOT</version>
<packaging>jar</packaging>

b)、使用mvn deploy 即可,运行结果如图所示:

c、因为Snapshot是快照版本,默认他每次会把Jar加一个时间戳,做为历史备份版本。

3  Releases包的管理

a)、与Snapshot大同小异,只是上传到私服上的Jar包不会自动带时间戳,如图所示:

b)、与Snapshot配置不同的地方,就是工程的PMO文件,加入repository配置

1
2
3
4
5
6
<distributionManagement>
        <repository>
            <id>nexus</id>
            <name>Nexus Snapshot</name>
            <url>http://10.0.1.42:8081/repository/maven-releases/</url>
        </repository>

c)、打包的时候需要把Snapshot去掉,如图所示:

1
2
3
4
<groupId>com.woasis</groupId>
    <artifactId>test-nexus</artifactId>
    <version>1.0.0</version>
<packaging>jar</packaging>

4  第三方Jar上传到Nexus

1
mvn deploy:deploy-file -DgroupId=org.jasig.cas.client -DartifactId=cas-client-core -Dversion=3.1.3 -Dpackaging=jar -DrepositoryId=nexus -Dfile=D:\cas-client-core-3.1.3.jar -Durl=http://10.0.1.42:8081/repository/maven-releases/-DgeneratePom=false

注意事项:

-DrepositoryId=nexus  对应的就是Maven中settings.xml的认证配的名字。

来源: https://www.cnblogs.com/qq27271609/p/5497815.html