简单使用Disruptor

1.定义事件与事件工厂
       事件(Event)就是通过 Disruptor 进行交换的数据类型。
       事件工厂(Event Factory)定义了如何实例化事件(Event),需要实现接口 com.lmax.disruptor.EventFactory<T>。Disruptor 通过EventFactory在 RingBuffer 中预创建 Event 的实例。一个 Event 实例实际上被用作一个“数据槽”,发布者发布前,先从RingBuffer 获得一个Event的实例,然后往Event 实例中填充数据,之后再发布到 RingBuffer 中,之后由Consumer 获得该 Event 实例并从中读取数据。
package com.zero.disruptor;

import com.lmax.disruptor.EventFactory;

public class SimpleEvent {
	private String value;

	public void setValue(String value) {
		this.value = value;
	}

	public String getValue() {
		return value;
	}

	public static final EventFactory<SimpleEvent> FACTORY = new EventFactory<SimpleEvent>() {
		@Override
		public SimpleEvent newInstance() {
			return new SimpleEvent();
		}
	};
}
2. 定义事件处理的具体实现
       通过实现接口 com.lmax.disruptor.EventHandler<T> 定义事件处理的具体实现,实际上就是消费者。
package com.zero.disruptor;

import java.util.concurrent.TimeUnit;

import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.WorkHandler;

/**
 * EventHandlers是BatchEventProcessor要用到的, WorkHandler是WorkerPool要用到的。为简便放在了一起
 */
public class SimpleHandler implements EventHandler<SimpleEvent>,
		WorkHandler<SimpleEvent> {
	private String handlerName;

	public SimpleHandler(String handlerName) {
		this.handlerName = handlerName;
	}

	@Override
	// Override---EventHandler
	public void onEvent(SimpleEvent event, long sequence, boolean endOfBatch)
			throws Exception {
		TimeUnit.MILLISECONDS.sleep(100);
		System.out.println(handlerName + " before deal : sequence = "
				+ sequence + ", Event: " + event.getValue());
		event.setValue(event.getValue() + "--" + handlerName);
		System.out.println(handlerName + " after  deal : "
				+ "Event: " + event.getValue());
	}

	@Override
	// Override---WorkHandler
	public void onEvent(SimpleEvent event) throws Exception {
		// TODO Auto-generated method stub
		TimeUnit.MILLISECONDS.sleep(100);
		System.out.println(handlerName + " before deal :  Event: "
				+ event.getValue());
		event.setValue(event.getValue() + "--" + handlerName);
		System.out.println(handlerName + " after  deal : "
				+ " Event: " + event.getValue());
	}

}
3. 等待策略
       Disruptor 定义了 com.lmax.disruptor.WaitStrategy 接口用于抽象Consumer如何等待新事件,这是策略模式的应用。
       Disruptor 提供了多个WaitStrategy 的实现,每种策略都具有不同性能和优缺点,根据实际运行环境的 CPU 的硬件特点选择恰当的策略,并配合特定的 JVM 的配置参数,能够实现不同的性能提升。
       例如,BlockingWaitStrategy、SleepingWaitStrategy、YieldingWaitStrategy 等,其中,BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现;SleepingWaitStrategy的性能表现跟BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景;YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于CPU逻辑核心数的场景中。
       • BlockingWaitStrategy: 对低延迟和吞吐率不像CPU占用那么重要
       • BusySpinWaitStrategy: CPU使用率高,低延迟
       • LiteBlockingWaitStrategy: BlockingWaitStrategy变种,实验性的
       • PhasedBackoffWaitStrategy: Spins, then yields, then waits,不过还是适合对低延迟和吞吐率不像CPU占用那么重要的情况
       • SleepingWaitStrategy: spin, then yield,然后sleep(LockSupport.parkNanos(1L))在性能和CPU占用率之间做了平衡。
       • TimeoutBlockingWaitStrategy: sleep一段时间。 低延迟。
       • YieldingWaitStrategy: 使用spin, Thread.yield()方式

