老系统接入rabbitMq

1.rabbitMQ基础知识:https://www.rabbitmq.com

2. rabbitMQ安装:

2.1. mq下载地址:https://www.rabbitmq.com/download.html

2.2. Erlang下载地址:http://www.erlang.org/downloads

2.3.后台管理安装:rabbitmq-plugins enable rabbitmq_management

2.4.默认后台路径:http://127.0.0.1:15672/ 

2.5.默认用户帐号密码:guest/guest

3.与系统整合

3.1.系统用的是SpringMVC 4.1.X ,故POM文件选型如下:

        <dependency>
			<groupId>com.rabbitmq</groupId>
			<artifactId>amqp-client</artifactId>
			<version>5.6.0</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-rabbit</artifactId>
			<version>1.4.5.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-amqp</artifactId>
			<version>1.4.5.RELEASE</version>
		</dependency>

3.2.JAVA配置

3.2.1 基础配置:

@Configuration
@PropertySources({
		@PropertySource(value = "classpath:properties/ease_rabbit.properties", ignoreResourceNotFound = true) })
public class RabbitConfig {

	@Value("${rabbit.host}")
	protected String host;

	@Value("${rabbit.port}")
	protected Integer port;

	@Value("${rabbit.username}")
	protected String username;

	@Value("${rabbit.password}")
	protected String password;

	@Value("${rabbit.virtualHost}")
	protected String virtualHost;
	
	@Value("${rabbit.channelCacheSize}")
	protected Integer channelCacheSize;
	
	@Value("${rabbit.connectionTimeout}")
	protected Integer connectionTimeout;
	
	@Value("${rabbit.retryTime}")
	protected Integer retryTime;
	
	@Value("${rabbit.exception.retryTime}")
	protected Integer excepRetryTime;

	@Value("${rabbit.exchangeType}")
	protected String exchangeType;

	@Value("${rabbit.exchange}")
	protected String exchange;

	@Value("${rabbit.tandem.queue}")
	protected String tandemQueue;

	@Value("${rabbit.tandem.routingKey}")
	protected String tandemRoutingKey;

	@Value("${rabbit.tandem.prefetchCount}")
	protected Integer tandemPrefetchCount;

	@Value("${rabbit.tandem.concurrentConsumers}")
	protected Integer tandemConcurrentConsumers;
	
	@Value("${rabbit.tandem.callback.queue}")
	protected String tandemCallbackQueue;
	
	@Value("${rabbit.tandem.callback.routingKey}")
	protected String tandemCallbackRoutingKey;

	@Value("${rabbit.tandem.callback.prefetchCount}")
	protected Integer tandemCallbackPrefetchCount;
	
	@Value("${rabbit.tandem.callback.concurrentConsumers}")
	protected Integer tandemCallbackConcurrentConsumers;
	
	
	// 配置交换机类型
	@Bean(name = "Exchange")
	public AbstractExchange Exchange() throws IOException {
		if (exchangeType.contains("F")) {
			FanoutExchange fanoutExchange = new FanoutExchange(exchange, true, false);
			System.out.println("ProducerExchange-Fanout 生产者:广播路由");
			return fanoutExchange;
		} else if (exchangeType.contains("T")) {
			TopicExchange topicExchange = new TopicExchange(exchange, true, false);
			System.out.println("ProducerExchange-Topic 生产者:多路广播路由");
			return topicExchange;
		} else {
			DirectExchange directExchange = new DirectExchange(exchange, true, false);
			System.out.println("ProducerExchange-Direct 生产者:直接路由");
			return directExchange;
		}
	}
	
	
	//Queue
	@Bean(name = "TandemQueue")
	public Queue TandemQueue() throws IOException {
		Queue queue = new Queue(tandemQueue, true, false, false);
		return queue;
	}
	
	 //Queue绑定
	@Bean(name = "TandemBinding")
	public Binding TandemBinding() {
		Binding binding = new Binding(tandemQueue, DestinationType.QUEUE, exchange, tandemRoutingKey, null);
		return binding;
	}
	

}

 3.2.2 生产者配置:


@Configuration
public class RabbitProducerConfig extends RabbitConfig{

