修改mq相关代码 整理netty与disruptor
parent
53e0bdfe70
commit
b4b9258b91
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cdp-tools</artifactId>
|
||||
<groupId>com.baiye</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>cdp-tool-dy</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<name>cdp-tool-dy</name>
|
||||
<description>cdp-tool-dy</description>
|
||||
<properties>
|
||||
<java.version>8</java.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -1,34 +0,0 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
.mvn
|
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cdp-tools</artifactId>
|
||||
<groupId>com.baiye</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>cdp-tool-netty-disruptor</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>cdp-tool-netty-disruptor</name>
|
||||
<description>cdp-tool-netty-disruptor</description>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baiye</groupId>
|
||||
<artifactId>cdp-common-core</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 序列化框架marshalling -->
|
||||
<dependency>
|
||||
<groupId>org.jboss.marshalling</groupId>
|
||||
<artifactId>jboss-marshalling</artifactId>
|
||||
<version>1.3.0.CR9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.marshalling</groupId>
|
||||
<artifactId>jboss-marshalling-serial</artifactId>
|
||||
<version>1.3.0.CR9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,36 @@
|
||||
package com.baiye;
|
||||
|
||||
import com.baiye.common.MessageConsumer;
|
||||
import com.baiye.common.RingBufferWorkerPoolFactory;
|
||||
import com.baiye.server.MessageConsumerImpl;
|
||||
import com.baiye.server.SocketServer;
|
||||
import com.lmax.disruptor.YieldingWaitStrategy;
|
||||
import com.lmax.disruptor.dsl.ProducerType;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date 2022-7-20
|
||||
*/
|
||||
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
|
||||
public class CdpToolNettyDisruptorApplication {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
int consumerCount = 10;
|
||||
MessageConsumer[] consumers = new MessageConsumer[consumerCount];
|
||||
|
||||
for (int i = 0; i < consumerCount; i++){
|
||||
MessageConsumer consumer =
|
||||
new MessageConsumerImpl("CODE" + i);
|
||||
consumers[i] = consumer;
|
||||
}
|
||||
RingBufferWorkerPoolFactory.getInstance().initStart
|
||||
(ProducerType.MULTI, 1024 * 1024, new YieldingWaitStrategy(), consumers);
|
||||
|
||||
SocketServer.getInstance().startServer();
|
||||
SpringApplication.run(CdpToolNettyDisruptorApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.baiye.common;
|
||||
|
||||
import com.lmax.disruptor.WorkHandler;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
public abstract class MessageConsumer implements WorkHandler<TranslatorDataWraper> {
|
||||
protected String consumerId;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.baiye.common;
|
||||
|
||||
import com.lmax.disruptor.RingBuffer;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class MessageProducer {
|
||||
|
||||
|
||||
private RingBuffer<TranslatorDataWraper> ringBuffer;
|
||||
|
||||
|
||||
public void sendData(ChannelHandlerContext context, String msg){
|
||||
long seq = ringBuffer.next();
|
||||
TranslatorDataWraper translatorDataWraper = ringBuffer.get(seq);
|
||||
translatorDataWraper.setContext(context);
|
||||
translatorDataWraper.setMsg(msg);
|
||||
ringBuffer.publish(seq);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.baiye.common;
|
||||
|
||||
import com.lmax.disruptor.*;
|
||||
import com.lmax.disruptor.dsl.ProducerType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Slf4j
|
||||
public class RingBufferWorkerPoolFactory {
|
||||
private static class SingletonHolder{
|
||||
static final RingBufferWorkerPoolFactory factory = new RingBufferWorkerPoolFactory();
|
||||
}
|
||||
public RingBufferWorkerPoolFactory() {
|
||||
}
|
||||
|
||||
public static RingBufferWorkerPoolFactory getInstance(){
|
||||
return SingletonHolder.factory;
|
||||
}
|
||||
|
||||
private static Map<String, MessageProducer> producers = new ConcurrentHashMap<>();
|
||||
private static Map<String, MessageConsumer> consumers = new ConcurrentHashMap<>();
|
||||
|
||||
private RingBuffer<TranslatorDataWraper> ringBuffer;
|
||||
|
||||
private WorkerPool<TranslatorDataWraper> workerPool;
|
||||
private SequenceBarrier sequenceBarrier;
|
||||
|
||||
public void initStart(ProducerType type, int bufferSize, WaitStrategy waitStrategy, MessageConsumer[] consumersList){
|
||||
//构造 ringbuffer
|
||||
this.ringBuffer = RingBuffer.create(type, TranslatorDataWraper::new, bufferSize, waitStrategy);
|
||||
|
||||
//设置栅栏
|
||||
this.sequenceBarrier = this.ringBuffer.newBarrier();
|
||||
//设置workpool
|
||||
this.workerPool = new WorkerPool<>(this.ringBuffer, this.sequenceBarrier, null, consumersList);
|
||||
//把所构建的消费者放入池中
|
||||
Arrays.asList(consumersList).forEach(messageConsumer -> consumers.put(messageConsumer.getConsumerId(), messageConsumer));
|
||||
|
||||
//添加sequence
|
||||
this.ringBuffer.addGatingSequences(this.workerPool.getWorkerSequences());
|
||||
//启动workpool
|
||||
this.workerPool.start(Executors.newFixedThreadPool(10));
|
||||
}
|
||||
|
||||
public MessageProducer getMessageProducer(String produceId){
|
||||
MessageProducer producer = producers.get(produceId);
|
||||
if (null == producer){
|
||||
producer = new MessageProducer(this.ringBuffer);
|
||||
producers.put(produceId, producer);
|
||||
}
|
||||
return producer;
|
||||
}
|
||||
/**
|
||||
* 事件异常处理程序
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author q
|
||||
* @date 2022/07/13
|
||||
*/
|
||||
static class EventExceptionHandler implements ExceptionHandler<TranslatorDataWapper> {
|
||||
|
||||
@Override
|
||||
public void handleEventException(Throwable throwable, long l, TranslatorDataWapper translatorDataWapper) {
|
||||
// TODO: 2022/7/13 0013 todo...
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleOnStartException(Throwable throwable) {
|
||||
// TODO: 2022/7/13 0013 todo...
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleOnShutdownException(Throwable throwable) {
|
||||
// TODO: 2022/7/13 0013 todo...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.baiye.common;
|
||||
|
||||
import com.baiye.dto.TranslatorData;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 包装 Netty 使用的数据类型给 Disruptor 进行使用
|
||||
*
|
||||
* @author q
|
||||
* @date 2022/07/13
|
||||
*/
|
||||
@Data
|
||||
public class TranslatorDataWapper {
|
||||
|
||||
private TranslatorData translatorData;
|
||||
|
||||
private ChannelHandlerContext ctx;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.baiye.common;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TranslatorDataWraper {
|
||||
private String msg;
|
||||
private ChannelHandlerContext context;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.baiye.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 传输数据对象
|
||||
*
|
||||
* @author q
|
||||
* @date 2022/07/11
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class TranslatorData implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 消息的名字
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String message;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.baiye.dto;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 包装 Netty 使用的数据类型给 Disruptor 进行使用
|
||||
*
|
||||
* @author q
|
||||
* @date 2022/07/13
|
||||
*/
|
||||
@Data
|
||||
public class TranslatorDataWapper {
|
||||
|
||||
private TranslatorData translatorData;
|
||||
|
||||
private ChannelHandlerContext ctx;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.baiye.server;
|
||||
|
||||
import com.baiye.common.MessageConsumer;
|
||||
import com.baiye.common.TranslatorDataWraper;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class MessageConsumerImpl extends MessageConsumer {
|
||||
public MessageConsumerImpl(String consumerId) {
|
||||
super(consumerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(TranslatorDataWraper translatorDataWraper) throws Exception {
|
||||
log.info("收到客户端消息----------{}", translatorDataWraper.getMsg());
|
||||
|
||||
ChannelHandlerContext ctx = translatorDataWraper.getContext();
|
||||
ctx.channel().writeAndFlush("返回给客户端消息");
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package com.baiye.server;
|
||||
|
||||
import com.baiye.codec.MarshallingCodeCFactory;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class SocketServer {
|
||||
|
||||
//扩展 完善 池化: ConcurrentHashMap<KEY -> String, Value -> Channel>
|
||||
private Channel channel;
|
||||
private SocketServer() {
|
||||
|
||||
}
|
||||
|
||||
public static SocketServer getInstance() {
|
||||
return Singleton.INSTANCE.getInstance();
|
||||
}
|
||||
|
||||
private enum Singleton {
|
||||
INSTANCE;
|
||||
|
||||
private SocketServer singleton;
|
||||
|
||||
|
||||
//jvm保证只调用一次
|
||||
Singleton() {
|
||||
singleton = new SocketServer();
|
||||
}
|
||||
|
||||
public SocketServer getInstance() {
|
||||
return singleton;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void startServer() throws InterruptedException {
|
||||
log.info("---------启动socket服务---------");
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup();
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
ServerBootstrap serverBootstrap;
|
||||
serverBootstrap = new ServerBootstrap()
|
||||
.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
// fixme 设置 backlog大小
|
||||
.option(ChannelOption.SO_BACKLOG, 1024)
|
||||
// fixme 表示缓存区动态调配(自适应) 数据包相差不大的时候比较合适
|
||||
.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)
|
||||
// 缓冲区 池化操作
|
||||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
// 记录日志
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
// 数据接收过来给哪个方法进行回调 -> 接收数据进行异步处理
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
// fixme 在管道上加一些拦截器进行处理
|
||||
|
||||
// 进行设置编解码 -> 对Java对象转为的二进制数据进行编解码
|
||||
socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
|
||||
socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
|
||||
// 信息处理
|
||||
socketChannel.pipeline().addLast(new SocketServerHandler());
|
||||
}
|
||||
});
|
||||
// 绑定端口, 同步请求链接
|
||||
ChannelFuture cf = serverBootstrap.bind(8099).sync();
|
||||
// 异步的进行关闭
|
||||
//接下来就进行数据的发送, 但是首先我们要获取channel:
|
||||
this.channel = cf.channel();
|
||||
log.info("---------socket服务启动成功---------");
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
SocketServer.getInstance().startServer();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.baiye.server;
|
||||
|
||||
import com.baiye.common.MessageProducer;
|
||||
import com.baiye.common.RingBufferWorkerPoolFactory;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author Enzo
|
||||
* @date 2022-7-21
|
||||
*/
|
||||
@Slf4j
|
||||
public class SocketServerHandler extends SimpleChannelInboundHandler<String> {
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
|
||||
MessageProducer messageProducer = RingBufferWorkerPoolFactory.getInstance().getMessageProducer("message001");
|
||||
messageProducer.sendData(channelHandlerContext, msg);
|
||||
log.info("消息-------- {}", msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||
log.info("server handler added----{}", ctx.channel().remoteAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
log.info("server handler removed----{}", ctx.channel().remoteAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.baiye.server;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
import io.netty.handler.codec.LengthFieldPrepender;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
public class SocketServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
ChannelPipeline pipeline = socketChannel.pipeline();
|
||||
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
|
||||
pipeline.addLast( new LengthFieldPrepender(4));
|
||||
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
|
||||
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
|
||||
pipeline.addLast(new SocketServerHandler());
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
server:
|
||||
port: 8092
|
@ -0,0 +1,13 @@
|
||||
package com.baiye;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class CdpToolNettyDisruptorApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>by-cdp</artifactId>
|
||||
<artifactId>cdp-tools</artifactId>
|
||||
<groupId>com.baiye</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cdp-wechat</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>cdp-tool-wechat</name>
|
||||
<artifactId>cdp-tool-wechat</artifactId>
|
||||
<description>cdp-tool-wechat</description>
|
||||
<modules>
|
||||
<module>cdp-wechat-server</module>
|
||||
<module>cdp-wechat-api</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<java.version>8</java.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
||||
|
||||
|
||||
</project>
|
Loading…
Reference in New Issue