4.发布事件
       Disruptor 的事件发布过程是一个两阶段提交的过程:
       第一步:先从 RingBuffer 获取下一个可以写入的事件的序号;
       第二步:获取对应的事件对象,将数据写入事件对象;
       第三部:将事件提交到 RingBuffer;
       事件只有在提交之后才会通知 EventProcessor 进行处理;ringBuffer.publish 方法最好包含在 finally中以确保必须得到调用;因为如果某个请求的sequence未被提交,将会堵塞后续的发布操作或者其它的producer。
long seq = 0;
for (int i = 0; i < 20; i++) {
	try {
		seq = ringBuffer.next();// 占个坑 ,ringBuffer一个可用区块
		ringBuffer.get(seq).setValue((long) (Math.random() * 9999));// 给这个区块放入数据
	} finally {
		ringBuffer.publish(seq);// 发布这个区块的数据使handler(consumer)可见
		System.out.println("Producer " + seq);
	}
}
Disruptor 还提供另外一种形式的调用来简化以上操作,并确保 publish 总是得到调用。
package com.zero.disruptor;

import java.nio.ByteBuffer;

import com.lmax.disruptor.EventTranslatorOneArg;
import com.lmax.disruptor.RingBuffer;

public class SimpleEventProducer {
	private final RingBuffer<SimpleEvent> ringBuffer;

	public SimpleEventProducer(RingBuffer<SimpleEvent> ringBuffer) {
		this.ringBuffer = ringBuffer;
	}

	private static final EventTranslatorOneArg TRANSLATOR = new EventTranslatorOneArg<SimpleEvent, String>() {
		@Override
		public void translateTo(SimpleEvent event, long sequence, String value) {
			System.out.println("Producer --- " + sequence + ", event = "
					+ value);
			event.setValue(value);
		}
	};

	/**
	 * onData用来发布事件
	 * 
	 * @param value
	 */
	public void onData(final String value) {
		ringBuffer.publishEvent(TRANSLATOR, value);
	}

}
5. 关闭 Disruptor
disruptor.shutdown();//关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;
executor.shutdown();//关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;
6.示例1:
	public static void demo0() throws Exception {
		/*
		 * createSingleProducer创建一个单生产者的RingBuffer,
		 * 第一个参数叫EventFactory,从名字上理解就是“事件工厂”,其实它的职责就是产生数据填充RingBuffer的区块。
		 * 第二个参数是RingBuffer的大小,它必须是2的指数倍 目的是为了将求模运算转为&运算提高效率
		 * 第三个参数是RingBuffer的生产都在没有可用区块的时候(可能是消费者(或者说是事件处理器) 太慢了)的等待策略
		 */
		final RingBuffer<SimpleEvent> ringBuffer = RingBuffer
				.createSingleProducer(SimpleEvent.FACTORY, 2,
						new SleepingWaitStrategy());
		// 创建线程池
		ExecutorService executors = Executors.newCachedThreadPool();

		// 创建SequenceBarrier
		SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();

		// 创建消息处理器 ,消费者
		BatchEventProcessor<SimpleEvent> processor1 = new BatchEventProcessor<SimpleEvent>(
				ringBuffer, sequenceBarrier, new SimpleHandler(
						"EventHandler_01"));
		// 传入所有消费者线程的序号,如果只有一个消费者的情况可以省略
		ringBuffer.addGatingSequences(processor1.getSequence());
		// 把消息处理器提交到线程池
		executors.submit(processor1);
		// 如果存在多个消费者 那重复执行上面3行代码 把SimpleHandler换成其它消费者类

		BatchEventProcessor<SimpleEvent> processor2 = new BatchEventProcessor<SimpleEvent>(
				ringBuffer, sequenceBarrier, new SimpleHandler(
						"EventHandler_02"));
		ringBuffer.addGatingSequences(processor2.getSequence());
		executors.submit(processor2);
		// 两个消费者会消费一样的事件

		// 生产者
		SimpleEventProducer producer = new SimpleEventProducer(ringBuffer);
		for (int i = 0; i < 4; i++) {
			producer.onData(String.valueOf(i));
		}
		processor1.halt();// 通知事件(或者说消息)处理器可以结束了(并不是马上结束!!!)
		processor2.halt();
		executors.shutdown();// 终止线程
	}