	// 配置生产者ConnectionFactory,配置基础连接信息
	@Bean(name = "ConnectionFactoryProducer")
	public ConnectionFactory ConnectionFactoryProducer() throws IOException {
		CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host);
		connectionFactory.setPort(port);
		connectionFactory.setUsername(username);
		connectionFactory.setPassword(password);
		connectionFactory.setVirtualHost(virtualHost);
		// 网络波动相关微调
		connectionFactory.setChannelCacheSize(channelCacheSize);
		connectionFactory.setConnectionTimeout(connectionTimeout);
		// ack返回操作 消息发送确认
		connectionFactory.setPublisherConfirms(true);
		return connectionFactory;
	}
	
	@Bean
	public RabbitAdmin RabbitAdminProducer() throws IOException {
		return new RabbitAdmin(ConnectionFactoryProducer());
	}
	
	// 配置生产者消息转换器
	@Bean(name = "jsonMessageConverter")
	public AbstractMessageConverter ProducerMessageConverter() {
		FastJsonMessageConverter jsonMessageConverter = new FastJsonMessageConverter();
		return jsonMessageConverter;
	}
	
	// 生产者发送确认
	@Bean(name = "ProducerConfirm")
	public ProducerConfirm ProducerConfirm(){
		ProducerConfirm producerConfirm = new ProducerConfirm(retryTime);
		return producerConfirm;
	}

}
@Configuration
public class TandemProducerConfig extends RabbitProducerConfig{

	// 配置生产者Template
	@Bean(name = "TandemProducerRabbitTemplate")
	public RabbitTemplate TandemProducerRabbitTemplate() throws IOException {
		RabbitTemplate rabbitTemplate = new RabbitTemplate();
		rabbitTemplate.setConnectionFactory(ConnectionFactoryProducer());
		rabbitTemplate.setExchange(exchange);
		// 配置生产者信息转换器,封装发送信息的格式
		rabbitTemplate.setMessageConverter(ProducerMessageConverter());
		// ack返回操作
		rabbitTemplate.setMandatory(true);
		rabbitTemplate.setConfirmCallback(ProducerConfirm());
		
		//配置重试机制
		//指定延迟的倍数,比如delay=5000l,multiplier=2时,第一次重试为5秒后,第二次为10秒,第三次为20秒
        RetryTemplate retryTemplate = new RetryTemplate();
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(500);
        backOffPolicy.setMultiplier(2.0); 
        backOffPolicy.setMaxInterval(10000);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        rabbitTemplate.setRetryTemplate(retryTemplate);
		return rabbitTemplate;
	}
	
	// 实例化生产者代码,配置生产者要投递信息的RoutingKey
	@Bean(name = "TandemProducer")
	public TandemProducer TandemProducer() throws Exception {
		TandemProducer producer = new TandemProducer(tandemRoutingKey,excepRetryTime,TandemProducerRabbitTemplate());// t.test
		return producer;
	}

}

 

 3.2.3 消费者配置:

@Configuration
public class RabbitConsumerConfig extends RabbitConfig {

	@Bean(name = "ConnectionFactoryConsumer")
	public ConnectionFactory ConnectionFactoryConsumer() throws IOException {
		CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host);
		connectionFactory.setPort(port);
		connectionFactory.setUsername(username);
		connectionFactory.setPassword(password);
		connectionFactory.setVirtualHost(virtualHost);
		// 网络波动相关微调
		connectionFactory.setChannelCacheSize(channelCacheSize);
		connectionFactory.setConnectionTimeout(connectionTimeout);
		return connectionFactory;
	}

	@Bean
	public RabbitAdmin RabbitAdminConsumer() throws IOException {
		return new RabbitAdmin(ConnectionFactoryConsumer());
	}
}
@Configuration
public class TandemConsumerConfig extends RabbitConsumerConfig{

	//配置消费者Template
	@Bean(name = "TandemConsumerRabbitTemplate")
	public RabbitTemplate TandemConsumerRabbitTemplate() throws IOException {
		RabbitTemplate rabbitTemplate = new RabbitTemplate(ConnectionFactoryConsumer());
		rabbitTemplate.setExchange(exchange);
		
		
		//配置重试机制
		//指定延迟的倍数,比如delay=5000l,multiplier=2时,第一次重试为5秒后,第二次为10秒,第三次为20秒
        RetryTemplate retryTemplate = new RetryTemplate();
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(500);
        backOffPolicy.setMultiplier(2.0); 
        backOffPolicy.setMaxInterval(10000);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        rabbitTemplate.setRetryTemplate(retryTemplate);
        
		return rabbitTemplate;
	}

	//实例化消费者代码
	@Bean(name = "TandemConsumer")
	public TandemConsumer TandemConsumer() {
		TandemConsumer consumer = new TandemConsumer();
		return consumer;
	}
	