运行结果:
Producer --- 0, event = 0
Producer --- 1, event = 1
EventHandler_02 before deal : sequence = 0, Event: 0
EventHandler_02 after  deal : Event: 0--EventHandler_02
EventHandler_01 before deal : sequence = 0, Event: 0--EventHandler_02
EventHandler_01 after  deal : Event: 0--EventHandler_02--EventHandler_01
EventHandler_02 before deal : sequence = 1, Event: 1
EventHandler_02 after  deal : Event: 1--EventHandler_02
EventHandler_01 before deal : sequence = 1, Event: 1--EventHandler_02
EventHandler_01 after  deal : Event: 1--EventHandler_02--EventHandler_01
Producer --- 2, event = 2
Producer --- 3, event = 3
EventHandler_01 before deal : sequence = 2, Event: 2
EventHandler_01 after  deal : Event: 2--EventHandler_01
EventHandler_02 before deal : sequence = 2, Event: 2--EventHandler_01
EventHandler_02 after  deal : Event: 2--EventHandler_01--EventHandler_02
EventHandler_01 before deal : sequence = 3, Event: 3
EventHandler_01 after  deal : Event: 3--EventHandler_01
EventHandler_02 before deal : sequence = 3, Event: 3--EventHandler_01
EventHandler_02 after  deal : Event: 3--EventHandler_01--EventHandler_02

可以看出,每个event都被处理了,但是先后顺序不同,如果需要有先后顺序,可以采用一下方式:

	public static void demo1() throws Exception {
		final RingBuffer<SimpleEvent> ringBuffer = RingBuffer
				.createSingleProducer(SimpleEvent.FACTORY, 2,
						new SleepingWaitStrategy());
		ExecutorService executors = Executors.newCachedThreadPool();

		SequenceBarrier stepOneSequenceBarrier = ringBuffer.newBarrier();
		BatchEventProcessor<SimpleEvent> processor1 = new BatchEventProcessor<SimpleEvent>(
				ringBuffer, stepOneSequenceBarrier, new SimpleHandler(
						"EventHandler_01"));
		executors.submit(processor1);

		SequenceBarrier stepTwoSequenceBarrier = ringBuffer
				.newBarrier(processor1.getSequence());
		BatchEventProcessor<SimpleEvent> processor2 = new BatchEventProcessor<SimpleEvent>(
				ringBuffer, stepTwoSequenceBarrier, new SimpleHandler(
						"EventHandler_02"));
		
		//processor2消费最慢,所以以processor2的Sequence为阈值
		ringBuffer.addGatingSequences(processor2.getSequence());
		
		executors.submit(processor2);

		// 生产者
		SimpleEventProducer producer = new SimpleEventProducer(ringBuffer);
		for (int i = 0; i < 4; i++) {
			producer.onData(String.valueOf(i));
		}
		
		executors.shutdown();// 终止线程
	}
运行结果:

Producer --- 0, event = 0
Producer --- 1, event = 1
EventHandler_01 before deal : sequence = 0, Event: 0
EventHandler_01 after  deal : Event: 0--EventHandler_01
EventHandler_01 before deal : sequence = 1, Event: 1
EventHandler_01 after  deal : Event: 1--EventHandler_01
EventHandler_02 before deal : sequence = 0, Event: 0--EventHandler_01
EventHandler_02 after  deal : Event: 0--EventHandler_01--EventHandler_02
EventHandler_02 before deal : sequence = 1, Event: 1--EventHandler_01
EventHandler_02 after  deal : Event: 1--EventHandler_01--EventHandler_02
Producer --- 2, event = 2
Producer --- 3, event = 3
EventHandler_01 before deal : sequence = 2, Event: 2
EventHandler_01 after  deal : Event: 2--EventHandler_01
EventHandler_01 before deal : sequence = 3, Event: 3
EventHandler_01 after  deal : Event: 3--EventHandler_01
EventHandler_02 before deal : sequence = 2, Event: 2--EventHandler_01
EventHandler_02 after  deal : Event: 2--EventHandler_01--EventHandler_02
EventHandler_02 before deal : sequence = 3, Event: 3--EventHandler_01
EventHandler_02 after  deal : Event: 3--EventHandler_01--EventHandler_02
如果想一个消息只被一个消费者消费呢?
	public static void demo2() throws Exception {
		final RingBuffer<SimpleEvent> ringBuffer = RingBuffer
				.createSingleProducer(SimpleEvent.FACTORY, 4,
						new SleepingWaitStrategy());
		ExecutorService executors = Executors.newCachedThreadPool();

		SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();

		// 创建消息处理器 ,消费者
		WorkHandler<SimpleEvent> processor1 = new SimpleHandler("WorkHandler_1");
		WorkHandler<SimpleEvent> processor2 = new SimpleHandler("WorkHandler_2");
		WorkerPool<SimpleEvent> workerPool = new WorkerPool<SimpleEvent>(
				ringBuffer, sequenceBarrier, new IgnoreExceptionHandler(),
				processor1, processor2);
		ringBuffer.addGatingSequences(workerPool.getWorkerSequences());// 要在workerPool.start(executors);之前
		workerPool.start(executors);

		// 生产者
		SimpleEventProducer producer = new SimpleEventProducer(ringBuffer);
		for (int i = 0; i < 4; i++) {
			producer.onData(String.valueOf(i));
		}
		workerPool.halt();
		executors.shutdown();// 终止线程
	}
运行结果:
Producer --- 0, event = 0
Producer --- 1, event = 1
Producer --- 2, event = 2
Producer --- 3, event = 3
WorkHandler_2 before deal :  Event: 1
WorkHandler_1 before deal :  Event: 0
WorkHandler_1 after  deal :  Event: 0--WorkHandler_1
WorkHandler_2 after  deal :  Event: 1--WorkHandler_2
WorkHandler_1 before deal :  Event: 2
WorkHandler_2 before deal :  Event: 3
WorkHandler_2 after  deal :  Event: 3--WorkHandler_2
WorkHandler_1 after  deal :  Event: 2--WorkHandler_1
如果想一个消息被两组消费者消费,但是每组只能有一个消费?
	public static void demo3() throws Exception {
		final RingBuffer<SimpleEvent> ringBuffer = RingBuffer
				.createSingleProducer(SimpleEvent.FACTORY, 4,
						new SleepingWaitStrategy());
		ExecutorService executors = Executors.newCachedThreadPool();

		SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();

		// 创建消息处理器 ,消费者
		WorkHandler<SimpleEvent> processor1 = new SimpleHandler("WorkHandler_1");
		WorkHandler<SimpleEvent> processor2 = new SimpleHandler("WorkHandler_2");
		WorkerPool<SimpleEvent> workerPool = new WorkerPool<SimpleEvent>(
				ringBuffer, sequenceBarrier, new IgnoreExceptionHandler(),
				processor1, processor2);
		ringBuffer.addGatingSequences(workerPool.getWorkerSequences());// 要在workerPool.start(executors);之前
		workerPool.start(executors);

		
		// 创建消息处理器 ,消费者
		WorkerPool<SimpleEvent> workerPool2 = new WorkerPool<SimpleEvent>(
				ringBuffer, sequenceBarrier, new IgnoreExceptionHandler(),
				new SimpleHandler("WorkHandler_3"));
		ringBuffer.addGatingSequences(workerPool2.getWorkerSequences());// 要在workerPool.start(executors);之前
		workerPool2.start(executors);
		
		// 生产者
		SimpleEventProducer producer = new SimpleEventProducer(ringBuffer);
		for (int i = 0; i < 4; i++) {
			producer.onData(String.valueOf(i));
		}
		workerPool.halt();
		executors.shutdown();// 终止线程
	}