	//配置监听模式,当有消息到达时会通知监听在对应的队列上的监听对象
	@Bean(name = "TandemListenerContainer")
	public AbstractMessageListenerContainer TandemListenerContainer() throws IOException {
		SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer();
		simpleMessageListenerContainer.setConnectionFactory(ConnectionFactoryConsumer());
		simpleMessageListenerContainer.setQueueNames(tandemQueue);
		if(tandemPrefetchCount!=null&&tandemPrefetchCount.intValue()>1) {
			simpleMessageListenerContainer.setPrefetchCount(tandemPrefetchCount);
		}
		simpleMessageListenerContainer.setConcurrentConsumers(tandemConcurrentConsumers);// 启动的线程数目
		simpleMessageListenerContainer.setMessageListener(TandemConsumer());
		
		// 手动确认消息返回
		simpleMessageListenerContainer.setAcknowledgeMode(AcknowledgeMode.MANUAL);
		return simpleMessageListenerContainer;
	}

}

 

4.共同API:

4.1.生产者API:


public class TandemProducer {

	private static Logger logger = LoggerFactory.getLogger(TandemProducer.class);

	private RabbitTemplate rabbitTemplate;

	@Autowired
	IFlowRabbitIdempotencyService flowRabbitIdempotencyService;

	private String routingKey;
	
	private int excepRetryTime;

	public TandemProducer() {
	}
	
	public TandemProducer(String routingKey, Integer excepRetryTime,RabbitTemplate rabbitTemplate) {
		this.routingKey = routingKey;
		this.excepRetryTime = excepRetryTime.intValue();
		this.rabbitTemplate = rabbitTemplate;
	}

	/**
	 * 发送mq
	 * 
	 * @param message
	 */
	public void sendDataToQueue(MessageInfo message) {
		
		long start = System.currentTimeMillis();
		logger.info("串接请求发送开始:{}", message);
		int retriedCnt = retrySend("", "", message, excepRetryTime, 0, false);
		logger.info("串接请求发送结束,消耗时间:{},重试次数:{}", System.currentTimeMillis()-start,retriedCnt);
	}
	
	/**
	 * 发送消息 实现重发机制
	 * @param rst
	 * @param id
	 * @param message
	 * @param retryCnt
	 * @param retriedCnt
	 * @param isSuccess
	 * @return
	 */
	private int retrySend(String rst, String id, MessageInfo message, int retryCnt, int retriedCnt, boolean isSuccess) {

		if (retriedCnt >= retryCnt || isSuccess) {
			return retriedCnt;
		}
		
		try {
			Thread.sleep(retriedCnt * 1000);
			retriedCnt++;

			if (RabbitConst.SUCCESS_STR.equals(rst)) {
				String[] idInfo = id.split(RabbitConst.DASH);
				if (idInfo == null || idInfo.length != 2) {
					logger.error("correlationDataId error :{}", id);
					return retriedCnt;
				}
				String type = idInfo[0];
				String uuid = idInfo[1];

				reSendDataToQueue(type, id, message);
				isSuccess = true;
			} else {
				Map<String, String> rstMap = flowRabbitIdempotencyService.insertTandemSendIdem(message, message.getParentInsId());
				rst = rstMap.get("rst");
				id = rstMap.get("id");
				if (RabbitConst.SUCCESS_STR.equals(rst)) {
					logger.info("串接插入幂等表成功:{}", id);
					// 发送rabbitmq
					message.setCorrelationDataId(id);
					rabbitTemplate.convertAndSend(this.routingKey, message, new CorrelationData(id));
					isSuccess = true;
				} else {
					logger.error("串接插入幂等表失败:{}", message);
					throw new Exception("串接插入幂等表失败");
				}
			}
		} catch (Exception e) {
			logger.error("maybe Rabbit server down");
			logger.error(e.getMessage(), e);
			isSuccess = false;
		}

		return retrySend(rst, id, message, retryCnt, retriedCnt, isSuccess);
	}

	/**
	 * 发送失败重新发送
	 * @param type
	 * @param uuid
	 * @param message
	 */
	public void reSendDataToQueue(String type, String correlationDataId, MessageInfo message) {

		long start = System.currentTimeMillis();
		logger.info("rabbitMQ producer reSend. type:{},correlationDataId{}, message:{}",type,correlationDataId,message);
		//MessageInfo messageInfo = JsonUtil.fromJson(message, MessageInfo.class);
		rabbitTemplate.convertAndSend(this.routingKey, message, new CorrelationData(correlationDataId));
		logger.info("reSendDataToQueue, spend{}", System.currentTimeMillis()-start);

	}

	public String getRoutingKey() {
		return routingKey;
	}

	public void setRoutingKey(String routingKey) {
		this.routingKey = routingKey;
	}

	public int getExcepRetryTime() {
		return excepRetryTime;
	}

	public void setExcepRetryTime(int excepRetryTime) {
		this.excepRetryTime = excepRetryTime;
	}

}

4.2.消费者API:

public class TandemConsumer implements ChannelAwareMessageListener {

	private static Logger logger = LoggerFactory.getLogger(TandemConsumer.class);

	@Autowired
	IFlowRabbitIdempotencyService flowRabbitIdempotencyService;

	public void onMessage(Message message, Channel channel) {

		long start = System.currentTimeMillis();
		
		try {
			// 手动确认消息返回
			channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
			
			String data = new String(message.getBody());
			logger.info("串接请求处理开始:{}", data);
			MQData mqData = JsonUtil.fromJson(data, MQData.class);
			MessageInfo messageInfo = mqData.getData();
			String id = messageInfo.getCorrelationDataId();
			String[] idInfo = id.split(RabbitConst.DASH);
			
			if(idInfo==null||idInfo.length != 2) {
				logger.error("correlationDataId error :{}",id);
				return;
			}
			
			String type = idInfo[0];
			String uuid = idInfo[1];
			
			// 幂等验证
			int rst  = flowRabbitIdempotencyService.checkIdem(uuid);
			if(rst==1) {
                // dosomething

                // 处理结果 更新
				flowRabbitIdempotencyService.finishIdem(uuid, processInsId);
			}else {
				logger.info("此请求已处理或处理中,uuid:{}",uuid);
			}
			
		} catch (Exception e) {
			logger.info("流程串接监听失败:{}",e.getMessage());
			logger.error(e.getMessage(),e);
		}
		
		logger.info("串接请求处理结束,消耗时间:{}", System.currentTimeMillis()-start);

	}
}

5.ack确认:


public class ProducerConfirm implements ConfirmCallback {

	private static Logger logger = LoggerFactory.getLogger(ProducerConfirm.class);

	@Lazy
	@Autowired
	TandemProducer tandemProducer;

	@Autowired
	IFlowRabbitIdempotencyService flowRabbitIdempotencyService;

	private Integer retryTime;

	public ProducerConfirm(Integer retryTime) {
		this.retryTime = retryTime;
	}

	@Override
	public void confirm(CorrelationData correlationData, boolean ack, String cause) {
		if (!ack) {
			logger.error("send message failed,id:{} ", correlationData.toString());
			
			long start = System.currentTimeMillis();
			logger.info("串接请求重新发送开始:{}", correlationData.toString());
			
			String id = correlationData.getId();
			String[] idInfo = id.split(RabbitConst.DASH);
			
			if(idInfo==null||idInfo.length != 2) {
				logger.error("correlationDataId error :{}",correlationData.toString());
				return;
			}
			
			String type = idInfo[0];
			String uuid = idInfo[1];
			
			// 根据重试次数查询幂等bean
			FlowRabbitIdempotencyTandem tandem = flowRabbitIdempotencyService.getFlowRabbitIdempotencyTandem(uuid,retryTime);
			if (tandem == null) {
				logger.error("had over error retry times. correlationDataId:{},times:{},",correlationData.toString(),retryTime);
				return;
			}
			
			int retryCnt = tandem.getRetryCnt().intValue();
			
			if (!(retryCnt < retryTime)) {
				logger.error("had over error retry times. correlationDataId:{},times:{},",correlationData.toString(),retryTime);
				return;
			}
			
			try {
				Thread.sleep(retryCnt*100);
			} catch (InterruptedException e) {
				logger.error(e.getMessage(),e);
			}
			
			String msgJson = JsonUtil.toJson(tandem.getMessage());
			MessageInfo messageInfo = JsonUtil.fromJson(msgJson, MessageInfo.class);
				tandemProducer.reSendDataToQueue(type, id, messageInfo);
			
			flowRabbitIdempotencyService.updRetryCnt(uuid, retryTime);
			logger.info("串接请求重新发送结束:{},重试次数:{}", System.currentTimeMillis()-start,retryCnt);
		}
	}

	public Integer getRetryTime() {
		return retryTime;
	}

	public void setRetryTime(Integer retryTime) {
		this.retryTime = retryTime;
	}

}

6.总结:

6.1.选型需要尽量用比较新的而且维护比较久的,向下兼容本系统,想上使用新的rabbitmq

6.2.需要处理网络原因导致的发送失败和ack返回失败的重发

6.3.根据5.2的处理,会导致一个处理发送多条的情况,需要幂等验证处理,保证不重复处理

6.4.网络断了重连之后,如果生产者和消费者使用同一个connection的话会造成Queue阻塞,所有生产者和消费者的连接需要分开

6.5.生产者和消费者会分开发布,所以生产者的配置和消费者的配置得分开

参考文档:

百度,google

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值