运行结果:
Producer --- 0, event = 0
Producer --- 1, event = 1
Producer --- 2, event = 2
Producer --- 3, event = 3
WorkHandler_2 before deal :  Event: 1
WorkHandler_3 before deal :  Event: 0
WorkHandler_3 after  deal :  Event: 0--WorkHandler_3
WorkHandler_1 before deal :  Event: 0
WorkHandler_1 after  deal :  Event: 0--WorkHandler_3--WorkHandler_1
WorkHandler_2 after  deal :  Event: 1--WorkHandler_2
WorkHandler_2 before deal :  Event: 3
WorkHandler_1 before deal :  Event: 2
WorkHandler_1 after  deal :  Event: 2--WorkHandler_1
WorkHandler_2 after  deal :  Event: 3--WorkHandler_2
WorkHandler_3 before deal :  Event: 1--WorkHandler_2
WorkHandler_3 after  deal :  Event: 1--WorkHandler_2--WorkHandler_3
WorkHandler_3 before deal :  Event: 2--WorkHandler_1
WorkHandler_3 after  deal :  Event: 2--WorkHandler_1--WorkHandler_3
WorkHandler_3 before deal :  Event: 3--WorkHandler_2
WorkHandler_3 after  deal :  Event: 3--WorkHandler_2--WorkHandler_3

7.示例2:
	public static void work1() {
		ExecutorService executor = Executors.newFixedThreadPool(2);
		int ringBufferSize = 2; // RingBuffer 大小,必须是 2 的 N 次方;
		// 构造Disruptor
		Disruptor<SimpleEvent> disruptor = new Disruptor<SimpleEvent>(
				SimpleEvent.FACTORY, ringBufferSize, executor,
				ProducerType.SINGLE, new BlockingWaitStrategy());

		disruptor.handleEventsWith(new SimpleHandler("BatchEventHandler_01"),
				new SimpleHandler("BatchEventHandler_02"));
		// 每个EventHandler占用一个线程,如果Executors.newFixedThreadPool(1);会导致阻塞

		RingBuffer<SimpleEvent> ringBuffer = disruptor.start();

		// 生产者
		SimpleEventProducer producer = new SimpleEventProducer(ringBuffer);
		for (long i = 10; i < 15; i++) {
			producer.onData(String.valueOf(i));
		}
		
		// 关闭 Disruptor
		disruptor.shutdown();// 关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;
		executor.shutdown();// 关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭,
							// disruptor在shutdown 时不会自动关闭;
	}
以上示例,效果等同于demo0。运行结果;
Producer --- 0, event = 10
Producer --- 1, event = 11
BatchEventHandler_02 before deal : sequence = 0, Event: 10
BatchEventHandler_02 after  deal : Event: 10--BatchEventHandler_02
BatchEventHandler_01 before deal : sequence = 0, Event: 10
BatchEventHandler_01 after  deal : Event: 10--BatchEventHandler_02--BatchEventHandler_01
BatchEventHandler_01 before deal : sequence = 1, Event: 11
BatchEventHandler_01 after  deal : Event: 11--BatchEventHandler_01
BatchEventHandler_02 before deal : sequence = 1, Event: 11--BatchEventHandler_01
BatchEventHandler_02 after  deal : Event: 11--BatchEventHandler_01--BatchEventHandler_02
Producer --- 2, event = 12
Producer --- 3, event = 13
BatchEventHandler_01 before deal : sequence = 2, Event: 12
BatchEventHandler_02 before deal : sequence = 2, Event: 12
BatchEventHandler_02 after  deal : Event: 12--BatchEventHandler_02
BatchEventHandler_01 after  deal : Event: 12--BatchEventHandler_02--BatchEventHandler_01
BatchEventHandler_02 before deal : sequence = 3, Event: 13
BatchEventHandler_02 after  deal : Event: 13--BatchEventHandler_02
BatchEventHandler_01 before deal : sequence = 3, Event: 13--BatchEventHandler_02
BatchEventHandler_01 after  deal : Event: 13--BatchEventHandler_02--BatchEventHandler_01
Producer --- 4, event = 14
BatchEventHandler_02 before deal : sequence = 4, Event: 14
BatchEventHandler_02 after  deal : Event: 14--BatchEventHandler_02
BatchEventHandler_01 before deal : sequence = 4, Event: 14--BatchEventHandler_02
BatchEventHandler_01 after  deal : Event: 14--BatchEventHandler_02--BatchEventHandler_01
可以看出每个SimpleHandler (implements EventHandler)都会处理同一条消息。另外还可以使用类似:
disruptor.handleEventsWith(new SimpleHandler("BatchEventHandler_01"))
				.then(new SimpleHandler("BatchEventHandler_02"));
这样的方法来定义依赖关系,比如先执行哪个handler再执行哪个handler。
运行结果:
Producer --- 0, event = 10
Producer --- 1, event = 11
BatchEventHandler_01 before deal : sequence = 0, Event: 10
BatchEventHandler_01 after  deal : Event: 10--BatchEventHandler_01
BatchEventHandler_01 before deal : sequence = 1, Event: 11
BatchEventHandler_01 after  deal : Event: 11--BatchEventHandler_01
BatchEventHandler_02 before deal : sequence = 0, Event: 10--BatchEventHandler_01
BatchEventHandler_02 after  deal : Event: 10--BatchEventHandler_01--BatchEventHandler_02
BatchEventHandler_02 before deal : sequence = 1, Event: 11--BatchEventHandler_01
BatchEventHandler_02 after  deal : Event: 11--BatchEventHandler_01--BatchEventHandler_02
Producer --- 2, event = 12
Producer --- 3, event = 13
BatchEventHandler_01 before deal : sequence = 2, Event: 12
BatchEventHandler_01 after  deal : Event: 12--BatchEventHandler_01
BatchEventHandler_01 before deal : sequence = 3, Event: 13
BatchEventHandler_01 after  deal : Event: 13--BatchEventHandler_01
BatchEventHandler_02 before deal : sequence = 2, Event: 12--BatchEventHandler_01
BatchEventHandler_02 after  deal : Event: 12--BatchEventHandler_01--BatchEventHandler_02
BatchEventHandler_02 before deal : sequence = 3, Event: 13--BatchEventHandler_01
BatchEventHandler_02 after  deal : Event: 13--BatchEventHandler_01--BatchEventHandler_02
Producer --- 4, event = 14
BatchEventHandler_01 before deal : sequence = 4, Event: 14
BatchEventHandler_01 after  deal : Event: 14--BatchEventHandler_01
BatchEventHandler_02 before deal : sequence = 4, Event: 14--BatchEventHandler_01
BatchEventHandler_02 after  deal : Event: 14--BatchEventHandler_01--BatchEventHandler_02
同样,还可以使用WorkerPool:
	public static void work2() {
		ExecutorService executor = Executors.newFixedThreadPool(2);
		int ringBufferSize = 4; // RingBuffer 大小,必须是 2 的 N 次方;
		// 构造Disruptor
		Disruptor<SimpleEvent> disruptor = new Disruptor<SimpleEvent>(
				SimpleEvent.FACTORY, ringBufferSize, executor,
				ProducerType.SINGLE, new BlockingWaitStrategy());

		disruptor.handleExceptionsWith(new IgnoreExceptionHandler());

		disruptor.handleEventsWithWorkerPool(new SimpleHandler(
				"WorkEventHandler_01"),
				new SimpleHandler("WorkEventHandler_02"));

		RingBuffer<SimpleEvent> ringBuffer = disruptor.start();

		// // 获取生产者的位置信息
		SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();

		// 生产者
		SimpleEventProducer producer = new SimpleEventProducer(ringBuffer);
		for (long i = 10; i < 15; i++) {
			producer.onData(String.valueOf(i));
		}
		disruptor.shutdown();
		executor.shutdown();
	}
运行结果:
Producer --- 0, event = 10
Producer --- 1, event = 11
Producer --- 2, event = 12
Producer --- 3, event = 13
WorkEventHandler_02 before deal :  Event: 10
WorkEventHandler_01 before deal :  Event: 11
WorkEventHandler_02 after  deal :  Event: 10--WorkEventHandler_02
WorkEventHandler_01 after  deal :  Event: 11--WorkEventHandler_01
Producer --- 4, event = 14
WorkEventHandler_02 before deal :  Event: 12
WorkEventHandler_02 after  deal :  Event: 12--WorkEventHandler_02
WorkEventHandler_01 before deal :  Event: 13
WorkEventHandler_01 after  deal :  Event: 13--WorkEventHandler_01
WorkEventHandler_02 before deal :  Event: 14
WorkEventHandler_02 after  deal :  Event: 14--WorkEventHandler_02






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值