mom
This commit is contained in:
26
.classpath
Normal file
26
.classpath
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/lib/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/lib/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/lib/classes"/>
|
||||
</classpath>
|
||||
29
.project
Normal file
29
.project
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>middleware-mom</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
4
.settings/org.eclipse.core.resources.prefs
Normal file
4
.settings/org.eclipse.core.resources.prefs
Normal file
@@ -0,0 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//src/main/java=UTF-8
|
||||
encoding//src/test/java=UTF-8
|
||||
encoding/<project>=UTF-8
|
||||
5
.settings/org.eclipse.jdt.core.prefs
Normal file
5
.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,5 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
4
.settings/org.eclipse.m2e.core.prefs
Normal file
4
.settings/org.eclipse.m2e.core.prefs
Normal file
@@ -0,0 +1,4 @@
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
2
broker.sh
Normal file
2
broker.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
java -cp target/lib/middleware-mom-1.0.jar com.alibaba.middleware.race.mom.Broker
|
||||
107
pom.xml
Normal file
107
pom.xml
Normal file
@@ -0,0 +1,107 @@
|
||||
<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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.alibaba.race</groupId>
|
||||
<artifactId>middleware-mom</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>middleware-mom</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<directory>${basedir}/target/lib</directory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptors>
|
||||
<descriptor>src/main/java/assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<!-- this is used for inheritance merges -->
|
||||
<phase>package</phase>
|
||||
<!-- bind to the packaging phase -->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
<version>2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.ruedigermoeller</groupId>
|
||||
<artifactId>fst</artifactId>
|
||||
<version>2.34</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.esotericsoftware</groupId>
|
||||
<artifactId>kryo</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.23.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.2</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.12</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<version>1.0</version>
|
||||
</project>
|
||||
30
src/main/java/assembly.xml
Normal file
30
src/main/java/assembly.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<assembly>
|
||||
<id>bin</id>
|
||||
<baseDirectory>middleware-mom</baseDirectory>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<formats>
|
||||
<format>dir</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
<include>LICENSE*</include>
|
||||
<include>NOTICE*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/lib</directory>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/lib</outputDirectory>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<scope>runtime</scope>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
276
src/main/java/com/alibaba/middleware/race/mom/Broker.java
Normal file
276
src/main/java/com/alibaba/middleware/race/mom/Broker.java
Normal file
@@ -0,0 +1,276 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageManager;
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageSend;
|
||||
import com.alibaba.middleware.race.mom.broker.group.ConsumerGroup;
|
||||
import com.alibaba.middleware.race.mom.serializer.RpcDecoder;
|
||||
import com.alibaba.middleware.race.mom.serializer.RpcEncoder;
|
||||
import com.alibaba.middleware.race.mom.util.InfoBodyConsumer;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
|
||||
public class Broker {
|
||||
private static Logger logger = LoggerFactory.getLogger(Broker.class);
|
||||
/**
|
||||
* 启动broker之前的netty服务器处理
|
||||
* @param port
|
||||
* @throws Exception
|
||||
*/
|
||||
public void bind(int port) throws Exception {
|
||||
//启动消费进度定时任务
|
||||
// storeSubscribe(true, 5000);
|
||||
// scanSendInfotMap();
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup();
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors()*3);
|
||||
try {
|
||||
ServerBootstrap serverBootstrap = new ServerBootstrap();
|
||||
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.SO_REUSEADDR, true)
|
||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||
.option(ChannelOption.SO_BACKLOG, 1024*1024)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
//
|
||||
// .option(ChannelOption.SO_BACKLOG, 1024)
|
||||
// //
|
||||
// .option(ChannelOption.SO_REUSEADDR, true)
|
||||
// //
|
||||
// .option(ChannelOption.SO_KEEPALIVE, false)
|
||||
// //
|
||||
// .childOption(ChannelOption.TCP_NODELAY, true)
|
||||
// .option(ChannelOption.SO_SNDBUF, 65535)
|
||||
// //
|
||||
// .option(ChannelOption.SO_RCVBUF, 65535)
|
||||
.childHandler(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ch.pipeline().addLast(new RpcDecoder()).addLast(new RpcEncoder())
|
||||
.addLast(new SimpleChannelInboundHandler<Object>() {
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, Object info) throws Exception {
|
||||
if (InfoBodyConsumer.class.isInstance(info)) {
|
||||
// 接受消费者订阅处理
|
||||
processConsumer((InfoBodyConsumer) info, ctx);
|
||||
} else if (ConsumeResult.class.isInstance(info)) {
|
||||
// 收到消费者ConsumeResult
|
||||
logger.debug(" 收到消费者ConsumeResult");
|
||||
ConsumerGroup.confirmConsumer((ConsumeResult)info, ctx);
|
||||
// confirmConsumer((ConsumeResult) info, ctx);
|
||||
}else if(MessageSend.class.isInstance(info)){
|
||||
//System.out.println("收到消息");
|
||||
MessageManager.recieveMsg((MessageSend) info,ctx);
|
||||
// MessageManager.recieveMsg((LinkedBlockingQueue)info,ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
logger.error("broker 异常:");
|
||||
// logger.error(cause.getMessage());
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
});//
|
||||
|
||||
ChannelFuture future = serverBootstrap.bind(port).sync();
|
||||
logger.debug("mom服务启动成功...... 绑定端口" + port);
|
||||
|
||||
// 等待服务端监听端口关闭
|
||||
future.channel().closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("smom服务抛出异常 " + e.getMessage());
|
||||
} finally {
|
||||
// 优雅退出 释放线程池资源
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
logger.debug("mom服务优雅的释放了线程资源...");
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 收到消费者的订阅关系,并创建队列
|
||||
* @param consumerRequestInfo
|
||||
* @param ctx
|
||||
*/
|
||||
public void processConsumer(InfoBodyConsumer consumerRequestInfo, final ChannelHandlerContext ctx) {
|
||||
|
||||
String topic = consumerRequestInfo.getTopic();
|
||||
String groupid = consumerRequestInfo.getGroupId();
|
||||
Map<String, String> filter = consumerRequestInfo.getFilterMap();
|
||||
TopicAndFilter topicAndFilter = new TopicAndFilter(topic, filter);
|
||||
Channel consumer = ctx.channel();
|
||||
logger.error("消费者:" + consumer.hashCode()+"发起订阅"+consumerRequestInfo);
|
||||
ConsumerGroup.addConsumer(consumer,groupid,topicAndFilter);
|
||||
//
|
||||
}
|
||||
|
||||
// public void scanSendInfotMap(){
|
||||
// this.timoutScannerTimer.schedule(new TimerTask(){
|
||||
// @Override
|
||||
// public void run() {
|
||||
// for(Map.Entry<String , SendInfo> entry:msgManager.getSendInfoMap().entrySet()){
|
||||
// SendInfo sendInfo=entry.getValue();
|
||||
// for(Map.Entry<ConsumerGroup , SendStatus> groupstatus:sendInfo.getGroup2status().entrySet()){
|
||||
// if(groupstatus.getValue()==SendStatus.TIMEOUT||sendInfo.isTimeout(System.currentTimeMillis())){
|
||||
// String msgId=sendInfo.getMsgId();
|
||||
// final String topic=sendInfo.getTopic();
|
||||
// final TopicAndFilter topicAndfilter=sendInfo.getTopicAndFilter();
|
||||
// final ConsumerGroup group=groupstatus.getKey();
|
||||
// BlockingQueue<String> reSendMessageQueue=topicAndgroup2reSendMsgQueue.get(topic+ "@" +group);
|
||||
// if(reSendMessageQueue==null){
|
||||
// reSendMessageQueue=new LinkedBlockingQueue<String>();
|
||||
// reSendMessageQueue.add(msgId);
|
||||
// topicAndgroup2reSendMsgQueue.put(topic+ "@" +group,reSendMessageQueue);
|
||||
// }else {
|
||||
// reSendMessageQueue.add(msgId);
|
||||
// }
|
||||
// // messageCache.remove(msgId);//消息进入重试队列,这里应该移除了,sendResultMap还可以重试结果使用
|
||||
// reSendPool.execute(new Runnable(){
|
||||
// @Override
|
||||
// public void run() {
|
||||
// reSendToConsumer(topicAndgroup2reSendMsgQueue.get(topic+ "@" +group),group);
|
||||
// }
|
||||
// });
|
||||
// // if(msgid2offset.containsKey(msgId)){
|
||||
// // topicAndgroup2subScribe.get(topic+"@"+group).get(sendInfo.getQueueId()).setCurrentoffset(msgid2offset.get(msgId));
|
||||
// // msgid2offset.remove(msgId);
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// },timeoutLimit+100,1000);
|
||||
// }
|
||||
/**
|
||||
* 恢复订阅关系
|
||||
*/
|
||||
// public void recover(){
|
||||
// logger.debug("恢复订阅关系队列");
|
||||
// long startTime = System.currentTimeMillis();
|
||||
// ArrayList<Offset>offsets =substore.read();
|
||||
// System.out.println("offsets:"+offsets);
|
||||
// String topic;
|
||||
// String group;
|
||||
// String queueId;
|
||||
// String topicAndFilter;
|
||||
// for(Offset offset:offsets){
|
||||
// topic=offset.getTopicId();
|
||||
// topicAndFilter=offset.getTopicAndFilter();
|
||||
// group=offset.getGroupId();
|
||||
// queueId=offset.getQueueId();
|
||||
// //恢复topicAndgroup2subScribe
|
||||
// LinkedList<Offset> offsetList;
|
||||
// if(!topicAndgroup2subScribe.containsKey(topic+"@"+group))
|
||||
// {
|
||||
// offsetList=new LinkedList<Offset>();
|
||||
// topicAndgroup2subScribe.put(topic+"@"+group, offsetList);
|
||||
// }else
|
||||
// {
|
||||
// offsetList=topicAndgroup2subScribe.get(topic+"@"+group);
|
||||
// }
|
||||
// offsetList.add(Integer.parseInt(queueId),offset);
|
||||
// //恢复topicAndgroup2sendMsgQueue
|
||||
// LinkedList<BlockingQueue<Integer>> sendQueuesList;
|
||||
// if(!topicAndgroup2sendMsgQueue.containsKey(topic + "@" + group))
|
||||
// {
|
||||
// sendQueuesList=new LinkedList<BlockingQueue<Integer>>();
|
||||
// topicAndgroup2sendMsgQueue.put(topic + "@" + group, sendQueuesList);
|
||||
// }else
|
||||
// {
|
||||
// sendQueuesList=topicAndgroup2sendMsgQueue.get(topic + "@" + group);
|
||||
// }
|
||||
// BlockingQueue<Integer>sendQueue=new LinkedBlockingQueue<>();
|
||||
// sendQueuesList.add(Integer.parseInt(queueId),sendQueue);
|
||||
// //消息入队(这里只磁盘拉)
|
||||
// int currentOffset=offset.getCurrentoffset();
|
||||
// Message msg;
|
||||
// int i=0;//currentOffset偏移量
|
||||
// List<byte[]> blist = mstore.readByteNormal(topicAndFilter,"t","t",queueId+"",offset.getCurrentoffset()+1, offset.getCacheOffset()-1);
|
||||
// System.out.println(blist.size());
|
||||
// for(byte[] b:blist){
|
||||
// try{
|
||||
// msg=(Message)fst.asObject(b);
|
||||
// }catch(Exception e){
|
||||
// continue;
|
||||
// }
|
||||
// System.out.println(msg);
|
||||
// sendQueue.add(msg.getOffset());
|
||||
// msgManager.getMessageCacheMap().put(msg.getOffset(), msg);
|
||||
// msgid2offset.put(msg.getMsgId(), currentOffset+i);
|
||||
// i++;
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println("恢复cost:" + (endTime - startTime));
|
||||
// }
|
||||
/**
|
||||
* 仅更新到磁盘 Subscribe之前的 offset,这部分属于 离散重试(至少中间有一条成功) ,须重试offset表存储到磁盘,broker故障重启只能离散随机恢复消息
|
||||
* 如果存在Subscribe之后的 offset,必然发生连续>=1(甚至大量)超时,连续超时将随Subscribe存储到磁盘,broker故障重启将发生连续一片(>=1条)消息恢复
|
||||
* @param start
|
||||
* @param during
|
||||
*/
|
||||
public void storeResendOffset(boolean start,long during/*ms*/){
|
||||
|
||||
}
|
||||
/**
|
||||
* 定时任务-存储订阅关系
|
||||
* @param start
|
||||
* @param during
|
||||
*/
|
||||
// public void storeSubscribe(boolean start,long during/*ms*/)
|
||||
// {
|
||||
// if(start==false)
|
||||
// return;
|
||||
// logger.debug("异步-订阅关系/消费进度定时持久化任务");
|
||||
//
|
||||
// shceduledPool.scheduleWithFixedDelay(
|
||||
// new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// ArrayList<Offset> sublist=new ArrayList<>();
|
||||
// logger.error("遍历订阅关系:"+topicAndgroup2subScribe.size());
|
||||
// for (Map.Entry<String/* topic@group */, LinkedList<Offset>> entry : topicAndgroup2subScribe.entrySet())
|
||||
// {
|
||||
// //System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
|
||||
// sublist.addAll(entry.getValue());
|
||||
// }
|
||||
// substore.write(sublist);
|
||||
// logger.error("订阅关系刷盘");
|
||||
// }
|
||||
// },
|
||||
// 5000,
|
||||
// during,
|
||||
// TimeUnit.MILLISECONDS);
|
||||
// }
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
int port = 9999;
|
||||
Broker broker=new Broker();
|
||||
// Recover recover=new Recover();
|
||||
// recover.recover();
|
||||
// SubsStore.subscribeStoreStart();
|
||||
broker.bind(port);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
public class ConsumeResult implements Serializable {
|
||||
private static final long serialVersionUID = -4489150726886715441L;
|
||||
private ConsumeStatus status=ConsumeStatus.FAIL;
|
||||
private String info;
|
||||
private String msgId;
|
||||
//来自哪个订阅集群
|
||||
private String groupId;
|
||||
//来自哪 些 订阅集群组
|
||||
private TopicAndFilter topicAndFilter;
|
||||
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
public TopicAndFilter getTopicAndFilter() {
|
||||
return topicAndFilter;
|
||||
}
|
||||
public void setTopicAndFilter(TopicAndFilter topicAndFilter) {
|
||||
this.topicAndFilter = topicAndFilter;
|
||||
}
|
||||
public void setStatus(ConsumeStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
public ConsumeStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
public String getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
public void setMsgId(String msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
public enum ConsumeStatus {
|
||||
SUCCESS,
|
||||
FAIL
|
||||
}
|
||||
56
src/main/java/com/alibaba/middleware/race/mom/Consumer.java
Normal file
56
src/main/java/com/alibaba/middleware/race/mom/Consumer.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.middleware.race.mom.util.InfoBodyConsumer;
|
||||
|
||||
public interface Consumer {
|
||||
/**
|
||||
* 启动消费者,初始化底层资源。要在属性设置和订阅操作发起之后执行
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* 发起订阅操作
|
||||
*
|
||||
* @param topic
|
||||
* 只接受该topic的消息
|
||||
* @param filter
|
||||
* 属性过滤条件,例如 area=hz,表示只接受area属性为hz的消息。消息的过滤要在服务端进行
|
||||
* @param listener
|
||||
*/
|
||||
void subscribe(String topic, String filter, MessageListener listener);
|
||||
|
||||
/**
|
||||
* 设置消费者组id,broker通过这个id来识别消费者机器
|
||||
*
|
||||
* @param groupId
|
||||
*/
|
||||
void setGroupId(String groupId);
|
||||
|
||||
/**
|
||||
* 停止消费者,broker不再投递消息给此消费者机器。
|
||||
*/
|
||||
void stop();
|
||||
|
||||
void prepare();
|
||||
|
||||
String getGroupId();
|
||||
|
||||
Map<String, String> getFilterMap();
|
||||
|
||||
void setFilterMap(Map<String, String> filterMap);
|
||||
|
||||
Message getMsg();
|
||||
|
||||
void setMsg(Message msg);
|
||||
|
||||
InfoBodyConsumer getConsumerRequestInfo();
|
||||
|
||||
void setConsumerRequestInfo(InfoBodyConsumer consumerRequestInfo);
|
||||
|
||||
String getTopic();
|
||||
|
||||
void setTopic(String topic);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.nustaq.serialization.FSTConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.serializer.RpcDecoder;
|
||||
import com.alibaba.middleware.race.mom.serializer.RpcEncoder;
|
||||
import com.alibaba.middleware.race.mom.util.InfoBodyConsumer;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
public class DefaultConsumer implements Consumer, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -4454001515077708258L;
|
||||
private static Logger logger = LoggerFactory.getLogger(DefaultConsumer.class);
|
||||
private String groupId;
|
||||
private Map<String, String> filterMap;
|
||||
transient private ChannelFuture future;
|
||||
transient private NioEventLoopGroup group;
|
||||
transient private String SIP;
|
||||
private Message msg;
|
||||
private InfoBodyConsumer consumerRequestInfo;
|
||||
private MessageListener listener;
|
||||
private String topic;
|
||||
FSTConfiguration fst=FSTConfiguration.getDefaultConfiguration();
|
||||
Bootstrap bootstrap;
|
||||
boolean reconnect=false;
|
||||
@Override
|
||||
public void start() {
|
||||
prepare();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
SIP=System.getProperty("SIP");
|
||||
if(SIP==null)
|
||||
SIP="127.0.0.1";
|
||||
System.out.println("consumer connect:"+System.getProperty("SIP"));
|
||||
group = new NioEventLoopGroup();
|
||||
try {
|
||||
bootstrap = new Bootstrap();
|
||||
bootstrap.group(group).channel(NioSocketChannel.class)
|
||||
.option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_KEEPALIVE, true)
|
||||
// .option(ChannelOption.TCP_NODELAY, true)
|
||||
// //
|
||||
// .option(ChannelOption.SO_KEEPALIVE, false)
|
||||
// //
|
||||
// .option(ChannelOption.SO_SNDBUF, 65535)
|
||||
// //
|
||||
// .option(ChannelOption.SO_RCVBUF,65535)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel channel) throws Exception {
|
||||
channel.pipeline().addLast(new RpcEncoder())
|
||||
.addLast(new RpcDecoder())
|
||||
.addLast(new SimpleChannelInboundHandler<Object>() {
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e)
|
||||
throws Exception {
|
||||
logger.error("消费者异常关闭:");
|
||||
e.printStackTrace();
|
||||
ctx.close();
|
||||
|
||||
}
|
||||
@Override
|
||||
protected void channelRead0(final ChannelHandlerContext ctx, Object info)
|
||||
throws Exception {
|
||||
logger.debug("client receive msg");
|
||||
Message msg =(Message)info;
|
||||
//返回ACK
|
||||
final ConsumeResult consumeResult=listener.onMessage(msg);
|
||||
//设置谁的 ack
|
||||
consumeResult.setMsgId(msg.getMsgId());
|
||||
/*
|
||||
*设置 consumeResult来源
|
||||
*/
|
||||
consumeResult.setGroupId(groupId);
|
||||
consumeResult.setTopicAndFilter(new TopicAndFilter(topic,filterMap));
|
||||
// if(Math.random()>0.95){
|
||||
// new Thread(new Runnable(){
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// // TODO Auto-generated method stub
|
||||
// try {
|
||||
// Thread.sleep(10000);
|
||||
//
|
||||
// ctx.writeAndFlush(consumeResult);
|
||||
// } catch (InterruptedException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }).start();;
|
||||
// }else
|
||||
ctx.writeAndFlush(consumeResult);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
connect();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("消费者抛出异常 " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void connect() throws InterruptedException
|
||||
{
|
||||
try{
|
||||
future = bootstrap.connect(SIP, 9999).sync();
|
||||
future.channel().writeAndFlush(consumerRequestInfo);
|
||||
logger.error("连接成功");
|
||||
}catch(Exception e)
|
||||
{
|
||||
// Thread.sleep(1000);
|
||||
logger.error("重新连接");
|
||||
connect();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void subscribe(String topic, String filter, MessageListener listener) {
|
||||
this.topic=topic;
|
||||
this.listener = listener;
|
||||
if (null != filter && (!"".equals(filter))) {
|
||||
filterMap = new HashMap<String, String>();
|
||||
String[] temp = filter.split("=",2);
|
||||
filterMap.put(temp[0],temp[1]);
|
||||
this.setFilterMap(filterMap);
|
||||
/*for(int i=0;i<temp.length;i++){
|
||||
filterMap.put(temp[i], temp[++i]);
|
||||
}*/
|
||||
}
|
||||
consumerRequestInfo = new InfoBodyConsumer();
|
||||
consumerRequestInfo.setFilterMap(filterMap);
|
||||
consumerRequestInfo.setTopic(topic);
|
||||
consumerRequestInfo.setGroupId(groupId);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, String> getFilterMap() {
|
||||
return filterMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setFilterMap(Map<String, String> filterMap) {
|
||||
this.filterMap = filterMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setMsg(Message msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public InfoBodyConsumer getConsumerRequestInfo() {
|
||||
return consumerRequestInfo;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setConsumerRequestInfo(InfoBodyConsumer consumerRequestInfo) {
|
||||
this.consumerRequestInfo = consumerRequestInfo;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
future.channel().closeFuture();
|
||||
future.channel().close();
|
||||
group.shutdownGracefully();
|
||||
logger.debug("消费者的释放了线程资源...");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((filterMap == null) ? 0 : filterMap.hashCode());
|
||||
result = prime * result + ((groupId == null) ? 0 : groupId.hashCode());
|
||||
result = prime * result + ((topic == null) ? 0 : topic.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
DefaultConsumer other = (DefaultConsumer) obj;
|
||||
if (filterMap == null) {
|
||||
if (other.filterMap != null)
|
||||
return false;
|
||||
} else if (!filterMap.equals(other.filterMap))
|
||||
return false;
|
||||
if (groupId == null) {
|
||||
if (other.groupId != null)
|
||||
return false;
|
||||
} else if (!groupId.equals(other.groupId))
|
||||
return false;
|
||||
if (topic == null) {
|
||||
if (other.topic != null)
|
||||
return false;
|
||||
} else if (!topic.equals(other.topic))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.LineBasedFrameDecoder;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageSend;
|
||||
import com.alibaba.middleware.race.mom.serializer.RpcEncoder;
|
||||
|
||||
|
||||
public class DefaultProducer implements Producer {
|
||||
transient private static Logger logger = LoggerFactory.getLogger(DefaultProducer.class);
|
||||
private String topic;
|
||||
private String groupId;
|
||||
private static String producerId="pro_"+String.valueOf(Math.random()*100);
|
||||
private ChannelFuture future;
|
||||
private static Random random=new Random();
|
||||
private NioEventLoopGroup group;
|
||||
private String SIP;
|
||||
private SendCallback callback;
|
||||
private ExecutorService sendPool = Executors.newFixedThreadPool(1);
|
||||
private BlockingQueue<Message> sendQueue=new LinkedBlockingQueue<Message>();
|
||||
private Map<String,BlockingQueue<SendResult>> resultMap=new ConcurrentHashMap<String, BlockingQueue<SendResult>>();
|
||||
private Bootstrap bootstrap;
|
||||
boolean isGroupMsg=false;
|
||||
private AtomicLong msgIdProduce=new AtomicLong();
|
||||
private CountDownLatch sendWait=new CountDownLatch(1);
|
||||
private Object lock=new Object();
|
||||
private Map<String,SendCallback> asyncResults=new HashMap<>();
|
||||
|
||||
|
||||
public DefaultProducer() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void asyncSendMessage(Message message, SendCallback callback) {
|
||||
Message msg = buildMessage(message, callback,1);
|
||||
//加入同步等待队列
|
||||
logger.debug("生产者准备发起请求和消息");
|
||||
// resultMap.put(message.getMsgId(), new LinkedBlockingQueue<SendResult>(1));
|
||||
asyncResults.put(msg.getMsgId(),callback);
|
||||
MessageSend msgSend=new MessageSend();
|
||||
msgSend.getSendIds().add(msg.getMsgId());
|
||||
future.channel().writeAndFlush(msgSend);
|
||||
}
|
||||
|
||||
private AtomicInteger msgCount=new AtomicInteger();
|
||||
@Override
|
||||
public SendResult sendMessage(Message message) {
|
||||
int msgCountL=msgCount.incrementAndGet();
|
||||
Message msg = buildMessage(message, null,"ackGroup", msgCountL);
|
||||
resultMap.put(message.getMsgId(), new LinkedBlockingQueue<SendResult>(1));
|
||||
sendQueue.add(msg);
|
||||
if(msgCountL>199){
|
||||
sendWait.countDown();
|
||||
}
|
||||
SendResult sr=null;
|
||||
try {
|
||||
sr = resultMap.get(message.getMsgId()).poll(5000, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if(sr==null)
|
||||
{
|
||||
sr=new SendResult();
|
||||
sr.setStatus(SendStatus.FAIL);
|
||||
}
|
||||
// synchronized(lock){
|
||||
// try {
|
||||
// lock.wait();
|
||||
// } catch (InterruptedException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
return sr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对Message 进行必要的信息设置,测试设置了groupId和topic producer.setGroupId(PID+code);producer.setTopic(topic);
|
||||
* @param msg
|
||||
* @param callback
|
||||
* @return
|
||||
*/
|
||||
private Message buildMessage (Message msg, SendCallback callback,int count) {
|
||||
if (callback != null) {
|
||||
this.callback = callback;
|
||||
logger.debug("生产者异步发送消息");
|
||||
}
|
||||
msg.setMsgId(String.valueOf(Math.random())+" "+String.valueOf(count));
|
||||
msg.setTopic(topic);
|
||||
msg.setBornTime(System.currentTimeMillis());
|
||||
return msg;
|
||||
}
|
||||
private Message buildMessage (Message msg, SendCallback callback,String qianzhui,int count) {
|
||||
if (callback != null) {
|
||||
this.callback = callback;
|
||||
logger.debug("生产者异步发送消息");
|
||||
}
|
||||
//msg.setMsgId(qianzhui+" "+String.valueOf(count));
|
||||
msg.setMsgId(String.valueOf((int)(Math.random()*999%999))+" "+String.valueOf(count));
|
||||
msg.setTopic(topic);
|
||||
msg.setBornTime(System.currentTimeMillis());
|
||||
return msg;
|
||||
}
|
||||
@Override
|
||||
public void start() {
|
||||
SIP=System.getProperty("SIP");
|
||||
if(SIP==null)
|
||||
SIP="127.0.0.1";
|
||||
System.out.println("connect:"+System.getProperty("SIP"));
|
||||
group = new NioEventLoopGroup();
|
||||
bootstrap = new Bootstrap();
|
||||
bootstrap.group(group).channel(NioSocketChannel.class)
|
||||
.option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_KEEPALIVE, true)
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
// //
|
||||
.option(ChannelOption.SO_KEEPALIVE,true)
|
||||
// //
|
||||
// .option(ChannelOption.SO_SNDBUF, 65535)
|
||||
////// //
|
||||
// .option(ChannelOption.SO_RCVBUF,65535)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel channel) throws Exception {
|
||||
channel.pipeline()
|
||||
.addLast(new RpcEncoder())
|
||||
.addLast(new LineBasedFrameDecoder(1024*256))
|
||||
.addLast(new StringDecoder())
|
||||
.addLast(new SimpleChannelInboundHandler<Object>() {
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
logger.error("producer失去broker链接");
|
||||
connect();
|
||||
}
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext arg0, Object info) throws Exception {
|
||||
|
||||
logger.debug("recv ack: "+(String)info);
|
||||
|
||||
if(info!=null/*&&info.toString().startsWith("ackGroup"*/){
|
||||
String id[]=((String)info).split("@");
|
||||
//System.out.println("收到:"+info.toString());
|
||||
//System.out.println(id.length+":len:"+id[0]+" | "+id[1]);
|
||||
if(id[0].startsWith("fail"))
|
||||
{
|
||||
//System.out.println("无人订阅");
|
||||
SendResult sr=new SendResult();
|
||||
sr.setMsgId(id[1]);
|
||||
resultMap.get(id[1]).put(sr);
|
||||
|
||||
}else
|
||||
{
|
||||
for(int i=1;i<id.length;i++)
|
||||
{
|
||||
//System.out.println(id[i]);
|
||||
SendResult sr=new SendResult();
|
||||
sr.setMsgId(id[i]);
|
||||
resultMap.get(id[i]).put(sr);
|
||||
}
|
||||
}
|
||||
// synchronized(lock){
|
||||
// lock.notifyAll();
|
||||
// }
|
||||
} else {
|
||||
// 异步方式接受数据
|
||||
if(asyncResults.containsKey((String)info)){
|
||||
SendResult sr=new SendResult();
|
||||
sr.setMsgId((String)info);
|
||||
asyncResults.get((String)info).onResult(sr);;
|
||||
}else{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
|
||||
logger.error("--生产者的异常关闭--" + cause.getMessage());
|
||||
cause.printStackTrace();
|
||||
logger.error("重新连接");
|
||||
ctx.close();
|
||||
connect();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
connect();
|
||||
}
|
||||
/**
|
||||
* 连接
|
||||
*
|
||||
*/
|
||||
public void connect() {
|
||||
try {
|
||||
future = bootstrap.connect(SIP, 9999).sync();
|
||||
logger.error("连接成功");
|
||||
// TODO Auto-generated catch block
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
logger.error("重新连接");
|
||||
connect();
|
||||
}
|
||||
|
||||
/*
|
||||
* 添加发送任务
|
||||
*/
|
||||
sendPool.execute(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
while(true){
|
||||
long start=System.currentTimeMillis();
|
||||
// messageGroup=false;
|
||||
// AtomicInteger waitTime=new AtomicInteger();
|
||||
/**
|
||||
* 这部分待完善,现在为了测tps,先不管系统的真实可用性了
|
||||
*/
|
||||
try {
|
||||
sendWait.await(30,TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
/**
|
||||
* 创建本次祖发对象,同时产生borntime
|
||||
*/
|
||||
MessageSend msgSend=new MessageSend();
|
||||
|
||||
while(true){
|
||||
logger.debug("start make messageGroup");
|
||||
Message msg=null;
|
||||
msg = sendQueue.poll();
|
||||
// if(!isGroupMsg&&msg==null){
|
||||
// break;
|
||||
// }
|
||||
if (msg==null&&msgSend.getBodys().size()>199){
|
||||
break;
|
||||
}else if (msg==null&&msgSend.getBodys().size()==1){
|
||||
try {//很有可能只有一条消息,比如函数测试,再确认一下
|
||||
msg = sendQueue.poll(10,TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if(msg==null){
|
||||
logger.error("10ms no message produced after last first message");
|
||||
break;
|
||||
}
|
||||
}else if(msg==null){
|
||||
continue;
|
||||
}
|
||||
if(msgSend.getTopic()==null||msgSend.getProperties()==null){
|
||||
msgSend.setProperties(msg.getProperties());
|
||||
msgSend.setTopic(msg.getTopic());
|
||||
}
|
||||
msgSend.getBodys().add(msg.getBody());
|
||||
msgSend.getSendIds().add(msg.getMsgId());
|
||||
}
|
||||
if(msgSend.getBodys().size()>0){
|
||||
|
||||
//msgSend.setSendId("ackGroup "+String.valueOf(msgSend.getBodys().size()));
|
||||
// logger.error((System.currentTimeMillis()-start)+"后生产者一次打包发送"+msgSend.getBodys().size());
|
||||
future.channel().writeAndFlush(msgSend);
|
||||
msgCount.set(0);
|
||||
sendWait=new CountDownLatch(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@Override
|
||||
public void stop() {
|
||||
future.channel().closeFuture();
|
||||
future.channel().close();
|
||||
group.shutdownGracefully();
|
||||
sendPool.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setGroupId(String groupId) {
|
||||
// TODO Auto-generated method stub
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSIP(String sip) {
|
||||
this.SIP = sip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
//package com.alibaba.middleware.race.mom;
|
||||
//
|
||||
//import io.netty.bootstrap.Bootstrap;
|
||||
//import io.netty.channel.ChannelFuture;
|
||||
//import io.netty.channel.ChannelHandlerContext;
|
||||
//import io.netty.channel.ChannelInitializer;
|
||||
//import io.netty.channel.ChannelOption;
|
||||
//import io.netty.channel.SimpleChannelInboundHandler;
|
||||
//import io.netty.channel.nio.NioEventLoopGroup;
|
||||
//import io.netty.channel.socket.SocketChannel;
|
||||
//import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
//import io.netty.handler.codec.LineBasedFrameDecoder;
|
||||
//import io.netty.handler.codec.string.StringDecoder;
|
||||
//
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.Map;
|
||||
//import java.util.concurrent.BlockingQueue;
|
||||
//import java.util.concurrent.ConcurrentHashMap;
|
||||
//import java.util.concurrent.ExecutorService;
|
||||
//import java.util.concurrent.Executors;
|
||||
//import java.util.concurrent.LinkedBlockingQueue;
|
||||
//import java.util.concurrent.TimeUnit;
|
||||
//import java.util.concurrent.atomic.AtomicLong;
|
||||
//
|
||||
//import org.slf4j.Logger;
|
||||
//import org.slf4j.LoggerFactory;
|
||||
//
|
||||
//import com.alibaba.middleware.race.mom.serializer.RpcEncoder;
|
||||
//import com.alibaba.middleware.race.mom.util.InfoBodyProduceList;
|
||||
//
|
||||
//
|
||||
//public class DefaultProducer1 implements Producer {
|
||||
// transient private static Logger logger = LoggerFactory.getLogger(DefaultProducer.class);
|
||||
// private String topic;
|
||||
// private String groupId;
|
||||
// private static String producerId="pro_"+String.valueOf(Math.random()*100);
|
||||
// private ChannelFuture future;
|
||||
// private NioEventLoopGroup group;
|
||||
// private String SIP;
|
||||
// Object obj=new Object();
|
||||
// private SendCallback callback;
|
||||
// private SendResult sr;
|
||||
// private Map<String,BlockingQueue<SendResult>> resultMap=new ConcurrentHashMap<String, BlockingQueue<SendResult>>();
|
||||
// private BlockingQueue<MessageInfo> sendQueue= new LinkedBlockingQueue<MessageInfo>(100);
|
||||
// private ExecutorService sendPool = Executors.newFixedThreadPool(1);
|
||||
// private Bootstrap bootstrap;
|
||||
// public DefaultProducer1() {startSend();}
|
||||
//
|
||||
// @Override
|
||||
// public void asyncSendMessage(Message message, SendCallback callback) {
|
||||
// MessageInfo msgInfo = buildMessage(message, callback);
|
||||
// //加入同步等待队列
|
||||
// logger.debug("生产者准备发起请求和消息");
|
||||
// resultMap.put(message.getMsgId(), new LinkedBlockingQueue<SendResult>(1));
|
||||
// future.channel().writeAndFlush(msgInfo);
|
||||
// }
|
||||
// /**
|
||||
// * 对Message 进行必要的信息设置,测试设置了groupId和topic producer.setGroupId(PID+code);producer.setTopic(topic);
|
||||
// * @param msg
|
||||
// * @param callback
|
||||
// * @return
|
||||
// */
|
||||
// private MessageInfo buildMessage (Message msg, SendCallback callback) {
|
||||
// if (callback != null) {
|
||||
// this.callback = callback;
|
||||
// logger.debug("生产者异步发送消息");
|
||||
// }
|
||||
//
|
||||
// msg.setTopic(topic);
|
||||
// msg.setBornTime(System.currentTimeMillis());
|
||||
// msg.makeID();
|
||||
// return new MessageInfo(msg,producerId,this.groupId);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public SendResult sendMessage(Message message) {
|
||||
// MessageInfo msgInfo = buildMessage(message, null);
|
||||
// while(!sendQueue.offer(e)){
|
||||
//
|
||||
// }
|
||||
//
|
||||
// if(sendQueue.put(msgInfo);(msgInfo)){
|
||||
// sendQueue.size()
|
||||
// }else {
|
||||
//
|
||||
// new MessageInfoQueue(sendQueue,);
|
||||
// sendQueue=new LinkedBlockingQueue<MessageInfo>(100);
|
||||
// }
|
||||
//
|
||||
// synchronized(obj){
|
||||
// try {
|
||||
// obj.wait();
|
||||
// } catch (InterruptedException e) {
|
||||
// }
|
||||
//// resultMap.put(message.getMsgId(), new LinkedBlockingQueue<SendResult>(1));
|
||||
//// SendResult sr=null;
|
||||
//// try {
|
||||
//// sr=resultMap.get(message.getMsgId()).take();
|
||||
//// } catch (InterruptedException e) {
|
||||
//// sr=new SendResult();
|
||||
//// sr.setStatus(SendStatus.FAIL);
|
||||
//// }
|
||||
//
|
||||
// return sr;}
|
||||
// }
|
||||
//
|
||||
// public void buildQueue(){
|
||||
// MessageInfoQueue msgInfoQueue=new MessageInfoQueue();
|
||||
// while(!sendQueue.isEmpty()){
|
||||
// sendQueue.
|
||||
// msgInfoQueue.
|
||||
// }
|
||||
// Message msg=sendQueue.poll();
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public void setGroupId(String groupId) {
|
||||
// // TODO Auto-generated method stub
|
||||
// this.groupId = groupId;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setSIP(String sip) {
|
||||
// this.SIP = sip;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setTopic(String topic) {
|
||||
// this.topic = topic;
|
||||
// }
|
||||
// public void startSend(){
|
||||
// sendPool.execute(new Runnable(){
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// while(true){
|
||||
// System.out.println(sendQueue.size());
|
||||
// if(sendQueue.size()==200){
|
||||
// future.channel().writeAndFlush(sendQueue);
|
||||
// sendQueue.clear();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// });
|
||||
// }
|
||||
// @Override
|
||||
// public void start() {
|
||||
//// try {
|
||||
//// Thread.sleep(500);
|
||||
//// } catch (InterruptedException e1) {
|
||||
//// e1.printStackTrace();
|
||||
//// }
|
||||
// SIP=System.getProperty("SIP");
|
||||
// if(SIP==null)
|
||||
// SIP="127.0.0.1";
|
||||
// System.out.println("connect:"+System.getProperty("SIP"));
|
||||
// group = new NioEventLoopGroup();
|
||||
// try {
|
||||
// bootstrap = new Bootstrap();
|
||||
// bootstrap.group(group).channel(NioSocketChannel.class)
|
||||
// .option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_KEEPALIVE, true)
|
||||
// .handler(new ChannelInitializer<SocketChannel>() {
|
||||
// @Override
|
||||
// public void initChannel(SocketChannel channel) throws Exception {
|
||||
// channel.pipeline()
|
||||
// .addLast(new RpcEncoder())
|
||||
// .addLast(new LineBasedFrameDecoder(1024))
|
||||
// .addLast(new StringDecoder())
|
||||
// .addLast(new SimpleChannelInboundHandler<Object>() {
|
||||
//
|
||||
// @Override
|
||||
// public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
// logger.error("producer失去broker链接");
|
||||
// connect();
|
||||
// }
|
||||
// @Override
|
||||
// protected void channelRead0(ChannelHandlerContext arg0, Object info) throws Exception {
|
||||
// // main work;
|
||||
// String msgId=info.toString();
|
||||
// sr=new SendResult();
|
||||
// sr.setMsgId(msgId);
|
||||
// synchronized(obj){
|
||||
//// DefaultProducer.this.sr=sr;
|
||||
// obj.notifyAll();
|
||||
//// obj.notify();
|
||||
// }
|
||||
//// if (null == callback) {
|
||||
//// resultMap.get(msgId).add(sr);
|
||||
//// return;
|
||||
//// } else {
|
||||
//// // 异步方式接受数据
|
||||
//// callback.onResult(sr);
|
||||
//// }
|
||||
// }
|
||||
// @Override
|
||||
// public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
//
|
||||
// logger.error("--生产者的异常关闭--" + cause.getMessage());
|
||||
// cause.printStackTrace();
|
||||
// ctx.close();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// connect();
|
||||
// } catch (InterruptedException e) {
|
||||
// logger.error("producer 抛出异常 " + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// public void connect() throws InterruptedException
|
||||
// {
|
||||
// try{
|
||||
// future = bootstrap.connect(SIP, 9999).sync();
|
||||
// logger.error("连接成功");
|
||||
// }catch(Exception e)
|
||||
// {
|
||||
// Thread.sleep(1000);
|
||||
// logger.error("重新连接");
|
||||
// connect();
|
||||
// }
|
||||
// }
|
||||
// @Override
|
||||
// public void stop() {
|
||||
// group.shutdownGracefully();
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,191 @@
|
||||
//package com.alibaba.middleware.race.mom;
|
||||
//
|
||||
//import io.netty.bootstrap.Bootstrap;
|
||||
//import io.netty.channel.ChannelFuture;
|
||||
//import io.netty.channel.ChannelHandlerContext;
|
||||
//import io.netty.channel.ChannelInitializer;
|
||||
//import io.netty.channel.ChannelOption;
|
||||
//import io.netty.channel.SimpleChannelInboundHandler;
|
||||
//import io.netty.channel.nio.NioEventLoopGroup;
|
||||
//import io.netty.channel.socket.SocketChannel;
|
||||
//import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
//import io.netty.handler.codec.LineBasedFrameDecoder;
|
||||
//import io.netty.handler.codec.string.StringDecoder;
|
||||
//
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.Map;
|
||||
//import java.util.concurrent.BlockingQueue;
|
||||
//import java.util.concurrent.ConcurrentHashMap;
|
||||
//import java.util.concurrent.ExecutorService;
|
||||
//import java.util.concurrent.Executors;
|
||||
//import java.util.concurrent.LinkedBlockingQueue;
|
||||
//import java.util.concurrent.TimeUnit;
|
||||
//import java.util.concurrent.atomic.AtomicLong;
|
||||
//
|
||||
//import org.slf4j.Logger;
|
||||
//import org.slf4j.LoggerFactory;
|
||||
//
|
||||
//import com.alibaba.middleware.race.mom.serializer.RpcEncoder;
|
||||
//import com.alibaba.middleware.race.mom.util.InfoBodyProduceList;
|
||||
//
|
||||
//
|
||||
//public class DefaultProducer2 implements Producer {
|
||||
// transient private static Logger logger = LoggerFactory.getLogger(DefaultProducer.class);
|
||||
// private String topic;
|
||||
// private String groupId;
|
||||
// private static String producerId="pro_"+String.valueOf(Math.random()*100);
|
||||
// private ChannelFuture future;
|
||||
// private NioEventLoopGroup group;
|
||||
// private String SIP;
|
||||
// private SendCallback callback;
|
||||
// private Map<String,BlockingQueue<SendResult>> resultMap=new ConcurrentHashMap<String, BlockingQueue<SendResult>>();
|
||||
// private Bootstrap bootstrap;
|
||||
// Object obj=new Object();
|
||||
// private SendResult sendResult;
|
||||
// public DefaultProducer2() {}
|
||||
//
|
||||
// @Override
|
||||
// public void asyncSendMessage(Message message, SendCallback callback) {
|
||||
// MessageInfo msgInfo = buildMessage(message, callback);
|
||||
// //加入同步等待队列
|
||||
// logger.debug("生产者准备发起请求和消息");
|
||||
// resultMap.put(message.getMsgId(), new LinkedBlockingQueue<SendResult>(1));
|
||||
// future.channel().writeAndFlush(msgInfo);
|
||||
// }
|
||||
// /**
|
||||
// * 对Message 进行必要的信息设置,测试设置了groupId和topic producer.setGroupId(PID+code);producer.setTopic(topic);
|
||||
// * @param msg
|
||||
// * @param callback
|
||||
// * @return
|
||||
// */
|
||||
// private MessageInfo buildMessage (Message msg, SendCallback callback) {
|
||||
// if (callback != null) {
|
||||
// this.callback = callback;
|
||||
// logger.debug("生产者异步发送消息");
|
||||
// }
|
||||
//
|
||||
// msg.setTopic(topic);
|
||||
// msg.setBornTime(System.currentTimeMillis());
|
||||
// msg.makeID();
|
||||
// return new MessageInfo(msg,producerId,this.groupId);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public SendResult sendMessage(Message message) {
|
||||
// MessageInfo msgInfo = buildMessage(message, null);
|
||||
// //resultMap.put(msg.getMsgId(), new LinkedBlockingQueue<SendResult>(1));
|
||||
// future.channel().writeAndFlush(msgInfo);
|
||||
// //SendResult sr=null;
|
||||
// synchronized(obj){
|
||||
// try {
|
||||
// obj.wait();
|
||||
// } catch (InterruptedException e) {
|
||||
// }
|
||||
// }
|
||||
//// try {
|
||||
//// sr=resultMap.get(msg.getMsgId()).take();
|
||||
//// } catch (InterruptedException e) {
|
||||
//// sr=new SendResult();
|
||||
//// sr.setStatus(SendStatus.FAIL);
|
||||
//// }
|
||||
// return sendResult;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setGroupId(String groupId) {
|
||||
// // TODO Auto-generated method stub
|
||||
// this.groupId = groupId;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setSIP(String sip) {
|
||||
// this.SIP = sip;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setTopic(String topic) {
|
||||
// this.topic = topic;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void start() {
|
||||
//// try {
|
||||
//// Thread.sleep(500);
|
||||
//// } catch (InterruptedException e1) {
|
||||
//// e1.printStackTrace();
|
||||
//// }
|
||||
// SIP=System.getProperty("SIP");
|
||||
// if(SIP==null)
|
||||
// SIP="127.0.0.1";
|
||||
// System.out.println("connect:"+System.getProperty("SIP"));
|
||||
// group = new NioEventLoopGroup();
|
||||
// try {
|
||||
// bootstrap = new Bootstrap();
|
||||
// bootstrap.group(group).channel(NioSocketChannel.class)
|
||||
// .option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_KEEPALIVE, true)
|
||||
// .handler(new ChannelInitializer<SocketChannel>() {
|
||||
// @Override
|
||||
// public void initChannel(SocketChannel channel) throws Exception {
|
||||
// channel.pipeline()
|
||||
// .addLast(new RpcEncoder())
|
||||
// .addLast(new LineBasedFrameDecoder(1024))
|
||||
// .addLast(new StringDecoder())
|
||||
// .addLast(new SimpleChannelInboundHandler<Object>() {
|
||||
//
|
||||
// @Override
|
||||
// public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
// logger.error("producer失去broker链接");
|
||||
// connect();
|
||||
// }
|
||||
// @Override
|
||||
// protected void channelRead0(ChannelHandlerContext arg0, Object info) throws Exception {
|
||||
// // main work;
|
||||
// String msgId=info.toString();
|
||||
// SendResult sr=new SendResult();
|
||||
// sendResult=sr;
|
||||
// sr.setMsgId(msgId);
|
||||
// if (null == callback) {
|
||||
// synchronized(obj){
|
||||
//// DefaultProducer.this.sr=sr;
|
||||
//// obj.notifyAll();
|
||||
// obj.notify();
|
||||
// }
|
||||
// //resultMap.get(result).put(sr);
|
||||
// } else {
|
||||
// // 异步方式接受数据
|
||||
// callback.onResult(sr);
|
||||
// }
|
||||
// }
|
||||
// @Override
|
||||
// public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
//
|
||||
// logger.error("--生产者的异常关闭--" + cause.getMessage());
|
||||
// cause.printStackTrace();
|
||||
// ctx.close();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// connect();
|
||||
// } catch (InterruptedException e) {
|
||||
// logger.error("producer 抛出异常 " + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// public void connect() throws InterruptedException
|
||||
// {
|
||||
// try{
|
||||
// future = bootstrap.connect(SIP, 9999).sync();
|
||||
// logger.error("连接成功");
|
||||
// }catch(Exception e)
|
||||
// {
|
||||
// Thread.sleep(1000);
|
||||
// logger.error("重新连接");
|
||||
// connect();
|
||||
// }
|
||||
// }
|
||||
// @Override
|
||||
// public void stop() {
|
||||
// group.shutdownGracefully();
|
||||
// }
|
||||
//
|
||||
//}
|
||||
82
src/main/java/com/alibaba/middleware/race/mom/Message.java
Normal file
82
src/main/java/com/alibaba/middleware/race/mom/Message.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Message ,目前只需要 三个属性,用于消费者消费(可能有遗漏,根据消费者消费即可)
|
||||
* @author youngforever
|
||||
*/
|
||||
public class Message implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1982349723732590L;
|
||||
private transient static AtomicLong msgIdProduce=new AtomicLong();
|
||||
/**
|
||||
* 测试严证,消费等需要的必要字段
|
||||
*/
|
||||
private String msgId;
|
||||
private String topic;
|
||||
private byte[] body;
|
||||
private Map<String, String> properties = new HashMap<String, String>();
|
||||
private long bornTime;
|
||||
|
||||
|
||||
// public void makeID(){
|
||||
// if(msgId==null){
|
||||
// msgId=this.topic+this.properties.toString()+"_"+String.valueOf(msgIdProduce.incrementAndGet());
|
||||
// }
|
||||
// }
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
public Message(){}
|
||||
public Message( String topic, byte[] body,
|
||||
Map<String, String> properties, long bornTime) {
|
||||
this.topic = topic;
|
||||
this.body = body;
|
||||
this.properties = properties;
|
||||
this.bornTime = bornTime;
|
||||
}
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
public byte[] getBody() {
|
||||
return body;
|
||||
}
|
||||
public void setBody(byte[] body) {
|
||||
this.body = body;
|
||||
}
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
public void setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
public void setProperty(String key,String property){
|
||||
this.properties.put(key, property);
|
||||
}
|
||||
public String getProperty(String key){
|
||||
return this.properties.get(key);
|
||||
}
|
||||
public long getBornTime() {
|
||||
return bornTime;
|
||||
}
|
||||
public void setBornTime(long bornTime) {
|
||||
this.bornTime = bornTime;
|
||||
}
|
||||
|
||||
public String getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
public void setMsgId(String msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
public String toString(){
|
||||
return this.msgId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
|
||||
public interface MessageListener {
|
||||
/**
|
||||
*
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
ConsumeResult onMessage(Message message);
|
||||
}
|
||||
20
src/main/java/com/alibaba/middleware/race/mom/Producer.java
Normal file
20
src/main/java/com/alibaba/middleware/race/mom/Producer.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
|
||||
public interface Producer {
|
||||
|
||||
void setSIP(String sip);
|
||||
|
||||
void start();
|
||||
|
||||
void setTopic(String topic);
|
||||
|
||||
void setGroupId(String groupId);
|
||||
|
||||
SendResult sendMessage(Message message);
|
||||
|
||||
void asyncSendMessage(Message message, SendCallback callback);
|
||||
|
||||
void stop();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
public class QueueMessage extends Message{
|
||||
private byte[] body;
|
||||
private long bornTime;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
public interface SendCallback {
|
||||
void onResult(SendResult result);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class SendResult implements Serializable {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 321275686710864167L;
|
||||
private String info;
|
||||
private SendStatus status=SendStatus.SUCCESS;
|
||||
private String msgId;
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
public String getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
public SendStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
public void setMsgId(String msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
public void setStatus(SendStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return "msg "+msgId+" send "+(status==SendStatus.SUCCESS?"success":"fail")+" info:"+info;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
/**
|
||||
* 发送状态
|
||||
*
|
||||
*/
|
||||
public enum SendStatus{
|
||||
SUCCESS,
|
||||
FAIL,
|
||||
TIMEOUT/*收到ack超时*/,
|
||||
/**
|
||||
* 投递中
|
||||
*/
|
||||
SEND,
|
||||
/**
|
||||
* 重投中
|
||||
*/
|
||||
RESEND,
|
||||
/**
|
||||
* 消费失败重投
|
||||
*/
|
||||
FAILRESEND,
|
||||
/**
|
||||
* 超时重投
|
||||
*/
|
||||
TIMEOUTRESEND,
|
||||
/**
|
||||
* 订阅集群所有消费者不在线
|
||||
*/
|
||||
GROUPLOST
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import com.alibaba.middleware.race.mom.config.Config;
|
||||
|
||||
/**
|
||||
* 消息堆积处理,当内存消息堆积过多,将不再内存中缓存消息,堆积减少后从硬盘拉取消息
|
||||
* 如果有大量超时消费者集群和正常快速消费集群,大量超时消息将不堆积内存,从磁盘拉取,保证不影响正常集群的消费tps
|
||||
* @author young
|
||||
*
|
||||
*/
|
||||
public class AccumulateHandler {
|
||||
public static volatile boolean full =false;
|
||||
private static Timer memoryCheckTimer=new Timer();
|
||||
/**
|
||||
* 是否可以继续内存添加消息
|
||||
* @param memoryCacheMaxOffset
|
||||
* @return
|
||||
*/
|
||||
public static void startCheckMemory(){
|
||||
memoryCheckTimer.schedule(new TimerTask(){
|
||||
@Override
|
||||
public void run() {
|
||||
if((double)Runtime.getRuntime().freeMemory()/Runtime.getRuntime().totalMemory()<(1-Config.memoryFullFactor)){//可用内存小于虚拟机内存3/4时不能再往内存堆积消息
|
||||
full=true;
|
||||
}
|
||||
}
|
||||
},5000,5000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.alibaba.middleware.race.mom.Message;
|
||||
|
||||
/**
|
||||
* 生产者生的产消息和其它信息封装
|
||||
* @author youngforever
|
||||
*
|
||||
*/
|
||||
public class MessageInfo implements Serializable{
|
||||
private static final long serialVersionUID = 529580833582395809L;
|
||||
/**
|
||||
* 由broker端设置,需要再加
|
||||
*/
|
||||
private Message msg;
|
||||
// private String producerId;
|
||||
// private String producerGroupId;
|
||||
private String msgInfoId;
|
||||
private Channel producer;
|
||||
private int queueId;
|
||||
private int offset;//存储顺序,是topicandfilter下 某个queueId 下的offset
|
||||
|
||||
|
||||
|
||||
public MessageInfo(Message msg, Channel producer){
|
||||
this.msg = msg;
|
||||
this.producer = producer;
|
||||
}
|
||||
public MessageInfo(Message msg, Channel producer, int queueId, int offset) {
|
||||
this.msg = msg;
|
||||
this.producer = producer;
|
||||
this.queueId = queueId;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* msg 订阅集群数
|
||||
*/
|
||||
// private AtomicInteger subGroupsCount;
|
||||
|
||||
// public MessageInfo(Message msg, String producerId, String producerGroupId) {
|
||||
// this.msg = msg;
|
||||
// this.producerId = producerId;
|
||||
// this.producerGroupId = producerGroupId;
|
||||
// }
|
||||
//
|
||||
public Message getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
public void setMsg(Message msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
|
||||
// public int getSubGroupsCount() {
|
||||
// return subGroupsCount.get();
|
||||
// }
|
||||
//
|
||||
// public void setSubGroupsCount(int subGroupsCount) {
|
||||
// this.subGroupsCount = new AtomicInteger(subGroupsCount);
|
||||
// }
|
||||
// public int decreSubGroupsCount(){
|
||||
// return subGroupsCount.decrementAndGet();
|
||||
// }
|
||||
// public String getProducerId() {
|
||||
// return producerId;
|
||||
// }
|
||||
|
||||
|
||||
public String getMsgInfoId() {
|
||||
return msgInfoId;
|
||||
}
|
||||
public void setMsgInfoId(String msgInfoId) {
|
||||
this.msgInfoId = msgInfoId;
|
||||
}
|
||||
public int getQueueId() {
|
||||
return queueId;
|
||||
}
|
||||
public int getQueueIndex() {
|
||||
return queueId;
|
||||
}
|
||||
|
||||
|
||||
public void setQueueId(int queueId) {
|
||||
this.queueId = queueId;
|
||||
}
|
||||
|
||||
|
||||
// public void setProducerId(String producerId) {
|
||||
// this.producerId = producerId;
|
||||
// }
|
||||
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
public void setOffset(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
|
||||
// public String getProducerGroupId() {
|
||||
// return producerGroupId;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setProducerGroupId(String producerGroupId) {
|
||||
// this.producerGroupId = producerGroupId;
|
||||
// }
|
||||
|
||||
|
||||
public Channel getProducer() {
|
||||
return producer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*由broker 端设置,连接成功后,设置生产者channel,以便精确返回ack
|
||||
* @param producer
|
||||
*/
|
||||
public void setProducer(Channel producer) {
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import com.alibaba.middleware.race.mom.QueueMessage;
|
||||
|
||||
public class MessageInfoQueue {
|
||||
private Channel producer;
|
||||
private BlockingQueue<QueueMessage> msgQueue;
|
||||
private String topic;
|
||||
private Map<String, String> properties = new HashMap<String, String>();
|
||||
private String msgQueueId;
|
||||
|
||||
|
||||
public MessageInfoQueue(BlockingQueue<QueueMessage> msgQueue, String topic,
|
||||
Map<String, String> properties) {
|
||||
super();
|
||||
this.msgQueue = msgQueue;
|
||||
this.topic = topic;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.Message;
|
||||
import com.alibaba.middleware.race.mom.broker.group.ConsumerGroup;
|
||||
import com.alibaba.middleware.race.mom.broker.group.ProducerGroup;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
public class MessageManager {
|
||||
private static org.slf4j.Logger logger = LoggerFactory.getLogger(MessageManager.class);
|
||||
|
||||
// public static Map<String/* TopicAndFilter+msgOffset */,MessageInfo> messageCacheMap;
|
||||
/**
|
||||
* 每个集群有独立的消费进度
|
||||
* 消息再broker收到后,按照TopicAndFilter 消息分类,每种TopicAndFilter消息有独立的offsetProducer
|
||||
* 如果要以queueId分多文件,那么再分一层独立的offsetProducer
|
||||
*/
|
||||
|
||||
// private MsgStore mstore = /* new MsgStoreImp();// */new MsgStoreImp_MappedBuffer();
|
||||
// private FSTConfiguration fst = FSTConfiguration.getDefaultConfiguration();
|
||||
|
||||
// private Map<String/* topic@group */,BlockingQueue<String /*id*/>/*reSendMsgQueue*/>topicAndgroup2reSendMsgQueue=new HashMap<>();
|
||||
|
||||
|
||||
|
||||
// public MessageManager(ConcurrentHashMap<String/* TopicAndFilter+msgOffset */,Message> recoverMsgMap){
|
||||
// if(recoverMsgMap!=null){
|
||||
// messageCacheMap=recoverMsgMap;
|
||||
// }else{
|
||||
// messageCacheMap=new ConcurrentHashMap<>();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 打包消息处理
|
||||
* @param msgSend
|
||||
* @param producer
|
||||
*/
|
||||
public static void recieveMsg(MessageSend msgSend,ChannelHandlerContext producer){
|
||||
long start=System.currentTimeMillis();
|
||||
/*
|
||||
* 存储处理
|
||||
*/
|
||||
msgSend.setProducer(producer.channel());
|
||||
TopicAndFilter topicAndFilter=new TopicAndFilter(msgSend.getTopic(),msgSend.getProperties());
|
||||
String queueIdAndOffsetArray[] = ProducerGroup.storeMsg(topicAndFilter,msgSend).split(" ");
|
||||
int queueIndex=Integer.parseInt(queueIdAndOffsetArray[0]);
|
||||
int offset=Integer.parseInt(queueIdAndOffsetArray[1]);
|
||||
logger.debug("currentCanUseOffset "+offset);
|
||||
/*
|
||||
* 消息进入待发送队列,等待存储完成()
|
||||
*/
|
||||
int i=0;
|
||||
for(byte[]body:msgSend.getBodys()){
|
||||
Message msg=new Message(msgSend.getTopic(),body,msgSend.getProperties(),msgSend.getBornTime());
|
||||
msg.setMsgId(msgSend.getSendIds().get(i));
|
||||
MessageInfo msgInfo=new MessageInfo(msg,producer.channel());
|
||||
msgInfo.setQueueId(queueIndex);
|
||||
msgInfo.setOffset(offset++);
|
||||
msgInfo.setMsgInfoId(topicAndFilter.toString()+queueIndex+""+String.valueOf(msgInfo.getOffset()));
|
||||
/*
|
||||
* 发送处理,加入发送队列,等待存储完成
|
||||
*/
|
||||
ConsumerGroup.sendMsg(topicAndFilter, msgInfo);
|
||||
i++;
|
||||
}
|
||||
// logger.error("消息born 到生产者recv处理完毕 cost:"+(System.currentTimeMillis()-msgSend.getBornTime()));
|
||||
// logger.error("消息recv生产者recv处理完毕 cost:"+(System.currentTimeMillis()-start));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 接收到生产者 消息,触发一系列处理
|
||||
* @param msg
|
||||
* @param producer
|
||||
*/
|
||||
// public static void recieveMsg(Message msg,ChannelHandlerContext producer){
|
||||
// //生产者信息构建的topicAndFilter
|
||||
// TopicAndFilter topicAndFilter=new TopicAndFilter(msg.getTopic(),msg.getProperties());
|
||||
//// String topicAndFilterString=topicAndFilter.toString();
|
||||
// /**
|
||||
// * broker端给消息的编号:offset,同时对应存储位置,消费进度
|
||||
// * topicAndFilter不同,offset将相互独立
|
||||
// */
|
||||
//// //broker端给消息的编号:offset,同时对应存储位置,消费进度
|
||||
//// if(offsetProducerMap.containsKey(topicAndFilterString)){
|
||||
//// int offset=offsetProducerMap.get(topicAndFilterString).getAndIncrement();
|
||||
//// msg.setOffset(offset);
|
||||
//// }
|
||||
// /**
|
||||
// * 以下两个过程,保证:
|
||||
// * 一.先加入持久化队列(以持久化队列下标编offset(对应消息在文件中的索引),保证正确对应(也不会受多线程影响)),再加入发送队列
|
||||
// * 二.保证每一条消息到磁盘(存储底层也已经强行force),再返回ack
|
||||
// * 三.发送队列与持久化队列一一对应(其实也可以允许队列均衡导致不一致,但大的来说顺序仍然一致),保证消费进度的正确性(我们可以通过配置,每一个topicandfilter下做几个队列)
|
||||
// * 四.发送给消费者与消息存储,多线程执行,不保证先后顺序(理想是并行)
|
||||
// */
|
||||
// long start=System.currentTimeMillis();
|
||||
// MessageInfo msgInfo=new MessageInfo(msg,producer.channel());
|
||||
// //持久化消息,成功后返回给生产者ack
|
||||
// String queueIdAndOffsetArray[]=ProducerGroup.storeMsg(topicAndFilter,msgInfo).split(" ");
|
||||
// int queueIndex=Integer.parseInt(queueIdAndOffsetArray[0]);
|
||||
// int offset=Integer.parseInt(queueIdAndOffsetArray[1]);
|
||||
// msgInfo.setQueueId(queueIndex);
|
||||
// msgInfo.setOffset(offset);
|
||||
// //消息对象引用存入内存map,为了避免多topicAndFilter时,map key重复,key以topicAndFilter+offset
|
||||
//// msg.setOffset(offset);
|
||||
//// MessageManager.addMessage(topicAndFilterString+String.valueOf(offset),msgInfo);
|
||||
// //转发给发送消费者
|
||||
// ConsumerGroup.sendMsg(topicAndFilter, msgInfo);
|
||||
//
|
||||
//// mstore.writeByteNormal(topicAndfilter.toString(),"t","t",0+"" , msgbytes/*caches*/);
|
||||
//// ProducerGroup.addProducer(producer.channel(),"PROO", topicAndFilter);
|
||||
//// producer.channel().writeAndFlush(msg.getMsgId() + "\r\n");
|
||||
// //接收新消息处理完成,等待消费结果,进入消费结果处理
|
||||
// logger.error("消息born 到生产者recv处理完毕 cost:"+(System.currentTimeMillis()-msg.getBornTime()));
|
||||
// logger.error("消息recv生产者recv处理完毕 cost:"+(System.currentTimeMillis()-start));
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// public static void recieveMsg(BlockingQueue<MessageInfo> msgInfoqueue,ChannelHandlerContext producer){
|
||||
// MessageInfo msgInfo=msgInfoqueue.peek();
|
||||
// //生产者信息构建的topicAndFilter
|
||||
// TopicAndFilter topicAndFilter=new TopicAndFilter(msgInfo.getMsg().getTopic(),msgInfo.getMsg().getProperties());
|
||||
// String topicAndFilterString=topicAndFilter.toString();
|
||||
// /**
|
||||
// * broker端给消息的编号:offset,同时对应存储位置,消费进度
|
||||
// * topicAndFilter不同,offset将相互独立
|
||||
// */
|
||||
//// //broker端给消息的编号:offset,同时对应存储位置,消费进度
|
||||
//// if(offsetProducerMap.containsKey(topicAndFilterString)){
|
||||
//// int offset=offsetProducerMap.get(topicAndFilterString).getAndIncrement();
|
||||
//// msg.setOffset(offset);
|
||||
//// }
|
||||
// /**
|
||||
// * 以下两个过程,保证:
|
||||
// * 一.先加入持久化队列(以持久化队列下标编offset(对应消息在文件中的索引),保证正确对应(也不会受多线程影响)),再加入发送队列
|
||||
// * 二.保证每一条消息到磁盘(存储底层也已经强行force),再返回ack
|
||||
// * 三.发送队列与持久化队列一一对应(其实也可以允许队列均衡导致不一致,但大的来说顺序仍然一致),保证消费进度的正确性(我们可以通过配置,每一个topicandfilter下做几个队列)
|
||||
// * 四.发送给消费者与消息存储,多线程执行,不保证先后顺序(理想是并行)
|
||||
// */
|
||||
// msgInfo.setProducer(producer.channel());
|
||||
// MessageInfoQueue msgQueue=new MessageInfoQueue(producer.channel(),topicAndFilter,msgInfoqueue);
|
||||
//
|
||||
// //持久化消息,成功后返回给生产者ack
|
||||
// ProducerGroup.storeMsg(topicAndFilter,msgQueue);
|
||||
//// int queueIndex=Integer.parseInt(queueIdAndOffsetArray[0]);
|
||||
//// int offset=Integer.parseInt(queueIdAndOffsetArray[1]);
|
||||
//// msgInfo.setQueueId(queueIndex);
|
||||
//// msgInfo.setOffset(offset);
|
||||
// //消息对象引用存入内存map,为了避免多topicAndFilter时,map key重复,key以topicAndFilter+offset
|
||||
//// msg.setOffset(offset);
|
||||
//// MessageManager.addMessage(topicAndFilterString+String.valueOf(offset),msgInfo);
|
||||
// //转发给发送消费者
|
||||
// ConsumerGroup.sendMsg(topicAndFilter, msgInfo);
|
||||
//
|
||||
//// mstore.writeByteNormal(topicAndfilter.toString(),"t","t",0+"" , msgbytes/*caches*/);
|
||||
//// ProducerGroup.addProducer(producer.channel(),"PROO", topicAndFilter);
|
||||
//// producer.channel().writeAndFlush(msg.getMsgId() + "\r\n");
|
||||
// //接收新消息处理完成,等待消费结果,进入消费结果处理
|
||||
// }
|
||||
|
||||
// public static void addMessage(String offet,MessageInfo msgInfo){
|
||||
// messageCacheMap.put(offet, msgInfo);
|
||||
// }
|
||||
// public static void setMsgMap(ConcurrentHashMap<String/* TopicAndFilter+msgOffset */,MessageInfo> recoverMap){
|
||||
// messageCacheMap=recoverMap;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 开始存储任务
|
||||
*/
|
||||
// long starttttt;
|
||||
// public void startStore(){
|
||||
//
|
||||
//// for(final BlockingQueue<MessageInfo> storeQueue:storeQueues){
|
||||
//// for(int i=0;i<storeThreadPoolSize/storeQueueNum;i++){//每个队列提交几次任务,占满线程池
|
||||
// storePool.execute(new Runnable(){
|
||||
// @Override
|
||||
// public void run() {
|
||||
// long start=System.currentTimeMillis();
|
||||
// AtomicInteger storeCount=new AtomicInteger();
|
||||
// BlockingQueue<MessageInfo> storeQueue=storeQueues.get(0);
|
||||
// while(true){
|
||||
// MessageInfo msgInfo=null;
|
||||
// ArrayList<byte[]> msgbytes=new ArrayList<>();
|
||||
// StoreSucceedListener storeSucceedListener=new StoreSucceedListener();
|
||||
// /**
|
||||
// * 准备一次存储过程,构建对应存储成功监听器
|
||||
// */
|
||||
//// int i=100;
|
||||
//// try {
|
||||
//// count.await(20, TimeUnit.MILLISECONDS);
|
||||
//// } catch (InterruptedException e) {
|
||||
//// count=new CountDownLatch(100);
|
||||
//// }
|
||||
//// boolean flag=false;
|
||||
//// while(!storeQueue.isEmpty()&&i-->0){
|
||||
//
|
||||
// int offset=0;
|
||||
//
|
||||
// while(true){
|
||||
//// if(storeCount.incrementAndGet()>98){
|
||||
//// System.out.println("一百条等待"+(System.currentTimeMillis()-start));
|
||||
//// break;
|
||||
//// };
|
||||
// try {
|
||||
// msgInfo=storeQueue.poll(0, TimeUnit.MILLISECONDS);
|
||||
// } catch (InterruptedException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// if(msgInfo==null&&storeCount.get()==0) continue;
|
||||
// if(msgInfo==null) continue;
|
||||
// offset=msgInfo.getOffset();
|
||||
// redo.writeLog2(offset+"", msgInfo.getMsg().getTopic(), msgInfo.getQueueId(),msgInfo.getMsg().getBody());
|
||||
//// i++;
|
||||
//
|
||||
//// storeSucceedListener.addAckWaiter(msgInfo.getProducer(), msgInfo.getMsg().getMsgId());
|
||||
// storeSucceedListener.addAckWaiterGroup(msgInfo.getProducer(), msgInfo.getMsg().getMsgId());
|
||||
//
|
||||
//// if(cando&&storeCount.get()!=0) break;
|
||||
// if(storeCount.incrementAndGet()>199) break;
|
||||
//// flag=true;
|
||||
////
|
||||
// }
|
||||
//// if(flag)
|
||||
// if(storeCount.get()==0) continue;
|
||||
// logger.error(Thread.currentThread().getName()+" "+(System.currentTimeMillis()-start)+" ms后提交新落盘任务,一次flush"+storeCount.get()+"条");
|
||||
//// storeTask(storeQueues.indexOf(storeQueue),msgbytes,storeSucceedListener,offset);
|
||||
//
|
||||
// long submit=System.currentTimeMillis();
|
||||
// if(starttttt==0){
|
||||
// starttttt=submit;
|
||||
// }
|
||||
// redo.flushLog();
|
||||
// logger.error("redo"+(System.currentTimeMillis()-starttttt));
|
||||
// storeSucceedListener.onStoreSucceed(topicAndfilter,0,offset);
|
||||
// storeCount.set(0);
|
||||
// start =System.currentTimeMillis();
|
||||
//// }else{
|
||||
//// //0就不提交了,大量废任务进入线程池。。。
|
||||
//// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//// for(int i=0;i<storeThreadPoolSize/storeQueueNum;i++){//每个队列提交几次任务,占满线程池
|
||||
// storePool.execute(new Runnable(){
|
||||
// @Override
|
||||
// public void run() {
|
||||
// long start=System.currentTimeMillis();
|
||||
// AtomicInteger storeCount=new AtomicInteger();
|
||||
// BlockingQueue<MessageInfo> storeQueue=storeQueues.get(1);
|
||||
// while(true){
|
||||
// MessageInfo msgInfo=null;
|
||||
// ArrayList<byte[]> msgbytes=new ArrayList<>();
|
||||
// StoreSucceedListener storeSucceedListener=new StoreSucceedListener();
|
||||
// /**
|
||||
// * 准备一次存储过程,构建对应存储成功监听器
|
||||
// */
|
||||
//// int i=100;
|
||||
//// try {
|
||||
//// count.await(20, TimeUnit.MILLISECONDS);
|
||||
//// } catch (InterruptedException e) {
|
||||
//// count=new CountDownLatch(100);
|
||||
//// }
|
||||
//// boolean flag=false;
|
||||
//// while(!storeQueue.isEmpty()&&i-->0){
|
||||
//
|
||||
// int offset=0;
|
||||
//
|
||||
// while(true){
|
||||
//// if(storeCount.incrementAndGet()>98){
|
||||
//// System.out.println("一百条等待"+(System.currentTimeMillis()-start));
|
||||
//// break;
|
||||
//// };
|
||||
// try {
|
||||
// msgInfo=storeQueue.poll(0, TimeUnit.MILLISECONDS);
|
||||
// } catch (InterruptedException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// if(msgInfo==null&&storeCount.get()==0) continue;
|
||||
// if(msgInfo==null) continue;
|
||||
// offset=msgInfo.getOffset();
|
||||
// redo2.writeLog2(offset+"", msgInfo.getMsg().getTopic(), msgInfo.getQueueId(),msgInfo.getMsg().getBody());
|
||||
//// i++;
|
||||
//
|
||||
//// storeSucceedListener.addAckWaiter(msgInfo.getProducer(), msgInfo.getMsg().getMsgId());
|
||||
// storeSucceedListener.addAckWaiterGroup(msgInfo.getProducer(), msgInfo.getMsg().getMsgId());
|
||||
//
|
||||
//// if(cando&&storeCount.get()!=0) break;
|
||||
// if(storeCount.incrementAndGet()>99) break;
|
||||
//// flag=true;
|
||||
////
|
||||
// }
|
||||
//// if(flag)
|
||||
// if(storeCount.get()==0) continue;
|
||||
// logger.error(Thread.currentThread().getName()+" "+(System.currentTimeMillis()-start)+" ms后提交新落盘任务,一次flush"+storeCount.get()+"条");
|
||||
//// storeTask(storeQueues.indexOf(storeQueue),msgbytes,storeSucceedListener,offset);
|
||||
// long submit=System.currentTimeMillis();
|
||||
// if(starttttt==0){
|
||||
// starttttt=submit;
|
||||
// }
|
||||
// redo2.flushLog();
|
||||
// logger.error("redo2 "+(System.currentTimeMillis()-starttttt));
|
||||
// storeSucceedListener.onStoreSucceed(topicAndfilter,1,offset);
|
||||
// storeCount.set(0);
|
||||
// start =System.currentTimeMillis();
|
||||
//// }else{
|
||||
//// //0就不提交了,大量废任务进入线程池。。。
|
||||
//// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//// }
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MessageSend implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1980989080980980L;
|
||||
private ArrayList<String> sendIds=new ArrayList<>();
|
||||
private String topic;
|
||||
private ArrayList<byte[]> bodys=new ArrayList<>();
|
||||
private Map<String, String> properties = new HashMap<String, String>();
|
||||
private long bornTime=System.currentTimeMillis();
|
||||
private Channel producer;
|
||||
|
||||
|
||||
public MessageSend(){}
|
||||
|
||||
|
||||
|
||||
|
||||
public ArrayList<String> getSendIds() {
|
||||
return sendIds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setSendIds(ArrayList<String> sendIds) {
|
||||
this.sendIds = sendIds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
public ArrayList<byte[]> getBodys() {
|
||||
return bodys;
|
||||
}
|
||||
public void setBodys(ArrayList<byte[]>bodys) {
|
||||
this.bodys = bodys;
|
||||
}
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
public void setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
public long getBornTime() {
|
||||
return bornTime;
|
||||
}
|
||||
public void setBornTime(long bornTime) {
|
||||
this.bornTime = bornTime;
|
||||
}
|
||||
public MessageSend( String topic,
|
||||
Map<String, String> properties) {
|
||||
this.topic = topic;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public MessageSend( String topic
|
||||
) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Channel getProducer() {
|
||||
return producer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setProducer(Channel producer) {
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.config.Config;
|
||||
import com.alibaba.middleware.race.mom.store.RedoLog;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
/**
|
||||
* 每个生产者集群(组)有一个MessageStore,用于存储此集群(组)(对应一种TopicAndFilter)的消息,也即每个生产者集群(组)一个storeQueues
|
||||
* @author youngforever
|
||||
*
|
||||
*/
|
||||
|
||||
public class MessageStore {
|
||||
private static Logger logger = LoggerFactory.getLogger(MessageStore.class);
|
||||
|
||||
/**
|
||||
* 注意这里要和sendqueuenum 一致,全局统一配置
|
||||
*/
|
||||
private int storeQueueNum=Config.maxqueue;
|
||||
/**
|
||||
*支持分布式队列(分布式存储任务),默认一个存储队列
|
||||
*/
|
||||
// private LinkedList<BlockingQueue<MessageInfo /*msg*/>> storeQueues = new LinkedList<>();
|
||||
private LinkedList<BlockingQueue<MessageSend /*msg*/>> storeGroupQueues = new LinkedList<>();
|
||||
private int storeThreadPoolSize = storeQueueNum;
|
||||
private AtomicInteger[] offsetMakers=new AtomicInteger[storeQueueNum];
|
||||
private ExecutorService storePool = Executors.newFixedThreadPool(storeThreadPoolSize);
|
||||
// private ExecutorService flushPool = Executors.newSingleThreadExecutor();
|
||||
private ExecutorService flushPool = Executors.newFixedThreadPool(storeQueueNum);
|
||||
// private MsgStore mstore = /* new MsgStoreImp();// */new MsgStoreImp_MappedBuffer2();
|
||||
// private FSTConfiguration fst = FSTConfiguration.getDefaultConfiguration();
|
||||
private TopicAndFilter topicAndfilter;
|
||||
// private Store store=new Store();
|
||||
// Redo redo=new Redo();
|
||||
// redotest redo=new redotest();
|
||||
RedoLog storer=new RedoLog();
|
||||
// CountDownLatch count=new CountDownLatch(1);
|
||||
volatile boolean cando=false;
|
||||
private boolean messageGroup=false;
|
||||
// private AtomicInteger recvCount=new AtomicInteger();
|
||||
|
||||
/**
|
||||
* 用于恢复时,为了衔接故障前的状态
|
||||
* @param topicAndfilter
|
||||
* @param queueMaxOffsets
|
||||
*/
|
||||
public MessageStore(TopicAndFilter topicAndfilter,Map<Integer /*queueIndex*/,Integer/*maxOffset*/> queueMaxOffsets) {
|
||||
this.topicAndfilter = topicAndfilter;
|
||||
/*
|
||||
* 创建时初始化队列
|
||||
*/
|
||||
// for(Map.Entry<Integer,Integer> queueMaxOffset:queueMaxOffsets.entrySet()){
|
||||
// this.storeQueues.add(new LinkedBlockingQueue<MessageInfo>());
|
||||
// offsetMakers[queueMaxOffset.getKey()]=new AtomicInteger(queueMaxOffset.getValue());
|
||||
// }
|
||||
for(Map.Entry<Integer,Integer> queueMaxOffset:queueMaxOffsets.entrySet()){
|
||||
this.storeGroupQueues.add(new LinkedBlockingQueue<MessageSend>());
|
||||
offsetMakers[queueMaxOffset.getKey()]=new AtomicInteger(queueMaxOffset.getValue());
|
||||
}
|
||||
// startStore();
|
||||
startStoreGroup();
|
||||
}
|
||||
/**
|
||||
* 用于新增
|
||||
* @param topicAndfilter
|
||||
*/
|
||||
public MessageStore(TopicAndFilter topicAndfilter) {
|
||||
this.topicAndfilter = topicAndfilter;
|
||||
/*
|
||||
* 创建时初始化队列
|
||||
*/
|
||||
// for(int i=0;i<storeQueueNum;i++){
|
||||
// this.storeQueues.add(new LinkedBlockingQueue<MessageInfo>());
|
||||
// offsetMakers[i]=new AtomicInteger();
|
||||
// }
|
||||
for(int i=0;i<storeQueueNum;i++){
|
||||
this.storeGroupQueues.add(new LinkedBlockingQueue<MessageSend>());
|
||||
offsetMakers[i]=new AtomicInteger();
|
||||
}
|
||||
// startStore();
|
||||
/*
|
||||
* 采用组发,则不必盯着队列
|
||||
*/
|
||||
// startStoreGroup();
|
||||
}
|
||||
/**
|
||||
* 存储队列均衡
|
||||
* @return
|
||||
*/
|
||||
// private int storeQueueBalancer=-1;
|
||||
// public synchronized BlockingQueue<MessageInfo> storeQueuesBalance(){
|
||||
// storeQueueBalancer++;
|
||||
// if(storeQueueBalancer==storeQueueNum){
|
||||
// storeQueueBalancer=0;
|
||||
// }
|
||||
// BlockingQueue<MessageInfo> storeQueue=storeQueues.get(storeQueueBalancer);
|
||||
// return storeQueue;
|
||||
// }
|
||||
private int storeGroupQueueBalancer=-1;
|
||||
public synchronized BlockingQueue<MessageSend> storeQueuesBalance(boolean isGroup){
|
||||
storeGroupQueueBalancer++;
|
||||
if(storeGroupQueueBalancer==storeQueueNum){
|
||||
storeGroupQueueBalancer=0;
|
||||
}
|
||||
BlockingQueue<MessageSend> storeGroupQueue=storeGroupQueues.get(storeGroupQueueBalancer);
|
||||
return storeGroupQueue;
|
||||
}
|
||||
/**
|
||||
* 加入均衡后的存储队列
|
||||
* @param msg
|
||||
* @return queueId+" "+offset
|
||||
*/
|
||||
// public String storeMsg(MessageInfo msgInfo){
|
||||
//// System.out.println(recvCount.incrementAndGet());
|
||||
//
|
||||
// BlockingQueue<MessageInfo> storeQueue=storeQueuesBalance();
|
||||
// int queueId=storeQueues.indexOf(storeQueue);
|
||||
// int offset;
|
||||
// synchronized(this){//同步块中进行,保证offset的准确性(存储队列中加入和offet产生的原子性)
|
||||
//// count.countDown();
|
||||
// storeQueue.offer(msgInfo);
|
||||
// offset=offsetMakers[queueId].getAndIncrement();
|
||||
// }
|
||||
// return String.valueOf(queueId)+" "+String.valueOf(offset);
|
||||
// }
|
||||
/**
|
||||
* 加入均衡后的组存 存储队列
|
||||
* @param msgSend
|
||||
* @return
|
||||
*/
|
||||
public String storeMsg(MessageSend msgSend){
|
||||
BlockingQueue<MessageSend> storeGroupQueue=storeQueuesBalance(true);
|
||||
int queueId=storeGroupQueues.indexOf(storeGroupQueue);
|
||||
int offset;
|
||||
synchronized(this){//同步块中进行,保证offset的准确性(存储队列中加入和offet产生的原子性)
|
||||
// count.countDown();
|
||||
//offset从0开始,所以返回的就是当前可用的起始offset
|
||||
offset=offsetMakers[queueId].getAndAdd(msgSend.getBodys().size());
|
||||
storeGroupQueue.offer(msgSend);
|
||||
}
|
||||
startStoreGroup();
|
||||
return String.valueOf(queueId)+" "+String.valueOf(offset);
|
||||
}
|
||||
// public void storeMsg(MessageInfoQueue msgInfoQueue){
|
||||
//// System.out.println(recvCount.incrementAndGet());
|
||||
//
|
||||
// BlockingQueue<MessageInfo> storeQueue=storeQueuesBalance();
|
||||
//// int queueId=storeQueues.indexOf(storeQueue);
|
||||
//// int offset;
|
||||
//// count.countDown();
|
||||
// storeQueue.addAll(msgInfoQueue.getMsgInfoqueue());
|
||||
//// offset=offsetMakers[queueId].getAndIncrement();
|
||||
//// }
|
||||
//// return String.valueOf(queueId)+" "+String.valueOf(offset);
|
||||
// }
|
||||
/**
|
||||
* 一次存储过程
|
||||
*/
|
||||
public void storeTask(final int queueIndex,ArrayList<byte[]> msgbytes,final StoreSucceedListener storeSucceedListener,final int offset){
|
||||
|
||||
flushPool.execute(new Runnable(){
|
||||
//
|
||||
// @Override
|
||||
public void run() {
|
||||
cando=false;
|
||||
final long start=System.currentTimeMillis();
|
||||
// store.flush();
|
||||
storer.flushLog();
|
||||
// redo.flushLog();
|
||||
// try {
|
||||
// Thread.sleep(15);
|
||||
// } catch (InterruptedException e) {
|
||||
// }
|
||||
// logger.error("一次flush cost:"+(System.currentTimeMillis()-start));
|
||||
// mstore.writeByteNormal( topicAndfilter.toString(), String.valueOf(queueIndex), msgbytes);
|
||||
cando=true;
|
||||
storeSucceedListener.onStoreSucceed(topicAndfilter,queueIndex,offset);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 组存任务开始
|
||||
*/
|
||||
public void startStoreGroup(){
|
||||
|
||||
for(final BlockingQueue<MessageSend> storeGroupQueue:storeGroupQueues){
|
||||
for(int i=0;i<storeThreadPoolSize/storeQueueNum;i++){//每个队列提交几次任务,占满线程池
|
||||
storePool.execute(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
long start=System.currentTimeMillis();
|
||||
AtomicInteger storeCount=new AtomicInteger();
|
||||
MessageSend msgSend=null;
|
||||
ArrayList<byte[]> msgbytes=new ArrayList<>();
|
||||
StoreSucceedListener storeSucceedListener=new StoreSucceedListener();
|
||||
/**
|
||||
* 准备一次存储过程,构建对应存储成功监听器
|
||||
*/
|
||||
// int i=100;
|
||||
// try {
|
||||
// count.await(20, TimeUnit.MILLISECONDS);
|
||||
// } catch (InterruptedException e) {
|
||||
// count=new CountDownLatch(100);
|
||||
// }
|
||||
// boolean flag=false;
|
||||
// while(!storeQueue.isEmpty()&&i-->0){
|
||||
//队列对应本次存储成功的条数
|
||||
int offset=0;
|
||||
|
||||
while(!storeGroupQueue.isEmpty()){
|
||||
|
||||
msgSend=storeGroupQueue.poll();
|
||||
if(msgSend==null) break;
|
||||
|
||||
storer.writeLog(msgSend.getTopic(), msgSend.getProperties().toString(),storeGroupQueues.indexOf(storeGroupQueue) , msgSend.getBodys());
|
||||
offset=msgSend.getBodys().size();
|
||||
// redo.writeLog(topic/*String*/, filter/*String*/,queueindex/*Integer*/,bodys/*(200个byte[])/*ArrayList<byte[]>*/);
|
||||
|
||||
// for(byte[] body:msgSend.getBodys()){
|
||||
//// redo.writeLog2(body);
|
||||
// offset++;
|
||||
// storeCount.incrementAndGet();
|
||||
// }
|
||||
|
||||
// i++;
|
||||
|
||||
// storeSucceedListener.addAckWaiter(msgSend.getProducer(), msgSend.getMsg().getMsgId());
|
||||
// storeSucceedListener.addAckWaiterGroup(msgSend.getProducer(), msgSend.getMsg().getMsgId());
|
||||
// storeSucceedListener.addAckWaiterNum(msgSend.getProducer());
|
||||
// if(cando&&storeCount.get()!=0) break;
|
||||
// flag=true;
|
||||
//
|
||||
storeSucceedListener.addAckGroupWaiter(msgSend.getProducer(),msgSend.getSendIds());
|
||||
}
|
||||
// if(flag)
|
||||
if(offset!=0){
|
||||
// logger.error((System.currentTimeMillis()-start)+" ms后提交新落盘任务,一次flush"+offset+"条");
|
||||
// logger.error("一共flush"+storeCount.get()*offset+"条");
|
||||
storeTask(storeGroupQueues.indexOf(storeGroupQueue),msgbytes,storeSucceedListener,offset);
|
||||
storeCount.set(0);
|
||||
// start =System.currentTimeMillis();
|
||||
// }else{
|
||||
// //0就不提交了,大量废任务进入线程池。。。
|
||||
// }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// /**
|
||||
// * 开始存储任务
|
||||
// */
|
||||
// public void startStore(){
|
||||
//
|
||||
// for(final BlockingQueue<MessageInfo> storeQueue:storeQueues){
|
||||
// for(int i=0;i<storeThreadPoolSize/storeQueueNum;i++){//每个队列提交几次任务,占满线程池
|
||||
// storePool.execute(new Runnable(){
|
||||
// @Override
|
||||
// public void run() {
|
||||
// int count=0;
|
||||
// long start=System.currentTimeMillis();
|
||||
// AtomicInteger storeCount=new AtomicInteger();
|
||||
// while(true){
|
||||
// MessageInfo msgInfo=null;
|
||||
// ArrayList<byte[]> msgbytes=new ArrayList<>();
|
||||
// StoreSucceedListener storeSucceedListener=new StoreSucceedListener();
|
||||
// /**
|
||||
// * 准备一次存储过程,构建对应存储成功监听器
|
||||
// */
|
||||
//// int i=100;
|
||||
//// try {
|
||||
//// count.await(20, TimeUnit.MILLISECONDS);
|
||||
//// } catch (InterruptedException e) {
|
||||
//// count=new CountDownLatch(100);
|
||||
//// }
|
||||
//// boolean flag=false;
|
||||
//// while(!storeQueue.isEmpty()&&i-->0){
|
||||
//
|
||||
// int offset=0;
|
||||
//
|
||||
// while(true){
|
||||
//// if(storeCount.incrementAndGet()>98){
|
||||
//// System.out.println("一百条等待"+(System.currentTimeMillis()-start));
|
||||
//// break;
|
||||
//// };
|
||||
//
|
||||
// try {
|
||||
// if(!messageGroup){
|
||||
// msgInfo=storeQueue.poll(500, TimeUnit.MILLISECONDS);
|
||||
// if(storeCount.get()>1){
|
||||
// messageGroup=true;
|
||||
// }
|
||||
// }else msgInfo=storeQueue.poll();
|
||||
//
|
||||
// } catch (InterruptedException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// if(msgInfo==null&&!messageGroup&&storeCount.get()==1)break;
|
||||
// if(msgInfo==null&&storeCount.get()==0) continue;
|
||||
// if(msgInfo==null) continue;
|
||||
// offset=msgInfo.getOffset();
|
||||
// redo.writeLog(offset+"", msgInfo.getMsg().getTopic(),msgInfo.getMsg().getBody());
|
||||
//// i++;
|
||||
//
|
||||
//// storeSucceedListener.addAckWaiter(msgInfo.getProducer(), msgInfo.getMsg().getMsgId());
|
||||
//// storeSucceedListener.addAckWaiterGroup(msgInfo.getProducer(), msgInfo.getMsg().getMsgId());
|
||||
//// storeSucceedListener.addAckWaiterNum(msgInfo.getProducer());
|
||||
//// if(cando&&storeCount.get()!=0) break;
|
||||
//// flag=true;
|
||||
// if(storeCount.incrementAndGet()>199){
|
||||
// messageGroup=true;
|
||||
// break;
|
||||
// }
|
||||
////
|
||||
// }
|
||||
//// if(flag)
|
||||
// if(storeCount.get()==0) continue;
|
||||
// count++;
|
||||
// messageGroup=true;
|
||||
//// logger.error((System.currentTimeMillis()-start)+" ms后提交新落盘任务,一次flush"+storeCount.get()+"条");
|
||||
// logger.error("一共flush"+storeCount.get()*count+"条");
|
||||
// storeTask(storeQueues.indexOf(storeQueue),msgbytes,storeSucceedListener,offset);
|
||||
// storeCount.set(0);
|
||||
// start =System.currentTimeMillis();
|
||||
//// }else{
|
||||
//// //0就不提交了,大量废任务进入线程池。。。
|
||||
//// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
public class Offset implements Serializable{
|
||||
private static final long serialVersionUID = -7988990980809880981L;
|
||||
/**
|
||||
* 消息信息,生产者集群信息
|
||||
*/
|
||||
private String topic;
|
||||
private Map<String,String> filter;
|
||||
// private TopicAndFilter topicAndFilter;
|
||||
// private String groupId;
|
||||
/**
|
||||
* 存储采用多队列多文件时采用
|
||||
*/
|
||||
private int queueIndex;
|
||||
/**
|
||||
* 消费者集群信息,姑且不需要,因为恢复 会重新发起订阅
|
||||
*/
|
||||
|
||||
/**
|
||||
* 与存储顺序(拉取索引)对应的 消费进度表示
|
||||
*/
|
||||
int currentOffset=-1; //当前消费进度
|
||||
/**
|
||||
* 存储成功的最大maxOffset;msg 的offset>此maxOffset,即为加入存储队列但是尚未存储(强行force)到磁盘,保证存储成功后,才来调用设置这个值
|
||||
*/
|
||||
int maxOffset=-1; //最大偏移(size)
|
||||
|
||||
|
||||
public Offset(){
|
||||
|
||||
};
|
||||
// public Offset(TopicAndFilter topicAndFilter){
|
||||
// this.topicAndFilter=topicAndFilter;
|
||||
// }
|
||||
|
||||
/*function*/
|
||||
public void addCacheMaxOffsetBy(int size)
|
||||
{
|
||||
// this.cacheOffset+=size;
|
||||
}
|
||||
public void addMaxOffsetBy(int size)
|
||||
{
|
||||
this.maxOffset+=size;
|
||||
}
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
public Map<String, String> getFilter() {
|
||||
return filter;
|
||||
}
|
||||
public void setFilter(Map<String, String> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
public void setCurrentOffsetBy(int size)
|
||||
{
|
||||
if(size>this.currentOffset)
|
||||
this.currentOffset=size;
|
||||
}
|
||||
// public String getGroupId() {
|
||||
// return groupId;
|
||||
// }
|
||||
// public void setGroupId(String groupId) {
|
||||
// this.groupId = groupId;
|
||||
// }
|
||||
public int getQueueIndex() {
|
||||
return queueIndex;
|
||||
}
|
||||
public void setQueueIndex(int queueIndex) {
|
||||
this.queueIndex = queueIndex;
|
||||
}
|
||||
public int getCurrentoffset() {
|
||||
return currentOffset;
|
||||
}
|
||||
public void setCurrentoffset(int currentoffset) {
|
||||
this.currentOffset = currentoffset;
|
||||
}
|
||||
// public int getCacheOffset() {
|
||||
// return cacheOffset;
|
||||
// }
|
||||
// public void setCacheOffset(int cacheOffset) {
|
||||
// this.cacheOffset = cacheOffset;
|
||||
// }
|
||||
public int getMaxOffset() {
|
||||
return maxOffset;
|
||||
}
|
||||
public void setMaxOffset(int maxOffset) {
|
||||
this.maxOffset = maxOffset;
|
||||
}
|
||||
// public String getTopic() {
|
||||
// return topic;
|
||||
// }
|
||||
// public void setTopic(String topic) {
|
||||
// this.topic = topic;
|
||||
// }
|
||||
// public TopicAndFilter getTopicAndFilter() {
|
||||
// return topicAndFilter;
|
||||
// }
|
||||
// public void setTopicAndFilter(TopicAndFilter topicAndFilter) {
|
||||
// this.topicAndFilter = topicAndFilter;
|
||||
// }
|
||||
// @Override
|
||||
// public String toString() {
|
||||
// return "Offset: [topicAndFilter=" + topicAndFilter + ", queueIndex=" + queueIndex
|
||||
// + ", currentoffset=" + currentOffset + ", MaxOffset=" + maxOffset
|
||||
// + "]";
|
||||
// }
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Offset: [topic=" + topic+"filter:"+filter + ", queueIndex=" + queueIndex
|
||||
+ ", currentoffset=" + currentOffset + ", MaxOffset=" + maxOffset
|
||||
+ "]";
|
||||
}
|
||||
// @Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + maxOffset;
|
||||
result = prime * result + currentOffset;
|
||||
// result = prime * result + ((queueIndex == 0) ? 0 : queueIndex.hashCode());
|
||||
// result = prime * result + ((topicAndFilter == null) ? 0 : topicAndFilter.hashCode());
|
||||
result = prime * result + ((topic == null) ? 0 : topic.hashCode());
|
||||
result = prime * result + ((filter == null) ? 0 : filter.hashCode());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Offset other = (Offset) obj;
|
||||
if (maxOffset != other.maxOffset)
|
||||
return false;
|
||||
// if (cacheOffset != other.cacheOffset)
|
||||
// return false;
|
||||
if (currentOffset != other.currentOffset)
|
||||
return false;
|
||||
// if (topicAndFilter == null) {
|
||||
// if (other.topicAndFilter != null)
|
||||
// return false;
|
||||
// } else if (!topicAndFilter.equals(other.topicAndFilter))
|
||||
// return false;
|
||||
if (queueIndex != other.queueIndex) {
|
||||
return false;
|
||||
}
|
||||
if (topic == null) {
|
||||
if (other.topic != null)
|
||||
return false;
|
||||
} else if (!topic.equals(other.topic))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//package com.alibaba.middleware.race.mom.broker.function;
|
||||
//
|
||||
//import io.netty.channel.Channel;
|
||||
//
|
||||
//import java.util.concurrent.BlockingQueue;
|
||||
//import java.util.concurrent.ExecutorService;
|
||||
//import java.util.concurrent.Executors;
|
||||
//import java.util.concurrent.LinkedBlockingQueue;
|
||||
//
|
||||
//import com.alibaba.middleware.race.mom.Message;
|
||||
//import com.alibaba.middleware.race.mom.broker.group.ConsumerGroup;
|
||||
//import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
///**
|
||||
// * 每个集群有自己的ReSendHandler
|
||||
// * @author youngforever
|
||||
// *
|
||||
// */
|
||||
//public class ReSendHandler {
|
||||
// private TopicAndFilter topicAndFilter;
|
||||
// private ConsumerGroup group;
|
||||
// public BlockingQueue<Integer> reSendQueue=new LinkedBlockingQueue<Integer>();
|
||||
//
|
||||
// int reSendThreadPoolSize=2;
|
||||
// private ExecutorService reSendPool = Executors.newFixedThreadPool(reSendThreadPoolSize);
|
||||
//
|
||||
//
|
||||
// public ReSendHandler(TopicAndFilter topicAndFilter, ConsumerGroup group) {
|
||||
// this.topicAndFilter = topicAndFilter;
|
||||
// this.group = group;
|
||||
// }
|
||||
// public void consumerLostReSend(){
|
||||
//
|
||||
// }
|
||||
// public void timeoutReSend(){
|
||||
//
|
||||
// }
|
||||
// public void failReSend(){
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 开始发送任务
|
||||
// */
|
||||
// public void startRend(){
|
||||
// for(int i=0;i<reSendThreadPoolSize;i++){//每个队列提交几次任务,占满线程池
|
||||
// reSendPool.execute(new Runnable(){
|
||||
// int offset;
|
||||
// Message msg;
|
||||
// Channel consumer;
|
||||
// @Override
|
||||
// public void run() {
|
||||
// while(true){
|
||||
// try {
|
||||
// offset=reSendQueue.take();
|
||||
// } catch (InterruptedException e) {
|
||||
// // 不可能出现线程中断异常的
|
||||
// Thread.interrupted();//清除中断状态
|
||||
// }
|
||||
// //取消息,发送
|
||||
// msg=MessageManager.messageCacheMap.get(topicAndFilter+String.valueOf(offset)).getMsg();
|
||||
// if(msg==null){
|
||||
// return;
|
||||
// }
|
||||
// consumer=group.consumersBalance();
|
||||
// if(consumer!=null){
|
||||
// consumer.writeAndFlush(msg);
|
||||
// group.getSendInfoMap().get(msg.getMsgId()).setSendTime(System.currentTimeMillis());
|
||||
// }else {
|
||||
//// logger.error("消费者集群都不在线,须集群恢复后重投");
|
||||
//
|
||||
// }
|
||||
//
|
||||
//// //记录发送状态信息
|
||||
//// SendInfo sendResult=new SendInfo(timeoutLimit);
|
||||
//// sendResult.setNeedSendGroupNum(ConsumerGroup.needSendGroupsNum(topicAndFilter));
|
||||
//// sendResult.setMsgOffset(msg.getOffset());
|
||||
//// sendResult.setTopicAndFilter(topicAndFilter);
|
||||
//// sendResult.setSendTime(System.currentTimeMillis());
|
||||
//// sendResult.setStatus(ConsumerGroup.this,SendStatus.SEND);
|
||||
//// sendInfoMap.put(msg.getMsgId(),sendResult);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.nustaq.serialization.FSTConfiguration;
|
||||
|
||||
import com.alibaba.middleware.race.mom.Message;
|
||||
import com.alibaba.middleware.race.mom.broker.group.ProducerGroup;
|
||||
import com.alibaba.middleware.race.mom.store.MsgStore;
|
||||
import com.alibaba.middleware.race.mom.store.MsgStoreImp_MappedBuffer2;
|
||||
import com.alibaba.middleware.race.mom.store.SubscribeStore;
|
||||
import com.alibaba.middleware.race.mom.store.SubscribeStoreImp;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
/**
|
||||
* broker 故障重启恢复
|
||||
* @author youngforever
|
||||
*
|
||||
*/
|
||||
public class Recover {
|
||||
private FSTConfiguration fst = FSTConfiguration.getDefaultConfiguration();
|
||||
private SubscribeStore substore=new SubscribeStoreImp();
|
||||
private MsgStore mstore = /* new MsgStoreImp();// */new MsgStoreImp_MappedBuffer2();
|
||||
private int maxOffset;
|
||||
|
||||
|
||||
public ConcurrentHashMap<String,Message> recover(){
|
||||
return recoverMessage(makeOffsets(recoveroffsets()));
|
||||
}
|
||||
|
||||
public ArrayList<Offset> recoveroffsets(){
|
||||
ArrayList<Offset> offsets=substore.read();
|
||||
System.out.println(offsets);
|
||||
return offsets;
|
||||
}
|
||||
/**
|
||||
* 集群组 进度重叠交叉等,对其取并集,避免从磁盘重复读取消息
|
||||
* 遍历所有恢复的Offset,做其它恢复的准备和恢复消息去重
|
||||
* @param offsets
|
||||
* @return
|
||||
*/
|
||||
public List<Offset> makeOffsets(ArrayList<Offset>offsets ){
|
||||
HashMap<TopicAndFilter,Offset>makedOffsets=new HashMap<>();
|
||||
HashMap<TopicAndFilter/*TopicAndFilter*/,Map<Integer /*queueIndex*/,Integer/*maxOffset*/>>producerGroupRecoverInfos=new HashMap<>();
|
||||
Offset makedoffset;
|
||||
TopicAndFilter topicAndFilter;
|
||||
for(Offset offset:offsets){
|
||||
topicAndFilter=new TopicAndFilter(offset.getTopic(),offset.getFilter());
|
||||
|
||||
System.out.println(offset);
|
||||
/*
|
||||
* 恢复存储进度关系
|
||||
*/
|
||||
if(producerGroupRecoverInfos.containsKey(topicAndFilter)){
|
||||
producerGroupRecoverInfos.get(topicAndFilter).put(offset.getQueueIndex(),offset.getMaxOffset());
|
||||
}else{
|
||||
Map<Integer /*queueIndex*/,Integer/*maxOffset*/> maxoffsets=new HashMap<>();
|
||||
maxoffsets.put(offset.getQueueIndex(), offset.getMaxOffset());
|
||||
producerGroupRecoverInfos.put(topicAndFilter, maxoffsets);
|
||||
}
|
||||
|
||||
if(makedOffsets.containsKey(topicAndFilter)){
|
||||
makedoffset=makedOffsets.get(topicAndFilter);
|
||||
if(offset.getCurrentoffset()<makedoffset.getCurrentoffset()){
|
||||
makedoffset.setCurrentoffset(offset.getCurrentoffset());
|
||||
}
|
||||
if(offset.getMaxOffset()>makedoffset.getMaxOffset()){
|
||||
makedoffset.setMaxOffset(offset.getMaxOffset());
|
||||
}
|
||||
}else {
|
||||
makedOffsets.put(topicAndFilter,offset);
|
||||
}
|
||||
}
|
||||
ProducerGroup.recover(producerGroupRecoverInfos);
|
||||
|
||||
return Arrays.asList(makedOffsets.values().toArray(new Offset[0]));
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String,Message> recoverMessage(List<Offset> makedOffsets){
|
||||
long startTime = System.currentTimeMillis();
|
||||
ConcurrentHashMap<String,Message> msgMap=new ConcurrentHashMap<>(1024*128);
|
||||
System.out.println("offsets:"+makedOffsets);
|
||||
int recoverNum=0;
|
||||
int queueId;
|
||||
TopicAndFilter topicAndFilter;
|
||||
for(Offset offset:makedOffsets){
|
||||
topicAndFilter=new TopicAndFilter(offset.getTopic(),offset.getFilter());
|
||||
queueId=offset.getQueueIndex();
|
||||
// //恢复topicAndgroup2subScribe
|
||||
// LinkedList<Offset> offsetList;
|
||||
// if(!topicAndgroup2subScribe.containsKey(topic+"@"+group)){
|
||||
// offsetList=new LinkedList<Offset>();
|
||||
// topicAndgroup2subScribe.put(topic+"@"+group, offsetList);
|
||||
// }else{
|
||||
// offsetList=topicAndgroup2subScribe.get(topic+"@"+group);
|
||||
// }
|
||||
// offsetList.add(Integer.parseInt(queueId),offset);
|
||||
// //恢复topicAndgroup2sendMsgQueue
|
||||
// LinkedList<BlockingQueue<String>> sendQueuesList;
|
||||
// if(!topicAndgroup2sendMsgQueue.containsKey(topic + "@" + group)){
|
||||
// sendQueuesList=new LinkedList<BlockingQueue<String>>();
|
||||
// topicAndgroup2sendMsgQueue.put(topic + "@" + group, sendQueuesList);
|
||||
// }else
|
||||
// {
|
||||
// sendQueuesList=topicAndgroup2sendMsgQueue.get(topic + "@" + group);
|
||||
// }
|
||||
// BlockingQueue<String>sendQueue=new LinkedBlockingQueue<String>();
|
||||
// sendQueuesList.add(Integer.parseInt(queueId),sendQueue);
|
||||
//消息入内存
|
||||
int currentOffset=offset.getCurrentoffset();
|
||||
int MaxOffset=offset.getMaxOffset();
|
||||
Message msg;
|
||||
int i=0;//currentOffset偏移量
|
||||
List<byte[]> blist = mstore.readByteNormal(topicAndFilter.toString(),String.valueOf(queueId),currentOffset+1, MaxOffset);
|
||||
recoverNum+=blist.size();
|
||||
System.out.println(blist.size());
|
||||
for(byte[] b:blist){
|
||||
try{
|
||||
msg=(Message)fst.asObject(b);
|
||||
System.out.println("恢复消息"+msg);
|
||||
}catch(Exception e){
|
||||
continue;
|
||||
}
|
||||
msgMap.put(new TopicAndFilter(msg.getTopic(),msg.getProperties()).toString(), msg);
|
||||
// msgid2offset.put(msg.getMsgId(), currentOffset+i);
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("恢复cost:" + (endTime - startTime)+" ,一共恢复"+recoverNum+"条");
|
||||
|
||||
return msgMap;
|
||||
}
|
||||
public int getMaxOffset() {
|
||||
return maxOffset;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.alibaba.middleware.race.mom.SendStatus;
|
||||
import com.alibaba.middleware.race.mom.config.Config;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
public class SendInfo implements Serializable {
|
||||
/**
|
||||
* 发送结果,判断消费是否超时等
|
||||
*/
|
||||
private static final long serialVersionUID = 321275686710864167L;
|
||||
|
||||
// public static final Map<String/* msgId */, SendInfo> sendInfoMap = new ConcurrentHashMap<>();
|
||||
private MessageInfo msgInfo;
|
||||
private String info;
|
||||
private SendStatus status=SendStatus.SEND;
|
||||
// private int msgOffset;
|
||||
// private String topic;
|
||||
/*
|
||||
* 订阅集群的topicAndFilter
|
||||
*/
|
||||
private TopicAndFilter topicAndFilter;
|
||||
// private int queueId;
|
||||
|
||||
private long sendTime;
|
||||
// private long recvAckTime;
|
||||
private long timeoutLimit=Config.timeoutLimit;
|
||||
private AtomicInteger reSendCounter=new AtomicInteger();
|
||||
|
||||
|
||||
public SendInfo(MessageInfo msgInfo){
|
||||
this.msgInfo=msgInfo;
|
||||
}
|
||||
/**
|
||||
* @param timeoutLimit unit:ms 允许的超时时间限制
|
||||
*/
|
||||
public SendInfo(MessageInfo msgInfo,long timeoutLimit){
|
||||
this.msgInfo=msgInfo;
|
||||
this.timeoutLimit=timeoutLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置snedResult,用于重投时重投次数增加、重置发送时间,状态等
|
||||
*
|
||||
*/
|
||||
public void reSend(){
|
||||
sendTime=System.currentTimeMillis();
|
||||
// recvAckTime=0;
|
||||
reSendCounter.incrementAndGet();
|
||||
this.status=SendStatus.RESEND;
|
||||
}
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
public SendStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
public void setStatus(SendStatus status) {
|
||||
this.status= status ;
|
||||
}
|
||||
|
||||
// public long getRecvAckTime() {
|
||||
// return recvAckTime;
|
||||
// }
|
||||
// public void setRecvAckTime(long recvAckTime) {
|
||||
// this.recvAckTime = recvAckTime;
|
||||
// }
|
||||
public void setSendTime(long sendTime){
|
||||
this.sendTime=sendTime;
|
||||
}
|
||||
public long getSendTime(){
|
||||
return this.sendTime;
|
||||
}
|
||||
public int getMsgOffset() {
|
||||
return this.msgInfo.getOffset();
|
||||
}
|
||||
public TopicAndFilter getTopicAndFilter() {
|
||||
return topicAndFilter;
|
||||
}
|
||||
public void setTopicAndFilter(TopicAndFilter topicAndFilter) {
|
||||
this.topicAndFilter = topicAndFilter;
|
||||
}
|
||||
public int getQueueId() {
|
||||
return this.msgInfo.getQueueIndex();
|
||||
}
|
||||
|
||||
public MessageInfo getMsgInfo() {
|
||||
return msgInfo;
|
||||
}
|
||||
public int getReSendCount() {
|
||||
return reSendCounter.get();
|
||||
}
|
||||
// public void decreaseNeedSendGroupsNum(){
|
||||
// needSendGroupsNum.decrementAndGet();
|
||||
// }
|
||||
/**
|
||||
* @param nowTime
|
||||
* @return 超时则 返回true;否则返回false;
|
||||
*/
|
||||
public boolean isTimeout(){
|
||||
if(System.currentTimeMillis()-sendTime>=this.timeoutLimit){
|
||||
this.status=SendStatus.TIMEOUT;
|
||||
return true;
|
||||
}else return false;
|
||||
}
|
||||
// public boolean canRemove(){
|
||||
// if(status==SendStatus.SUCCESS){
|
||||
// return true;
|
||||
// }else if(this.reSendCounter.get()>=Config.maxReSendTime){
|
||||
// return true;
|
||||
// }else return false;
|
||||
// }
|
||||
/**
|
||||
* @return 超时则 返回true;否则返回false;
|
||||
* @throws Exception :throw exception if do not receive consumer's ACK or do not set the receive ACK time:recvAckTime
|
||||
*/
|
||||
// public boolean isTimeout() throws Exception{
|
||||
// if(this.recvAckTime!=0){
|
||||
// return recvAckTime-sendTime>=this.timeoutLimit;
|
||||
// }else {
|
||||
// throw new Exception("do not recieve consumer's ACK or do not set the recieve ACK time:recvAckTime ");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
@Override
|
||||
public String toString(){
|
||||
return "msg "+msgInfo.getMsg()+" send "+status+" info:"+info;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.SendStatus;
|
||||
import com.alibaba.middleware.race.mom.broker.group.ConsumerGroup;
|
||||
import com.alibaba.middleware.race.mom.config.Config;
|
||||
import com.alibaba.middleware.race.mom.store.ArrayStore;
|
||||
import com.alibaba.middleware.race.mom.store.ArrayStoreImp;
|
||||
import com.alibaba.middleware.race.mom.store.OffsetNum;
|
||||
|
||||
|
||||
/**
|
||||
* 发送状态扫描器
|
||||
* 一.负责检测消费超时,失败等,如果超时或失败加入重投
|
||||
* 二.负责内存检测,一旦内存堆积过多超过内存大小警戒线,超时堆积消息将移除,保证系统正常运行以及正常消费tps
|
||||
* @author young
|
||||
*
|
||||
*/
|
||||
public class SendInfoScanner {
|
||||
private static Logger logger = LoggerFactory.getLogger(SendInfoScanner.class);
|
||||
|
||||
private ConsumerGroup group;
|
||||
private Timer timoutScannerTimer;
|
||||
/**
|
||||
* 重投配置
|
||||
*/
|
||||
private int timeoutLimit=10000;
|
||||
private int maxReSendTime=10000000;
|
||||
/**
|
||||
* 重发线程池大小,也许可以调整大小来削峰谷
|
||||
*/
|
||||
private int reSendPoolSize=2;
|
||||
private ExecutorService reSendPool;
|
||||
private boolean reSendStarted=false;
|
||||
/**
|
||||
* 重投队列,死信队列,记得刷盘...TO DO.......................................................................
|
||||
*/
|
||||
private BlockingQueue<MessageInfo/*msgoffset*/> reSendQueue=new LinkedBlockingQueue<MessageInfo/*msgoffset*/>();
|
||||
private BlockingQueue<MessageInfo/*msgoffset*/> deadMessageQueue=new LinkedBlockingQueue<MessageInfo/*msgoffset*/>();
|
||||
|
||||
private ArrayStore arrayStore=new ArrayStoreImp();
|
||||
private AtomicInteger reSendFailCount=new AtomicInteger();
|
||||
|
||||
/**
|
||||
* 重投恢复
|
||||
*/
|
||||
ExecutorService recoverPool=Executors.newFixedThreadPool(1);
|
||||
public SendInfoScanner(ConsumerGroup group){
|
||||
this.group=group;
|
||||
}
|
||||
public void start(){
|
||||
scanSendInfoMap();
|
||||
}
|
||||
public void scanSendInfoMap(){
|
||||
if(timoutScannerTimer==null){
|
||||
timoutScannerTimer=new Timer();
|
||||
}
|
||||
timoutScannerTimer.schedule(new TimerTask(){
|
||||
final Map <String,Map<String,ArrayList<OffsetNum>>> topicAndFilter2queues=new HashMap<>();
|
||||
Map<String,ArrayList<OffsetNum>> queue2offsets;
|
||||
ArrayList<OffsetNum> offsets;
|
||||
Map<String,SendInfo> sendInfoMap=group.getSendInfoMap();
|
||||
@Override
|
||||
public void run() {
|
||||
SendInfo sendInfo;
|
||||
for(Map.Entry<String , SendInfo> entry:sendInfoMap.entrySet()){
|
||||
sendInfo=entry.getValue();
|
||||
if(sendInfo.getStatus()==SendStatus.TIMEOUT||sendInfo.getStatus()==SendStatus.FAIL||sendInfo.isTimeout()){
|
||||
if(sendInfo.getReSendCount()<maxReSendTime){
|
||||
// logger.error("消息"+sendInfo.getMsgId()+sendInfo.getStatus()+"加入重投队列");
|
||||
if(!reSendStarted){//第一次此集群出现需要重投
|
||||
startReSend();
|
||||
reSendStarted=true;
|
||||
}
|
||||
/*
|
||||
* 内存已经超出,应该移除重投部分部分
|
||||
*/
|
||||
// if(AccumulateHandler.full){
|
||||
// if(sendInfoMap.size()>5000){
|
||||
// logger.error("内存超出"+sendInfoMap.size());
|
||||
// /*
|
||||
// * 内存不足,存储重投部分offset
|
||||
// */
|
||||
// if((queue2offsets=topicAndFilter2queues.get(sendInfo.getTopicAndFilter().toString()))!=null){
|
||||
// if((offsets=queue2offsets.get(String.valueOf(sendInfo.getQueueId())))!=null){
|
||||
// offsets.add(new OffsetNum().setI(sendInfo.getMsgOffset()));
|
||||
// }else{
|
||||
// offsets=new ArrayList<OffsetNum>();
|
||||
// offsets.add(new OffsetNum().setI(sendInfo.getMsgOffset()));
|
||||
// queue2offsets.put(String.valueOf(sendInfo.getQueueId()), offsets);
|
||||
// }
|
||||
// }else {
|
||||
// offsets=new ArrayList<OffsetNum>();
|
||||
// offsets.add(new OffsetNum().setI(sendInfo.getMsgOffset()));
|
||||
// queue2offsets=new HashMap<>();
|
||||
// queue2offsets.put(String.valueOf(sendInfo.getQueueId()), offsets);
|
||||
// topicAndFilter2queues.put(sendInfo.getTopicAndFilter().toString(), queue2offsets);
|
||||
// }
|
||||
// sendInfoMap.remove(entry.getKey());
|
||||
// /*
|
||||
// * 死信队列暂时先简单清空....................................
|
||||
// */
|
||||
// deadMessageQueue.clear();
|
||||
// }else
|
||||
{
|
||||
reSendQueue.add(sendInfo.getMsgInfo());//加入重投队列
|
||||
// logger.error(sendInfo.getMsgInfo().getMsg()+"加入重投队列");
|
||||
recoverPool.execute(new Runnable(){
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ArrayList<OffsetNum> offsets=null;
|
||||
// TODO Auto-generated method stub
|
||||
for(int i=0;i<Config.maxqueue;i++){
|
||||
if(offsets==null){
|
||||
offsets=arrayStore.recover(group.getTopicAndFilter().toString(), String.valueOf(i));
|
||||
}else offsets.addAll(arrayStore.recover(group.getTopicAndFilter().toString(), String.valueOf(i)));
|
||||
}
|
||||
logger.error("内存充足,恢复重投"+offsets);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}else{//三次重投失败
|
||||
logger.error("消息"+sendInfo.getMsgInfo().getMsg().getMsgId()+" 给集群"+group+" "+sendInfo.getReSendCount()+" 次重投失败");
|
||||
sendInfoMap.remove(sendInfo.getMsgInfo().getMsgInfoId());
|
||||
deadMessageQueue.add(sendInfo.getMsgInfo());//加入死信队列
|
||||
}
|
||||
}else{
|
||||
|
||||
}
|
||||
}
|
||||
logger.error("存储重投的offsets");
|
||||
for(Map.Entry<String, Map<String,ArrayList<OffsetNum>>> queue2offsets:topicAndFilter2queues.entrySet()){
|
||||
for(Map.Entry<String,ArrayList<OffsetNum>> offsets:queue2offsets.getValue().entrySet()){
|
||||
arrayStore.store(offsets.getValue(), queue2offsets.getKey(), offsets.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
},timeoutLimit+100,1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重投
|
||||
* @param reSendQueue
|
||||
* @param consumers
|
||||
* @param offset
|
||||
*/
|
||||
public void startReSend(){
|
||||
//第一次开启重投时创建线程池
|
||||
reSendPool = Executors.newFixedThreadPool(reSendPoolSize/*Runtime.getRuntime().availableProcessors()+2*/);
|
||||
for(int i=0;i<reSendPoolSize;i++){
|
||||
reSendPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MessageInfo msgInfo=null;
|
||||
while(true){
|
||||
// System.out.println("消费失败条数:"+consumeFailCount.get());
|
||||
// System.out.println("开始重投,重投队列长度:"+reSendQueue.size());
|
||||
try {
|
||||
msgInfo=reSendQueue.take();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.interrupted();
|
||||
}
|
||||
if(msgInfo==null) continue;
|
||||
//重投给消费者
|
||||
group.consumersBalance().writeAndFlush(msgInfo.getMsg());
|
||||
//sendResult 重置
|
||||
group.getSendInfoMap().get(msgInfo.getMsgInfoId()).reSend();
|
||||
|
||||
}//end while
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.middleware.race.mom.Broker;
|
||||
import com.alibaba.middleware.race.mom.broker.group.ConsumerGroup;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
/**
|
||||
* 存储成功监听器:force到磁盘成功,回调触发返回ack给生产者
|
||||
* 一次存储过程,对应一个监听器
|
||||
* @author youngforever
|
||||
*
|
||||
*/
|
||||
public class StoreSucceedListener {
|
||||
private static Logger logger = LoggerFactory.getLogger(StoreSucceedListener.class);
|
||||
|
||||
private static ExecutorService sendACKPool = Executors.newFixedThreadPool(1);
|
||||
private Map<Channel/*producer*/,Integer/*msgIds*/> waitAckNumMap=new HashMap<>();
|
||||
private static int i=0;
|
||||
private Map<Channel/*producer*/,List<String/*msgId*/>> waitAckMap=new HashMap<>();
|
||||
private Map<Channel/*producer*/,StringBuilder/*msgIds*/> waitAcksMap=new HashMap<>();
|
||||
private Map<Channel/*producer*/,ArrayList<String>/*sendId or ack String*/> waitAckGroupMap=new HashMap<>();
|
||||
// private StringBuilder acks=new StringBuilder();
|
||||
|
||||
|
||||
/**
|
||||
* 一次存储成功回调,存储完成(强行force)后异步返回对应的ack
|
||||
*/
|
||||
public void onStoreSucceed(TopicAndFilter topicAndFilter,int queueIndex,int offset){
|
||||
/**
|
||||
* ack返回
|
||||
*/
|
||||
sendACKPool.execute(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
// for(Channel producer:waitAckMap.keySet()){
|
||||
// for(String msgId:waitAckMap.get(producer)){
|
||||
// producer.writeAndFlush(msgId + "\r\n");
|
||||
// i++;
|
||||
//// System.out.println("存储成功,返回ack"+i+"条");
|
||||
// }
|
||||
// }
|
||||
// for(Map.Entry<Channel,StringBuilder> acks:waitAcksMap.entrySet()){
|
||||
// acks.getKey().writeAndFlush(acks.getValue().toString()+"\r\n");
|
||||
// logger.error("sendback acks"+acks.getValue().toString());
|
||||
// }
|
||||
// for(Map.Entry<Channel,Integer>acks:waitAckNumMap.entrySet()){
|
||||
// acks.getKey().writeAndFlush(acks.getValue()+"\r\n");
|
||||
//// logger.error("sendback acks"+acks.getValue().toString());
|
||||
// }
|
||||
for(Map.Entry<Channel,ArrayList<String> >ack:waitAckGroupMap.entrySet()){
|
||||
StringBuilder acks=new StringBuilder();
|
||||
for(String id:ack.getValue())
|
||||
{
|
||||
acks.append("@");
|
||||
acks.append(id);
|
||||
}
|
||||
ack.getKey().writeAndFlush(acks+"\r\n");
|
||||
//System.out.println("返回的是:"+acks);
|
||||
// logger.error("sendback acks"+acks.getValue().toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* 精确发送已存储的消息
|
||||
*/
|
||||
ConcurrentHashMap<String, ConsumerGroup> needSendGroups=ConsumerGroup.getNeedSendGroups(topicAndFilter);
|
||||
if(needSendGroups!=null)
|
||||
for(ConsumerGroup consumerGroup:needSendGroups.values()){
|
||||
// consumerGroup.startSend();
|
||||
consumerGroup.sendSpecificMsgs(queueIndex, offset);
|
||||
/**
|
||||
* 存储到文件,再来设置MaxOffset
|
||||
*/
|
||||
consumerGroup.setMaxOffset( queueIndex,offset);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 添加ack等待
|
||||
*/
|
||||
public void addAckWaiter(Channel producer,String msgId){
|
||||
List<String/*msgId*/> msgIds=waitAckMap.get(producer);
|
||||
if(msgIds==null){
|
||||
msgIds=new ArrayList<>();
|
||||
msgIds.add(msgId);
|
||||
waitAckMap.put(producer,msgIds);
|
||||
}else {
|
||||
msgIds.add(msgId);
|
||||
}
|
||||
}
|
||||
public void addAckWaiterGroup(Channel producer,String msgId){
|
||||
StringBuilder acks=waitAcksMap.get(producer);
|
||||
if(acks==null){
|
||||
acks=new StringBuilder();
|
||||
acks.append(msgId).append(" ");
|
||||
waitAcksMap.put(producer, acks);
|
||||
}else acks.append(msgId).append(" ");
|
||||
}
|
||||
public void addAckWaiterNum(Channel producer){
|
||||
Integer ackNum=waitAckNumMap.get(producer);
|
||||
logger.debug(String.valueOf(ackNum));
|
||||
if(ackNum==null){
|
||||
ackNum=new Integer(1);
|
||||
waitAckNumMap.put(producer, ackNum);
|
||||
}else waitAckNumMap.put(producer, ++ackNum);
|
||||
}
|
||||
public void addAckGroupWaiter(Channel producer,ArrayList<String> sendId){
|
||||
List<String> ack=waitAckGroupMap.get(producer);
|
||||
logger.debug(String.valueOf(ack));
|
||||
if(ack==null){
|
||||
waitAckGroupMap.put(producer, sendId);
|
||||
}else if(!ack.equals(sendId)){
|
||||
logger.error(" 两次存储并未串行,可能需要解决异步调用");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.alibaba.middleware.race.mom.broker.function;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.broker.group.ConsumerGroup;
|
||||
import com.alibaba.middleware.race.mom.store.SubscribeStore;
|
||||
import com.alibaba.middleware.race.mom.store.SubscribeStoreImp;
|
||||
|
||||
public class SubsStore {
|
||||
private static Logger logger = LoggerFactory.getLogger(SubsStore.class);
|
||||
private static SubscribeStore substore=new SubscribeStoreImp();
|
||||
private static Timer timer=new Timer();
|
||||
/**
|
||||
* 定时任务-存储订阅关系
|
||||
* @param start
|
||||
* @param during
|
||||
*/
|
||||
public static void subscribeStoreStart(){
|
||||
|
||||
|
||||
timer.schedule(new TimerTask(){
|
||||
@Override
|
||||
public void run() {
|
||||
logger.error("异步-订阅关系/消费进度定时持久化任务");
|
||||
ArrayList<Offset> sublist=new ArrayList<>();
|
||||
for(ConcurrentHashMap<String/*groupid*/,ConsumerGroup/* group */> consumerGroups:ConsumerGroup.topicAndFilter2groups.values()){
|
||||
for (ConsumerGroup consumerGroup :consumerGroups.values()){
|
||||
//System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
|
||||
sublist.addAll(consumerGroup.getConsumeOffsets());
|
||||
}
|
||||
}
|
||||
|
||||
substore.write(sublist);
|
||||
logger.error("订阅消费进度关系刷盘"+sublist);
|
||||
}
|
||||
},1000, 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
package com.alibaba.middleware.race.mom.broker.group;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.ConsumeResult;
|
||||
import com.alibaba.middleware.race.mom.Message;
|
||||
import com.alibaba.middleware.race.mom.SendStatus;
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageInfo;
|
||||
import com.alibaba.middleware.race.mom.broker.function.Offset;
|
||||
import com.alibaba.middleware.race.mom.broker.function.SendInfo;
|
||||
import com.alibaba.middleware.race.mom.broker.function.SendInfoScanner;
|
||||
import com.alibaba.middleware.race.mom.config.Config;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
/**
|
||||
* 消费者集群管理
|
||||
* @author young
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* 消费者集群管理
|
||||
* @author young
|
||||
*
|
||||
*/
|
||||
public class ConsumerGroup {
|
||||
private static Logger logger = LoggerFactory.getLogger(ConsumerGroup.class);
|
||||
|
||||
/**
|
||||
* 所有用于发送的 线程池可分配大小,也可以不限制大小,但也许应给避免订阅集群过多,发送线程池过大,不断优化测试.....
|
||||
*/
|
||||
private static int sendThreadPoolTotalSize=10;
|
||||
|
||||
|
||||
/**
|
||||
* 每个订阅集群
|
||||
*/
|
||||
private LinkedList<Channel>consumers = new LinkedList<Channel>();
|
||||
private String groupId;
|
||||
private String subscribedTopic;
|
||||
private Map<String,String> subscribedFilter;
|
||||
private TopicAndFilter topicAndFilter;
|
||||
/**
|
||||
* 每个集群的发送信息(发送状态,失败,成功,超时等)
|
||||
*/
|
||||
private Map<String/* msgId */, SendInfo> sendInfoMap = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 重投
|
||||
*/
|
||||
private SendInfoScanner sendInfoScanner=new SendInfoScanner(this);
|
||||
|
||||
// private Map<String/* topic@group */, LinkedList<Offset>> topicAndgroup2subScribe = new ConcurrentHashMap<>();
|
||||
/*
|
||||
* 每个集群的sendQueueNum个发送队列,对应消息拆分存储为sendQueueNum份文件
|
||||
* sendThreadPoolSize为分配改当前集群发送线程池大小,不断优化测试.....
|
||||
* sendPool,负责当前集群发送的线程池
|
||||
*/
|
||||
/**
|
||||
* 发送队列个数,与存储队列一一对应,须全局统一配置(错了将报错,注意!!!!)
|
||||
*/
|
||||
private int sendQueueNum=Config.maxqueue;
|
||||
// private int sendThreadPoolSize=sendQueueNum*4;
|
||||
private int sendThreadPoolSize=Runtime.getRuntime().availableProcessors()/2;
|
||||
private LinkedList<BlockingQueue<MessageInfo/* TopicAndFilter+msgOffset */>> sendQueues = new LinkedList<>();
|
||||
/**
|
||||
* 当前集群(组)的消费进度情况
|
||||
*/
|
||||
private LinkedList<Offset> consumeOffsets=new LinkedList<Offset> ();
|
||||
|
||||
private int sendQueueBalancer=-1;
|
||||
private ExecutorService sendPool = Executors.newFixedThreadPool(sendThreadPoolSize);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param subscribedTopic
|
||||
* @param subscribedFilter
|
||||
* @param groupId
|
||||
*/
|
||||
public ConsumerGroup(String subscribedTopic,Map<String,String> subscribedFilter,String groupId){
|
||||
this.subscribedTopic=subscribedTopic;
|
||||
this.subscribedFilter=subscribedFilter;
|
||||
topicAndFilter=new TopicAndFilter(subscribedTopic,subscribedFilter);
|
||||
this.groupId=groupId;
|
||||
/*
|
||||
* 集群创建时初始化队列,初始化对应的offet进度
|
||||
*/
|
||||
Offset offset;
|
||||
for(int i=0;i<sendQueueNum;i++){
|
||||
this.sendQueues.add(new LinkedBlockingQueue<MessageInfo>());
|
||||
// offset=new Offset(subscribedTopic,groupId,topicAndFilter.toString());
|
||||
offset=new Offset();
|
||||
offset.setQueueIndex(i);
|
||||
// offset.setTopic(subscribedTopic);
|
||||
this.consumeOffsets.add(offset);
|
||||
}
|
||||
/*
|
||||
* 启动发送任务,不采用立即转发方式,旺旺群讨论的结果
|
||||
*/
|
||||
// startSend();
|
||||
/*
|
||||
* 启动重投检测
|
||||
*/
|
||||
|
||||
// sendInfoScanner.start();
|
||||
}
|
||||
/**
|
||||
* 负载均衡,且去除死掉的消费者
|
||||
* @return 本次发往的消费者.如果集群都不在线,则返回null
|
||||
*/
|
||||
private volatile int consumerBalancer=-1;
|
||||
public synchronized Channel consumersBalance(){
|
||||
consumerBalancer++;
|
||||
if(consumerBalancer==consumers.size()){
|
||||
consumerBalancer=0;
|
||||
}
|
||||
Channel consumer=consumers.get(consumerBalancer);
|
||||
if (consumer==null) return null;
|
||||
if(!consumer.isOpen()){
|
||||
logger.error("消费者失去连接: " + consumer.hashCode());
|
||||
consumers.remove(consumer);
|
||||
return consumersBalance();
|
||||
}else return consumer;
|
||||
|
||||
}
|
||||
/**
|
||||
* 发送队列均衡
|
||||
* @return
|
||||
*/
|
||||
public synchronized BlockingQueue<MessageInfo> sendQueuesBalance(){
|
||||
sendQueueBalancer++;
|
||||
if(sendQueueBalancer==sendQueueNum){
|
||||
sendQueueBalancer=0;
|
||||
}
|
||||
BlockingQueue<MessageInfo> sendQueue=sendQueues.get(sendQueueBalancer);
|
||||
return sendQueue;
|
||||
}
|
||||
/**
|
||||
* 开始发送任务,此方式为收到消息,立即转发,与存储并行
|
||||
*/
|
||||
public void startSend(){
|
||||
for(final BlockingQueue<MessageInfo> sendQueue:sendQueues){
|
||||
for(int i=0;i<sendThreadPoolSize/sendQueueNum;i++){//每个队列提交几次任务,占满线程池
|
||||
sendPool.execute(new Runnable(){
|
||||
MessageInfo msgInfo;
|
||||
Message msg;
|
||||
Channel consumer;
|
||||
final int queueIndex=sendQueues.indexOf(sendQueue);
|
||||
@Override
|
||||
public void run() {
|
||||
while(true){
|
||||
while(!sendQueue.isEmpty()){
|
||||
// try {
|
||||
// msgInfo=sendQueue.take();
|
||||
msgInfo=sendQueue.poll();
|
||||
// System.out.println(sendQueue.size());
|
||||
// } catch (InterruptedException e) {
|
||||
// 不可能出现线程中断异常的
|
||||
// Thread.interrupted();//清除中断状态
|
||||
// }
|
||||
//取消息,发送
|
||||
if(msgInfo==null) break;
|
||||
msg=msgInfo.getMsg();
|
||||
// if(msg==null) continue;
|
||||
if(msg==null) break;
|
||||
consumer=consumersBalance();
|
||||
//记录发送状态信息
|
||||
SendInfo sendInfo=new SendInfo(msgInfo);
|
||||
sendInfo.setTopicAndFilter(topicAndFilter);
|
||||
sendInfo.setSendTime(System.currentTimeMillis());
|
||||
sendInfoMap.put(sendInfo.getTopicAndFilter().toString()+String.valueOf(sendInfo.getMsgOffset()),sendInfo);
|
||||
if(consumer!=null){
|
||||
consumer.writeAndFlush(msg);
|
||||
sendInfo.setStatus(SendStatus.SEND);
|
||||
}else {
|
||||
logger.error("消费者集群都不在线,须集群恢复后重投");
|
||||
sendInfo.setStatus(SendStatus.GROUPLOST);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 发送当前集群 某一队列 特定offset范围的消息
|
||||
* 此方式 为成功存储再转发
|
||||
* @param queueIndex
|
||||
* @param num 存出成功可发送条数
|
||||
*/
|
||||
public void sendSpecificMsgs(final int queueIndex,final int num){
|
||||
sendPool.execute(new Runnable(){
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int i=0;
|
||||
Channel consumer;
|
||||
MessageInfo msgInfo;
|
||||
Message msg;
|
||||
do{
|
||||
msgInfo=sendQueues.get(queueIndex).poll();
|
||||
if(msgInfo==null) break;
|
||||
msg=msgInfo.getMsg();
|
||||
if(msg==null) break;
|
||||
consumer=consumersBalance();
|
||||
consumer.writeAndFlush(msg);
|
||||
logger.debug("send msg:"+msg);
|
||||
SendInfo sendInfo=new SendInfo(msgInfo);
|
||||
sendInfo.setTopicAndFilter(topicAndFilter);
|
||||
sendInfo.setSendTime(System.currentTimeMillis());
|
||||
sendInfoMap.put(sendInfo.getMsgInfo().getMsgInfoId(),sendInfo);
|
||||
}while(++i<=num);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 设置队列的最大存储进度
|
||||
* @param queueIndex
|
||||
* @param maxOffset
|
||||
*/
|
||||
public void setMaxOffset( int queueIndex, int maxOffset){
|
||||
consumeOffsets.get(queueIndex).setMaxOffset(maxOffset);
|
||||
}
|
||||
public void handleConsumeResult(SendInfo sendInfo, ChannelHandlerContext ctx) {
|
||||
logger.error("result handle");
|
||||
String msgId = sendInfo.getTopicAndFilter().toString()+String.valueOf(sendInfo.getMsgOffset());
|
||||
/*
|
||||
* 进度仅仅在此处收到ack 更新.无论成功或失败超时(进入重试队列)
|
||||
* 假设此处未收到未更新进度,而超时扫描 到超时进入重试队列(即重试队列中消息对应offset大于此处更新的currentoffset,则必然此处currentoffset开始发生连续超时)
|
||||
* 连续超时 将不会更新到重试 随offset表(队列)更新到 重试进度,如果故障broker重启将由此处currentoffset开始 连续恢复
|
||||
*/
|
||||
// topicAndgroup2subScribe.get(topic+"@"+groupId).get(sendInfo.getQueueId()).setCurrentoffset((sendInfo.getMsgOffset()));
|
||||
/**
|
||||
* 当前集群投递成功
|
||||
*/
|
||||
if((!sendInfo.isTimeout())&&sendInfo.getStatus().equals(SendStatus.SUCCESS)){//未超时并且消费成功
|
||||
consumeOffsets.get(sendInfo.getQueueId()).setCurrentoffset(sendInfo.getMsgOffset());
|
||||
if(sendInfo.getReSendCount()!=0){
|
||||
logger.error("消息"+sendInfo.getMsgInfo().getMsg()+" 给集群"+this+" "+sendInfo.getReSendCount()+" 次重新投递成功");
|
||||
}else {
|
||||
System.out.println("消息"+sendInfo.getMsgInfo().getMsg()+" 给集群"+this+" "+sendInfo.getReSendCount()+" 次重新投递成功");
|
||||
logger.error("消息"+sendInfo.getMsgInfo().getMsgInfoId()+"一次投递消费成功");
|
||||
}
|
||||
sendInfoMap.remove(sendInfo.getMsgInfo().getMsgInfoId());
|
||||
// logger.error("sendInfoMap size:"+sendInfoMap.size());
|
||||
}else if(sendInfo.getStatus().equals(SendStatus.FAIL)){//消费失败
|
||||
consumeOffsets.get(sendInfo.getQueueId()).setCurrentoffset(sendInfo.getMsgOffset());
|
||||
// logger.error("消费失败"+consumeFailCount.incrementAndGet()+"条");
|
||||
}else if(sendInfo.isTimeout()){//消费超时,这个很有可能已经被超时扫描 给处理了,多久了才发过来
|
||||
if(sendInfoMap.containsKey(msgId)){//因为有可能已经被超时扫描 给处理了
|
||||
sendInfoMap.get(msgId).setStatus(SendStatus.TIMEOUT);
|
||||
//这里其实 由超时扫描来处理就好
|
||||
}
|
||||
// logger.error("消费超时"+consumeTimeoutCount.incrementAndGet()+"条");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 加入发送队列,queueId(queueIndex)将由存储队列均衡后的index来决定,保证了sendQueue与storeQueue的一致性,从而保证文件索引与消费进度offset的精确对应
|
||||
* @param offset
|
||||
* @param msgInfo
|
||||
*/
|
||||
public void addToSendQueue(TopicAndFilter topicAndFilter/* */,MessageInfo msgInfo){
|
||||
// this.sendQueuesBalance().add(offset);
|
||||
//获取队列和对应的Offset
|
||||
sendQueues.get(msgInfo.getQueueIndex()).add(msgInfo);
|
||||
Offset offset=consumeOffsets.get(msgInfo.getQueueIndex());
|
||||
// if(offset.getTopicAndFilter()==null){
|
||||
// offset.setTopicAndFilter(topicAndFilter);
|
||||
// }
|
||||
if(offset.getTopic()==null){
|
||||
offset.setTopic(topicAndFilter.getTopic());
|
||||
offset.setFilter(topicAndFilter.getFilter());
|
||||
}
|
||||
}
|
||||
|
||||
public void addConsumer(Channel channel){
|
||||
if(!consumers.contains(channel)){
|
||||
consumers.add(channel);
|
||||
}
|
||||
}
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public LinkedList<Offset> getConsumeOffsets() {
|
||||
return consumeOffsets;
|
||||
}
|
||||
public String getSubscribedTopic() {
|
||||
return subscribedTopic;
|
||||
}
|
||||
public void setSubscribedTopic(String subscribedTopic) {
|
||||
this.subscribedTopic = subscribedTopic;
|
||||
}
|
||||
public Map<String, String> getSubscribedFilter() {
|
||||
return subscribedFilter;
|
||||
}
|
||||
public Map<String, SendInfo> getSendInfoMap() {
|
||||
return sendInfoMap;
|
||||
}
|
||||
public void setSendInfoMap(Map<String, SendInfo> sendInfoMap) {
|
||||
this.sendInfoMap = sendInfoMap;
|
||||
}
|
||||
public void setSubscribedFilter(Map<String, String> subscribedFilter) {
|
||||
this.subscribedFilter = subscribedFilter;
|
||||
}
|
||||
public LinkedList<Channel> getConsumers() {
|
||||
return consumers;
|
||||
}
|
||||
public void setConsumers(LinkedList<Channel> consumers) {
|
||||
this.consumers = consumers;
|
||||
}
|
||||
|
||||
public int getSendQueueNum() {
|
||||
return sendQueueNum;
|
||||
}
|
||||
public void setSendQueueNum(int sendQueueNum) {
|
||||
this.sendQueueNum = sendQueueNum;
|
||||
}
|
||||
public LinkedList<BlockingQueue<MessageInfo>> getSendQueues() {
|
||||
return sendQueues;
|
||||
}
|
||||
public void setSendQueues(LinkedList<BlockingQueue<MessageInfo>> sendQueues) {
|
||||
this.sendQueues = sendQueues;
|
||||
}
|
||||
public int getSendQueueBalancer() {
|
||||
return sendQueueBalancer;
|
||||
}
|
||||
public TopicAndFilter getTopicAndFilter() {
|
||||
return topicAndFilter;
|
||||
}
|
||||
public void setTopicAndFilter(TopicAndFilter topicAndFilter) {
|
||||
this.topicAndFilter = topicAndFilter;
|
||||
}
|
||||
public void setSendQueueBalancer(int sendQueueBalancer) {
|
||||
this.sendQueueBalancer = sendQueueBalancer;
|
||||
}
|
||||
public String toString(){
|
||||
return "subscribedTopic"+subscribedTopic+" subscribedFilter"+subscribedFilter;
|
||||
}
|
||||
/**
|
||||
* 非静态部分 为某一具体集群
|
||||
* 静态部分 属于集群组,管理所有订阅集群
|
||||
*/
|
||||
/**
|
||||
* 订阅集群组,每种topicAndfilter可能有一个或多个订阅集群
|
||||
*/
|
||||
public static Map<String/* topicAndfilter */, ConcurrentHashMap<String/*groupid*/,ConsumerGroup/* group */>> topicAndFilter2groups = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 添加 topicAndFilter的订阅集群
|
||||
* @param topicAndFilter
|
||||
* @param groupId
|
||||
*/
|
||||
public static ConsumerGroup addGroup(TopicAndFilter topicAndFilter,String groupId){
|
||||
Map<String,ConsumerGroup> groups=topicAndFilter2groups.get(topicAndFilter.toString());//集群组
|
||||
ConsumerGroup group=new ConsumerGroup(topicAndFilter.getTopic(),topicAndFilter.getFilter(),groupId);
|
||||
if(groups==null){
|
||||
groups=new ConcurrentHashMap<String,ConsumerGroup>();
|
||||
groups.put(groupId,group);
|
||||
topicAndFilter2groups.put(topicAndFilter.toString(), (ConcurrentHashMap<String, ConsumerGroup>) groups);
|
||||
}else if(!groups.containsKey(groupId)){
|
||||
groups.put(groupId, group);
|
||||
}
|
||||
// else {//集群已经存在,这种情况可能发生在集群全部掉线,而后恢复重连.或者集群新增消费者
|
||||
// group=groups.get(groupId);
|
||||
// }
|
||||
return group;
|
||||
}
|
||||
public static ConcurrentHashMap<String,ConsumerGroup> getGroups(TopicAndFilter topicAndFilter){
|
||||
return topicAndFilter2groups.get(topicAndFilter.toString());
|
||||
}
|
||||
public static ConsumerGroup getGroup(TopicAndFilter topicAndFilter,String groupId){
|
||||
Map<String,ConsumerGroup> groups=getGroups(topicAndFilter);
|
||||
Map<String,ConsumerGroup> groupsNoFilter=getGroups(new TopicAndFilter(topicAndFilter.getTopic(),null));
|
||||
ConsumerGroup group;
|
||||
if(groupsNoFilter==null&&groups==null){
|
||||
return null;
|
||||
}else if(groups!=null&&(group=groups.get(groupId))!=null){
|
||||
return group;
|
||||
}else if(groupsNoFilter!=null){
|
||||
return groupsNoFilter.get(groupId);
|
||||
}else return null;
|
||||
}
|
||||
/**
|
||||
* 取得filter 和null两种needSend订阅集群组
|
||||
* @param topicAndFilter
|
||||
* @return
|
||||
*/
|
||||
public static ConcurrentHashMap<String,ConsumerGroup> getNeedSendGroups(TopicAndFilter topicAndFilter){
|
||||
ConcurrentHashMap<String,ConsumerGroup> groups=getGroups(topicAndFilter);
|
||||
TopicAndFilter topicNoFilter=new TopicAndFilter (topicAndFilter.getTopic(),null);
|
||||
ConcurrentHashMap<String,ConsumerGroup> noFilterGroups=getGroups(topicNoFilter);
|
||||
|
||||
|
||||
if(groups!=null&&noFilterGroups!=null){
|
||||
groups.putAll(getGroups(topicNoFilter));
|
||||
}else if(noFilterGroups!=null){
|
||||
groups=noFilterGroups;
|
||||
}
|
||||
// if(groups!=null)
|
||||
// for(ConsumerGroup group:groups.values()){
|
||||
// if(group.consumers.isEmpty()){
|
||||
// groups.remove(group.getGroupId());
|
||||
// }
|
||||
// }
|
||||
return groups;
|
||||
}
|
||||
/**
|
||||
* 取得 此topicAndFilter下所有负载均衡后须发送的消费者
|
||||
* @param topicAndFilter
|
||||
* @return
|
||||
*/
|
||||
public static LinkedList<Channel> getNeedSendBalancedConsumers(TopicAndFilter topicAndFilter){
|
||||
LinkedList<Channel> needSendBalancedConsumers=new LinkedList<Channel>();
|
||||
Map<String,ConsumerGroup>groups=getNeedSendGroups(topicAndFilter);
|
||||
for(ConsumerGroup consumerGroup:groups.values()){//每个须发送集群都要负载均衡找出消费者
|
||||
Channel consumer=consumerGroup.consumersBalance();
|
||||
if(consumer!=null){
|
||||
needSendBalancedConsumers.add(consumer);
|
||||
}else {
|
||||
logger.error("消费者集群"+consumerGroup+"全部失去连接");
|
||||
}
|
||||
}
|
||||
return needSendBalancedConsumers;
|
||||
}
|
||||
public static int needSendGroupsNum(TopicAndFilter topicAndFilter){
|
||||
return getNeedSendGroups(topicAndFilter).size();
|
||||
}
|
||||
// public static boolean contains(TopicAndFilter topicAndFilter){
|
||||
// return topicAndFilter2groups.containsKey(topicAndFilter.toString());
|
||||
// }
|
||||
/**
|
||||
* 注册订阅者(消费者)
|
||||
* @param consumer
|
||||
* @param groupId
|
||||
* @param topicAndFilter
|
||||
*/
|
||||
public static void addConsumer(Channel consumer ,String groupId,TopicAndFilter topicAndFilter){
|
||||
ConsumerGroup group=getGroup(topicAndFilter,groupId);
|
||||
if(group!=null){
|
||||
if(group.consumers.size()==0){
|
||||
logger.error("消费者集群全部down机后恢复,触发消费者离线重投");
|
||||
/**
|
||||
* TO DO..........................................................
|
||||
*/
|
||||
}
|
||||
group.addConsumer(consumer);
|
||||
}else addGroup(topicAndFilter,groupId).addConsumer(consumer);
|
||||
}
|
||||
/**
|
||||
* 消费结果总处理
|
||||
* @param consumeResult
|
||||
* @param ctx
|
||||
*/
|
||||
public static void confirmConsumer(ConsumeResult consumeResult, ChannelHandlerContext ctx){
|
||||
logger.debug("recv consume result");
|
||||
String msgId = consumeResult.getMsgId();
|
||||
ConsumerGroup group=ConsumerGroup.getGroup(consumeResult.getTopicAndFilter(),consumeResult.getGroupId());
|
||||
SendInfo sendInfo=group.sendInfoMap.get(msgId);
|
||||
if(sendInfo==null) return;
|
||||
switch (consumeResult.getStatus()){
|
||||
case SUCCESS:sendInfo.setStatus( SendStatus.SUCCESS); break;
|
||||
case FAIL:sendInfo.setStatus( SendStatus.FAIL); break;
|
||||
default:logger.error("消费者 无消费状态.......");break;
|
||||
}
|
||||
group.handleConsumeResult(sendInfo, ctx);
|
||||
}
|
||||
/**
|
||||
* 准备消息发送(进入发送队列),存储完成后发送
|
||||
* @param topicAndFilter
|
||||
* @param msgInfo
|
||||
*/
|
||||
private static long waitSubStartTime;
|
||||
public static void sendMsg(TopicAndFilter topicAndFilter,MessageInfo msgInfo){
|
||||
//获取消息应给发往的订阅集群组,即所有订阅此消息的集群
|
||||
Map<String,ConsumerGroup> groups=ConsumerGroup.getNeedSendGroups(topicAndFilter);
|
||||
if(groups==null){
|
||||
if(waitSubStartTime==0){
|
||||
waitSubStartTime=System.currentTimeMillis();
|
||||
}else if(System.currentTimeMillis()-waitSubStartTime>Config.noConsumerWaitTimeLimit){//800ms后仍无订阅者信息,则递归结束,
|
||||
msgInfo.getProducer().writeAndFlush("fail@"+msgInfo.getMsg().getMsgId()+ "\r\n");
|
||||
logger.error(Config.noConsumerWaitTimeLimit+"ms 任无此"+topicAndFilter+"的订阅集群(组)发起订阅");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
//logger.error("没有此"+topicAndFilter+"的订阅集群(组)");
|
||||
sendMsg(topicAndFilter,msgInfo);//递归不断尝试获取订阅者集群
|
||||
}else{
|
||||
//设置需要发送的订阅集群数
|
||||
// msgInfo.setSubGroupsCount(groups.size());
|
||||
//加入各订阅集群的发送队列,自动发送给消费者
|
||||
logger.debug((System.currentTimeMillis()-waitSubStartTime)+"发现订阅信息");
|
||||
for(ConsumerGroup consumerGroup:groups.values()){
|
||||
consumerGroup.addToSendQueue(topicAndFilter,msgInfo);
|
||||
}
|
||||
}
|
||||
waitSubStartTime=0;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 以groupId区分俩个组
|
||||
// */
|
||||
// @Override
|
||||
// public boolean equals(Object obj) {
|
||||
// if(obj instanceof ConsumerGroup){
|
||||
// if(((ConsumerGroup) obj).getGroupId().equals(this.groupId))return true;
|
||||
// else return false;
|
||||
// }else return false;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package com.alibaba.middleware.race.mom.broker.group;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageInfo;
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageInfoQueue;
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageSend;
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageStore;
|
||||
import com.alibaba.middleware.race.mom.util.TopicAndFilter;
|
||||
|
||||
|
||||
/**
|
||||
* 生产者集群(组)管理
|
||||
* 假设存在多个集群生产同一种topicAndfilter的消息,broker端可将集群组视为同一集群,不做集群区分,只以topicAndfilter区分
|
||||
* 消息存储以topicAndfilter将生产者消息归类存储
|
||||
* ack返回,我们假定必须返回相应生产者(消息的直接生产者),而不是集群中任一生产者
|
||||
*
|
||||
* @author youngforever
|
||||
*/
|
||||
public class ProducerGroup {
|
||||
private static Logger logger = LoggerFactory.getLogger(ProducerGroup.class);
|
||||
/**
|
||||
* 每个生产者集群
|
||||
*/
|
||||
// private Map<String,/*producerId*/Channel/*producer*/>producers = new HashMap<>();
|
||||
// private String groupId;
|
||||
// private String produceTopic;
|
||||
// private Map<String,String> produceFilter;
|
||||
private TopicAndFilter produceTopicAndFilter;
|
||||
private MessageStore msgStore ;
|
||||
|
||||
/**
|
||||
* 私有构造函数,仅用于恢复时
|
||||
* @param produceTopic
|
||||
* @param produceFilter
|
||||
* @param queueMaxOffsets
|
||||
*/
|
||||
private ProducerGroup(TopicAndFilter topicAndFilter,Map<Integer /*queueIndex*/,Integer/*maxOffset*/> queueMaxOffsets){
|
||||
this.produceTopicAndFilter=topicAndFilter;
|
||||
this.msgStore=new MessageStore(produceTopicAndFilter, queueMaxOffsets);
|
||||
|
||||
}
|
||||
/**
|
||||
* 用于新增生产者集群
|
||||
* @param produceTopic
|
||||
* @param produceFilter
|
||||
* @param queueMaxOffsets
|
||||
*/
|
||||
public ProducerGroup(String produceTopic,Map<String,String> produceFilter){
|
||||
// this.produceTopic=produceTopic;
|
||||
// this.produceFilter=produceFilter;
|
||||
this.produceTopicAndFilter=new TopicAndFilter(produceTopic,produceFilter);
|
||||
this.msgStore=new MessageStore(produceTopicAndFilter);
|
||||
}
|
||||
// public void addProducer(Channel producer,String producerId){
|
||||
// if(!producers.containsKey(producerId)){
|
||||
// producers.put(producerId,producer);
|
||||
// }else if(!producers.get(producerId).isOpen()){//已经存在此id 的producer,但是关闭了连接. 现在应该时发起了重连
|
||||
// producers.put(producerId,producer);
|
||||
// }
|
||||
// }
|
||||
public String toString(){
|
||||
return produceTopicAndFilter.toString();
|
||||
}
|
||||
/**
|
||||
* 非静态部分 为某一具体集群
|
||||
* 静态部分 属于集群组,管理所有生产者集群
|
||||
*/
|
||||
public static Map<String/* topicAndfilter */, ProducerGroup/* group */> topicAndFilter2group = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 添加 topicAndFilter的订阅集群
|
||||
* @param topicAndFilter
|
||||
* @param groupId
|
||||
*/
|
||||
public static ProducerGroup addGroup(TopicAndFilter topicAndFilter){
|
||||
ProducerGroup group=topicAndFilter2group.get(topicAndFilter.toString());//集群组
|
||||
if(group==null){
|
||||
group=new ProducerGroup(topicAndFilter.getTopic(),topicAndFilter.getFilter());
|
||||
topicAndFilter2group.put(topicAndFilter.toString(),group);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
/**
|
||||
* 仅用于恢复时
|
||||
* @param topicAndFilter
|
||||
* @param queueMaxOffsets
|
||||
* @return
|
||||
*/
|
||||
private static ProducerGroup addGroup(TopicAndFilter topicAndFilter,Map<Integer /*queueIndex*/,Integer/*maxOffset*/> queueMaxOffsets){
|
||||
ProducerGroup group=topicAndFilter2group.get(topicAndFilter.toString());//集群组
|
||||
if(group==null){
|
||||
group=new ProducerGroup(topicAndFilter,queueMaxOffsets);
|
||||
topicAndFilter2group.put(topicAndFilter.toString(),group);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
public static ProducerGroup getGroup(TopicAndFilter topicAndFilter){
|
||||
return topicAndFilter2group.get(topicAndFilter.toString());
|
||||
}
|
||||
/**
|
||||
* 注册生产者
|
||||
* @param producer
|
||||
* @param groupId
|
||||
* @param topicAndFilter
|
||||
*/
|
||||
/*
|
||||
* 采用了 存储成功监听器机制,那么生产者实际上不用保存
|
||||
*/
|
||||
// public static void addProducer(Channel producer ,String producerId,String producerGroupId,TopicAndFilter topicAndFilter){
|
||||
// ProducerGroup group=getGroup(topicAndFilter);
|
||||
// if(group!=null){
|
||||
// group.addProducer(producer,producerId);
|
||||
// }else addGroup(topicAndFilter).addProducer(producer,producerId);
|
||||
//// System.out.println(topicAndFilter2groups);
|
||||
// }
|
||||
/**
|
||||
*
|
||||
* @param topicAndFilter
|
||||
* @param msgInfo
|
||||
* @return offset,消息进入持久化队列后,返回下标做为offset(代表存储顺序,对应文件消息索引,同时用于表示消费进度)
|
||||
*/
|
||||
// public static String storeMsg(TopicAndFilter topicAndFilter,MessageInfo msgInfo){
|
||||
// ProducerGroup producerGroup = getGroup(topicAndFilter);
|
||||
// if(producerGroup==null){
|
||||
// logger.error("生产者集群(组)尚未注册");
|
||||
// producerGroup=addGroup(topicAndFilter);
|
||||
// }
|
||||
// return producerGroup.msgStore.storeMsg(msgInfo);
|
||||
// }
|
||||
/**
|
||||
* 组存处理
|
||||
* @param topicAndFilter
|
||||
* @param msgInfo
|
||||
* @return offset(返回为maxOffset),消息进入持久化队列后,返回下标做为offset(代表存储顺序,对应文件消息索引,同时用于表示消费进度)
|
||||
*/
|
||||
public static String storeMsg(TopicAndFilter topicAndFilter,MessageSend msgSend){
|
||||
ProducerGroup producerGroup = getGroup(topicAndFilter);
|
||||
if(producerGroup==null){
|
||||
logger.error("生产者集群(组)尚未注册");
|
||||
producerGroup=addGroup(topicAndFilter);
|
||||
}
|
||||
return producerGroup.msgStore.storeMsg(msgSend);
|
||||
}
|
||||
/**
|
||||
* 恢复
|
||||
*/
|
||||
public static void recover(HashMap<TopicAndFilter/*TopicAndFilter*/,Map<Integer /*queueIndex*/,Integer/*maxOffset*/>>producerGroupRecoverInfos){
|
||||
for(Map.Entry<TopicAndFilter,Map<Integer /*queueIndex*/,Integer/*maxOffset*/>>queueMaxOffsets :producerGroupRecoverInfos.entrySet()){
|
||||
addGroup(queueMaxOffsets.getKey(),queueMaxOffsets.getValue());
|
||||
System.out.println(addGroup(queueMaxOffsets.getKey(),queueMaxOffsets.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 集群组.区分集群(先勿删!!!)
|
||||
*/
|
||||
|
||||
// /**
|
||||
// * 订阅集群组,每种topicAndfilter可能有一个或多个订阅集群
|
||||
// */
|
||||
// public static Map<String/* topicAndfilter */, ConcurrentHashMap<String/*groupid*/,ProducerGroup/* group */>> topicAndFilter2groups = new ConcurrentHashMap<>();
|
||||
// /**
|
||||
// * 添加 topicAndFilter的订阅集群
|
||||
// * @param topicAndFilter
|
||||
// * @param groupId
|
||||
// */
|
||||
// public static ProducerGroup addGroup(TopicAndFilter topicAndFilter,String groupId){
|
||||
// Map<String,ProducerGroup> groups=topicAndFilter2groups.get(topicAndFilter.toString());//集群组
|
||||
// ProducerGroup group=new ProducerGroup(topicAndFilter.getTopic(),topicAndFilter.getFilter(),groupId);
|
||||
// if(groups==null){
|
||||
// groups=new ConcurrentHashMap<String,ProducerGroup>();
|
||||
// groups.put(groupId,group);
|
||||
// topicAndFilter2groups.put(topicAndFilter.toString(), (ConcurrentHashMap<String, ProducerGroup>) groups);
|
||||
// }else if(!groups.containsKey(groupId)){
|
||||
// groups.put(groupId, group);
|
||||
// }
|
||||
// return group;
|
||||
// }
|
||||
// public static ConcurrentHashMap<String,ProducerGroup> getGroups(TopicAndFilter topicAndFilter){
|
||||
// return topicAndFilter2groups.get(topicAndFilter.toString());
|
||||
// }
|
||||
// public static ProducerGroup getGroup(TopicAndFilter topicAndFilter,String groupId){
|
||||
// Map<String,ProducerGroup> groups=getGroups(topicAndFilter);
|
||||
// if(groups!=null){
|
||||
// return groups.get(groupId);
|
||||
// }else return null;
|
||||
// }
|
||||
// /**
|
||||
// * 注册生产者
|
||||
// * @param producer
|
||||
// * @param groupId
|
||||
// * @param topicAndFilter
|
||||
// */
|
||||
// public static void addProducer(Channel producer ,String producerId,String producerGroupId,TopicAndFilter topicAndFilter){
|
||||
// ProducerGroup group=getGroup(topicAndFilter,producerGroupId);
|
||||
// if(group!=null){
|
||||
// group.addProducer(producer,producerId);
|
||||
// }else addGroup(topicAndFilter,producerGroupId).addProducer(producer,producerId);
|
||||
// System.out.println(topicAndFilter2groups);
|
||||
// }
|
||||
// public static void storeMsg(TopicAndFilter topicAndFilter,MessageInfo msgInfo){
|
||||
// ProducerGroup producerGroup = getGroup(topicAndFilter,msgInfo.getProducerGroupId());
|
||||
// if(producerGroup!=null){
|
||||
// producerGroup.msgStoreHanler.storeCache.
|
||||
// }else{
|
||||
// logger.error("生产者集群尚未注册");
|
||||
// }
|
||||
// }
|
||||
// /**
|
||||
// * 以groupId区分俩个集群
|
||||
// */
|
||||
// @Override
|
||||
// public boolean equals(Object obj) {
|
||||
// if(obj instanceof ProducerGroup){
|
||||
// if(((ProducerGroup) obj).getGroupId().equals(this.groupId))return true;
|
||||
// else return false;
|
||||
// }else return false;
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.alibaba.middleware.race.mom.config;
|
||||
|
||||
public class Config {
|
||||
//每个group对应的队列条数
|
||||
public static int maxqueue=1;
|
||||
//内存每个队列只能装这么多条消息
|
||||
public static int maxMsgNum=30000/maxqueue;
|
||||
//每次从磁盘拉取多少条
|
||||
public static int maxLoadNum=2000;
|
||||
/**
|
||||
* 重投次数限制
|
||||
* 达到配置次数仍然失败,则不再重投
|
||||
*/
|
||||
public static int maxReSendTime=3;
|
||||
/**
|
||||
* 10s超时限制
|
||||
*/
|
||||
public static int timeoutLimit=10000;
|
||||
/**
|
||||
* 磁盘拉因子,当内存使用达到虚拟机内存(-Xms配置)的memoryFullFactor倍时,消息不能再存入内存,从磁盘拉取
|
||||
*/
|
||||
public static double memoryFullFactor=0.25;
|
||||
/**
|
||||
* 无消费者时重复尝试时间 ms,可能系统启动时,订阅信息稍有延迟
|
||||
*/
|
||||
public static int noConsumerWaitTimeLimit=500;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.alibaba.middleware.race.mom.serializer;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
|
||||
public class JdkObjectSerializer{
|
||||
|
||||
|
||||
public <T> T deserialize( final byte[] bytes , final Class<T> clazz ) {
|
||||
final ByteArrayInputStream in = new ByteArrayInputStream( bytes );
|
||||
try {
|
||||
final ObjectInputStream ois = new ObjectInputStream( in );
|
||||
Object obj = ois.readObject();
|
||||
return clazz.cast( obj );
|
||||
}
|
||||
catch ( final ClassNotFoundException e ) {
|
||||
throw new IllegalStateException( e.getMessage() , e );
|
||||
}
|
||||
catch ( final IOException e ) {
|
||||
throw new IllegalStateException( e.getMessage() , e );
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
in.close();
|
||||
}
|
||||
catch ( final IOException e ) {
|
||||
throw new IllegalStateException( e.getMessage() , e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public <T> byte[] serialize( final T source ) {
|
||||
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
final ObjectOutputStream oos = new ObjectOutputStream( out );
|
||||
oos.writeObject( source );
|
||||
oos.flush();
|
||||
}
|
||||
catch ( final IOException e ) {
|
||||
throw new IllegalStateException( e.getMessage() , e );
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
out.close();
|
||||
}
|
||||
catch ( final IOException e ) {
|
||||
throw new IllegalStateException( e.getMessage() , e );
|
||||
}
|
||||
}
|
||||
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.alibaba.middleware.race.mom.serializer;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import sun.reflect.ReflectionFactory;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
|
||||
public class KryoxSerializer extends Kryo {
|
||||
|
||||
private final ReflectionFactory REFLECTION_FACTORY = ReflectionFactory
|
||||
.getReflectionFactory();
|
||||
|
||||
private final ConcurrentHashMap<Class<?>, Constructor<?>> _constructors = new ConcurrentHashMap<Class<?>, Constructor<?>>();
|
||||
|
||||
@Override
|
||||
public <T> T newInstance(Class<T> type) {
|
||||
try {
|
||||
return super.newInstance(type);
|
||||
} catch (Exception e) {
|
||||
return (T) newInstanceFromReflectionFactory(type);
|
||||
}
|
||||
}
|
||||
|
||||
private Object newInstanceFrom(Constructor<?> constructor) {
|
||||
try {
|
||||
return constructor.newInstance();
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T newInstanceFromReflectionFactory(Class<T> type) {
|
||||
Constructor<?> constructor = _constructors.get(type);
|
||||
if (constructor == null) {
|
||||
constructor = newConstructorForSerialization(type);
|
||||
Constructor<?> saved = _constructors.putIfAbsent(type, constructor);
|
||||
if(saved!=null)
|
||||
constructor=saved;
|
||||
}
|
||||
return (T) newInstanceFrom(constructor);
|
||||
}
|
||||
|
||||
private <T> Constructor<?> newConstructorForSerialization(
|
||||
Class<T> type) {
|
||||
try {
|
||||
Constructor<?> constructor = REFLECTION_FACTORY
|
||||
.newConstructorForSerialization(type,
|
||||
Object.class.getDeclaredConstructor());
|
||||
constructor.setAccessible(true);
|
||||
return constructor;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.alibaba.middleware.race.mom.serializer;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.nustaq.serialization.FSTConfiguration;
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.serializers.JavaSerializer;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
|
||||
public class RpcDecoder extends ByteToMessageDecoder {
|
||||
|
||||
|
||||
private FSTConfiguration fst=FSTConfiguration.getDefaultConfiguration();
|
||||
@Override
|
||||
public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
try{
|
||||
if (in.readableBytes() < 4) {
|
||||
return;
|
||||
}
|
||||
in.markReaderIndex();
|
||||
int dataLength = in.readInt();
|
||||
if (dataLength < 0) {
|
||||
ctx.close();
|
||||
}
|
||||
if (in.readableBytes() < dataLength) {
|
||||
in.resetReaderIndex();
|
||||
return ;
|
||||
}
|
||||
byte[] data = new byte[dataLength];
|
||||
in.readBytes(data);
|
||||
out.add(fst.asObject(data));
|
||||
|
||||
}catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
package com.alibaba.middleware.race.mom.serializer;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.nustaq.serialization.FSTConfiguration;
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import com.esotericsoftware.kryo.serializers.JavaSerializer;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
public class RpcEncoder extends MessageToByteEncoder {
|
||||
|
||||
private FSTConfiguration fst=FSTConfiguration.getDefaultConfiguration();
|
||||
public RpcEncoder() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception {
|
||||
try{
|
||||
//对象
|
||||
if (!String.class.isInstance(in)) {
|
||||
byte[] data =fst.asByteArray(in);
|
||||
//写入内容
|
||||
out.writeInt(data.length);
|
||||
out.writeBytes(data);
|
||||
|
||||
}
|
||||
//字符串
|
||||
else
|
||||
{
|
||||
String data=in.toString();
|
||||
out.writeBytes(data.getBytes());
|
||||
}
|
||||
}catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 数组存储与恢复
|
||||
* @param <OffsetNum>
|
||||
*
|
||||
*/
|
||||
public interface ArrayStore {
|
||||
/**
|
||||
* 数组存储
|
||||
* 每次调用以更新的方式(不能追加)
|
||||
* @param offsets 需要存储的OffsetNum [] (一般用Integer[]),存储OffsetNum [] 中所有条目
|
||||
* @param topicAndFilter 这几个参数用于文件夹结构,和消息存储一样(去掉了consumerid项)
|
||||
* @param groupId
|
||||
* @param queueId
|
||||
*/
|
||||
public boolean store(ArrayList<OffsetNum> arrayList,String topicAndFilter, String queueId);
|
||||
/**
|
||||
* 数组恢复
|
||||
* @param topicAndFilter 这几个参数用于文件夹结构,和消息存储一样(去掉了consumerid项)
|
||||
* @param groupId
|
||||
* @param queueId
|
||||
* @return OffsetNum [],读出所有条目,并装到 OffsetNum [](一般用Integer[])返回
|
||||
*/
|
||||
public ArrayList<OffsetNum> recover(String topicAndFilter, String queueId);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
public class ArrayStoreImp implements ArrayStore {
|
||||
private static String filePath = StoreConfig.baseDir + StoreConfig.arrayDir;
|
||||
private static Logger logger = LoggerFactory.getLogger(ArrayStoreImp.class);
|
||||
private static ConcurrentHashMap<String, FileStoreManager> fileStoreManagerMap = new ConcurrentHashMap<String, FileStoreManager>();
|
||||
|
||||
@Override
|
||||
public boolean store(ArrayList<OffsetNum> arrayList, String topicAndFilter,
|
||||
String queueId) {
|
||||
// TODO Auto-generated method stub
|
||||
// 对应的文件管理器
|
||||
String arrayFilePath = filePath + "/" + topicAndFilter+"/"+queueId;
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap
|
||||
.get(arrayFilePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(arrayFilePath);
|
||||
// 为恢复
|
||||
fileStoreManagerMap.put(arrayFilePath, fileStoreManager);
|
||||
}
|
||||
// 将写位置重置为16
|
||||
fileStoreManager.resetFilePosition();
|
||||
|
||||
ArrayList<byte[]> dataList = new ArrayList<byte[]>(arrayList.size());
|
||||
int count = 1;
|
||||
for (OffsetNum tmp : arrayList) {
|
||||
logger.debug("保存订阅关系 i+== " + (count++) + " , " + tmp.i);
|
||||
dataList.add(JSON.toJSONBytes(tmp));
|
||||
}
|
||||
|
||||
return fileStoreManager.saveMessage(dataList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<OffsetNum> recover(String topicAndFilter, String queueId) {
|
||||
String arrayFilePath = filePath + "/" + topicAndFilter+"/"+queueId;
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap
|
||||
.get(arrayFilePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(arrayFilePath);
|
||||
// 为恢复
|
||||
fileStoreManagerMap.put(arrayFilePath, fileStoreManager);
|
||||
}
|
||||
return (ArrayList<OffsetNum>) fileStoreManager.getOffsetNum();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Comm {
|
||||
/**
|
||||
* 获取每个文件的名称地址
|
||||
*
|
||||
*/
|
||||
private static Logger logger = LoggerFactory
|
||||
.getLogger(Comm.class);
|
||||
|
||||
|
||||
public static ArrayList<Integer> getFileNum(String filePath) {
|
||||
ArrayList<Integer> fileNumArray=new ArrayList<Integer>();
|
||||
logger.error("filePath==="+filePath);
|
||||
File indexFile = new File(filePath);
|
||||
if (indexFile.isDirectory()) {
|
||||
String[] fileList = indexFile.list();
|
||||
int fileCount = fileList.length;
|
||||
if (fileCount == 0) {
|
||||
logger.warn(" 没有 index file 存在!");
|
||||
} else {
|
||||
// 遍历文件编号并保存
|
||||
for (int i = 0; i < fileCount; i++) {
|
||||
int t = Integer.parseInt(fileList[i]);
|
||||
fileNumArray.add(t);
|
||||
logger.error(" 查找index目录下的所有文件并记录为一个数组中 fileList[i]= "+fileList[i]);
|
||||
}
|
||||
}
|
||||
Collections.sort(fileNumArray);
|
||||
}
|
||||
return fileNumArray;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DataMappedFile {
|
||||
// 文件编号
|
||||
private long dataNum;
|
||||
// 文件大小
|
||||
private long fileSize;
|
||||
// 文件数目
|
||||
private long fileCount = 0;
|
||||
// data文件
|
||||
private MappedFileInfo dataMappedFile;
|
||||
// 文件的fileChannel
|
||||
private FileChannel fileChannel;
|
||||
// 文件的MappedByteBuffer
|
||||
private MappedByteBuffer mappedByteBuffer;
|
||||
// 文件夹的路径
|
||||
private String filePath;
|
||||
private static Logger logger = LoggerFactory
|
||||
.getLogger(DataMappedFile.class);
|
||||
// 保存已经打开的读文件列表
|
||||
private static ConcurrentHashMap<String, MappedFileInfo> readDataFileMap = new ConcurrentHashMap<String, MappedFileInfo>();
|
||||
public long dataWriterPositon;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param filePath
|
||||
*/
|
||||
public DataMappedFile(String filePath) {
|
||||
this.filePath = filePath;
|
||||
this.initFileInfo();
|
||||
/*
|
||||
* this.mappedByteBuffer = this.dataMappedFile.getMappedByteBuffer();
|
||||
* this.fileChannel = this.dataMappedFile.getFileChannel();
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 把数据从data文件的读出 带msg大小的标识
|
||||
*
|
||||
* @param Offset
|
||||
* 在data文件中的位置
|
||||
* @param size
|
||||
* 写入data文件中数据的大小
|
||||
* @param dataFileNum
|
||||
* 写入data文件的名称
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public List<long[]> checkLastMsg(long[] indexData) {
|
||||
// 保存已经开打的再读data文件
|
||||
List<long[]> rs = new ArrayList<long[]>();
|
||||
int offset, size, dataFileNum;
|
||||
if (indexData == null) {
|
||||
// 初始状态,index文件没有数据
|
||||
logger.error("indexData == null");
|
||||
dataFileNum = 0;
|
||||
offset = 16;
|
||||
size = 0;
|
||||
logger.error(" int offset, int size, int dataFileNum=" + offset
|
||||
+ " " + size + " " + dataFileNum);
|
||||
} else {
|
||||
offset = (int) indexData[0];
|
||||
size = (int) indexData[1];
|
||||
dataFileNum = (int) indexData[2];
|
||||
logger.error("int offset, int size, int dataFileNum=" + offset
|
||||
+ " " + size + " " + dataFileNum);
|
||||
|
||||
}
|
||||
MappedFileInfo readForDataFile = readDataFileMap.get(this.filePath
|
||||
+ "/" + dataFileNum);
|
||||
if (readForDataFile == null) {
|
||||
readForDataFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ dataFileNum);
|
||||
readDataFileMap.put(this.filePath + "/" + dataFileNum,
|
||||
readForDataFile);
|
||||
}
|
||||
|
||||
MappedByteBuffer readMappedByteFile = readForDataFile
|
||||
.getMappedByteBuffer();
|
||||
int writePos = (int) readForDataFile.getWritePosition().get();
|
||||
logger.error("int writePos = (int) readForDataFile.getWritePosition().get();="
|
||||
+ writePos);
|
||||
// 定位到读位置,然后获取byte数据
|
||||
int currentPos = offset + size + 4;
|
||||
// 当前data文件中
|
||||
if (currentPos < writePos) {
|
||||
do {
|
||||
readMappedByteFile.position(currentPos);
|
||||
long nextSize = readMappedByteFile.getInt();
|
||||
long[] t = new long[3];
|
||||
t[0] = currentPos;
|
||||
t[1] = nextSize;
|
||||
t[2] = dataFileNum;
|
||||
logger.error("t[0] = currentPos;t[1] = nextSize;t[2]=dataFileNum;"
|
||||
+ t.toString());
|
||||
rs.add(t);
|
||||
currentPos += nextSize + 4;
|
||||
} while (currentPos < writePos);
|
||||
// 判断是否还有下一个data文件,并进行恢复
|
||||
ArrayList<Integer> fileNameList = Comm.getFileNum(this.filePath);
|
||||
for (int i=0; i < fileNameList.size(); i++) {
|
||||
if(fileNameList.get(i)>dataFileNum){
|
||||
currentPos=16;
|
||||
dataFileNum=fileNameList.get(i);
|
||||
do {
|
||||
readMappedByteFile.position(currentPos);
|
||||
long nextSize = readMappedByteFile.getInt();
|
||||
long[] t = new long[3];
|
||||
t[0] = currentPos;
|
||||
t[1] = nextSize;
|
||||
t[2] = dataFileNum;
|
||||
logger.error("t[0] = currentPos;t[1] = nextSize;t[2]=dataFileNum;"
|
||||
+ t.toString());
|
||||
rs.add(t);
|
||||
currentPos += nextSize + 4;
|
||||
} while (currentPos < writePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取每个文件的名称地址
|
||||
*
|
||||
*/
|
||||
private ArrayList<Integer> dataNumArray;
|
||||
|
||||
/**
|
||||
* 创建或者获取当前使用的index文件 暂时以文件的数目来确定存储文件
|
||||
*/
|
||||
private void initFileInfo() {
|
||||
dataNumArray = Comm.getFileNum(this.filePath);
|
||||
if (this.dataNumArray == null || this.dataNumArray.size() == 0) {
|
||||
logger.warn("create index file");
|
||||
this.dataMappedFile = new MappedFileInfo(this.filePath + "/0");
|
||||
this.dataNum = 0;
|
||||
this.fileCount = 1;
|
||||
this.dataNumArray.add(0);
|
||||
} else {
|
||||
// 判断文件的最大编号
|
||||
int max = dataNumArray.get(dataNumArray.size() - 1);
|
||||
logger.warn("max index " + max);
|
||||
this.dataNum = max;
|
||||
this.dataMappedFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ this.dataNum);
|
||||
this.fileCount = dataNumArray.size();
|
||||
}
|
||||
|
||||
logger.error("initFileInfo 创建了一个datafile + this.datanum==="
|
||||
+ this.dataNum);
|
||||
readDataFileMap.put(this.filePath + "/" + this.dataNum,
|
||||
this.dataMappedFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存data数据到data文件
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public long addData(byte[] data) {
|
||||
// 保存当写入失败是上次写的位置
|
||||
long lastWritePosition = this.dataMappedFile.getWritePosition().get();
|
||||
boolean addDataFlag = this.dataMappedFile.saveData(data);
|
||||
if (!addDataFlag) {
|
||||
logger.error("增加数据时,当前data文件空间不够 ,将新建data文件");
|
||||
// 恢复data的写位置
|
||||
this.dataMappedFile.setWritePosition(new AtomicLong(
|
||||
lastWritePosition));
|
||||
this.dataMappedFile.setWritePositionToFile(lastWritePosition);
|
||||
this.dataMappedFile.flush();
|
||||
// 新建data文件
|
||||
this.dataMappedFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ this.fileCount);
|
||||
this.dataNum = this.fileCount;
|
||||
dataNumArray.add((int) this.dataNum);
|
||||
Collections.sort(dataNumArray);
|
||||
this.fileCount++;
|
||||
// 重新写入文件
|
||||
return this.addData(data);
|
||||
}
|
||||
return lastWritePosition;
|
||||
// this.dataMappedFile.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存data数据到data文件
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public long addData_new(byte[] data) {
|
||||
// 保存当写入失败是上次写的位置
|
||||
long lastWritePosition = this.dataMappedFile.getWritePosition().get();
|
||||
// 新存储data
|
||||
boolean addDataFlag = this.dataMappedFile.saveData(data);
|
||||
if (!addDataFlag) {
|
||||
logger.error("增加数据时,当前data文件空间不够 将新建data文件");
|
||||
// 恢复data的写位置
|
||||
this.dataMappedFile.setWritePosition(new AtomicLong(
|
||||
lastWritePosition));
|
||||
this.dataMappedFile.setWritePositionToFile(lastWritePosition);
|
||||
this.dataMappedFile.flush();
|
||||
// 新建data文件
|
||||
this.dataMappedFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ this.fileCount);
|
||||
this.dataNum = this.fileCount;
|
||||
dataNumArray.add((int) this.dataNum);
|
||||
Collections.sort(dataNumArray);
|
||||
this.fileCount++;
|
||||
// 重新写入文件
|
||||
return this.addData(data);
|
||||
}
|
||||
return lastWritePosition;
|
||||
// this.dataMappedFile.flush();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存dataList数据到data文件
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public long[] addDataByOnce(List<byte[]> dataList) {
|
||||
// 保存当写入失败是上次写的位置
|
||||
long lastWritePosition = this.dataMappedFile.getWritePosition().get();
|
||||
long[] result = this.dataMappedFile.saveDataByOnce(dataList);
|
||||
|
||||
if (result == null) {
|
||||
logger.error("增加数据时,当前data文件空间不够 将新建data文件");
|
||||
// 恢复data的写位置
|
||||
this.dataMappedFile.setWritePosition(new AtomicLong(
|
||||
lastWritePosition));
|
||||
this.dataMappedFile.setWritePositionToFile(lastWritePosition);
|
||||
this.dataMappedFile.flush();
|
||||
// 新建data文件
|
||||
this.dataMappedFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ this.fileCount);
|
||||
this.dataNum = this.fileCount;
|
||||
// 加入数据并排序
|
||||
dataNumArray.add((int) this.dataNum);
|
||||
Collections.sort(dataNumArray);
|
||||
this.fileCount++;
|
||||
// 重新写入文件
|
||||
logger.error("增加数据时,当前data文件空间不够 将新建data文件" + dataNum);
|
||||
return this.addDataByOnce(dataList);
|
||||
}
|
||||
return result;
|
||||
// this.dataMappedFile.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件buffer刷新落盘保持到磁盘
|
||||
*/
|
||||
public void flush() {
|
||||
this.dataMappedFile.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定文件中获取byte[]数据
|
||||
*
|
||||
* @param offset
|
||||
* 指定文件读取位置 size 读取大小 dataFileName 数据文件
|
||||
* @return byte[]数据
|
||||
*/
|
||||
|
||||
public byte[] getData(long[] dataPos) {
|
||||
return getData((int) dataPos[0], (int) dataPos[1], (int) dataPos[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一组数据
|
||||
*
|
||||
*/
|
||||
public List<byte[]> getDataByOnce(List<long[]> dataIndexList) {
|
||||
List<byte[]> result = new ArrayList<byte[]>();
|
||||
for (int i = 0; i < dataIndexList.size(); i++) {
|
||||
long[] dataPos = dataIndexList.get(i);
|
||||
result.add(getData((int) dataPos[0], (int) dataPos[1],
|
||||
(int) dataPos[2]));
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 把数据从data文件的读出
|
||||
*
|
||||
* @param Offset
|
||||
* 在data文件中的位置
|
||||
* @param size
|
||||
* 写入data文件中数据的大小
|
||||
* @param dataFileNum
|
||||
* 写入data文件的名称
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public byte[] getData(int offset, int size, int dataFileNum) {
|
||||
// 保存已经开打的再读data文件
|
||||
MappedFileInfo readForDataFile = readDataFileMap.get(this.filePath
|
||||
+ "/" + dataFileNum);
|
||||
if (readForDataFile == null) {
|
||||
readForDataFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ dataFileNum);
|
||||
readDataFileMap.put(this.filePath + "/" + dataFileNum,
|
||||
readForDataFile);
|
||||
}
|
||||
|
||||
MappedByteBuffer readMappedByteFile = readForDataFile
|
||||
.getMappedByteBuffer();
|
||||
// 定位到读位置,然后获取byte数据
|
||||
readMappedByteFile.position(offset + 4);
|
||||
byte[] tempData = new byte[size];
|
||||
readMappedByteFile.get(tempData);
|
||||
return tempData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把数据从data文件的读出 带msg大小的标识
|
||||
*
|
||||
* @param Offset
|
||||
* 在data文件中的位置
|
||||
* @param size
|
||||
* 写入data文件中数据的大小
|
||||
* @param dataFileNum
|
||||
* 写入data文件的名称
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public byte[] getData_new(int offset, int size, int dataFileNum) {
|
||||
// 保存已经开打的再读data文件
|
||||
System.out.println("int offset, int size, int dataFileNum=" + offset
|
||||
+ " " + size + " " + dataFileNum);
|
||||
MappedFileInfo readForDataFile = readDataFileMap.get(this.filePath
|
||||
+ "/" + dataFileNum);
|
||||
if (readForDataFile == null) {
|
||||
readForDataFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ dataFileNum);
|
||||
readDataFileMap.put(this.filePath + "/" + dataFileNum,
|
||||
readForDataFile);
|
||||
}
|
||||
|
||||
MappedByteBuffer readMappedByteFile = readForDataFile
|
||||
.getMappedByteBuffer();
|
||||
// 定位到读位置,然后获取byte数据
|
||||
readMappedByteFile.position(offset + 4);
|
||||
byte[] tempData = new byte[size];
|
||||
readMappedByteFile.get(tempData);
|
||||
return tempData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取从读位置到写位置的所有的数据
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return this.dataMappedFile.getData();
|
||||
|
||||
}
|
||||
|
||||
public long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
public long getFileCount() {
|
||||
return fileCount;
|
||||
}
|
||||
|
||||
public void setFileCount(long fileCount) {
|
||||
this.fileCount = fileCount;
|
||||
}
|
||||
|
||||
public MappedFileInfo getDataMappedFile() {
|
||||
return dataMappedFile;
|
||||
}
|
||||
|
||||
public void setDataMappedFile(MappedFileInfo dataMappedFile) {
|
||||
this.dataMappedFile = dataMappedFile;
|
||||
}
|
||||
|
||||
public FileChannel getFileChannel() {
|
||||
return fileChannel;
|
||||
}
|
||||
|
||||
public void setFileChannel(FileChannel fileChannel) {
|
||||
this.fileChannel = fileChannel;
|
||||
}
|
||||
|
||||
public MappedByteBuffer getMappedByteBuffer() {
|
||||
return mappedByteBuffer;
|
||||
}
|
||||
|
||||
public void setMappedByteBuffer(MappedByteBuffer mappedByteBuffer) {
|
||||
this.mappedByteBuffer = mappedByteBuffer;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public long getDataNum() {
|
||||
return dataNum;
|
||||
}
|
||||
|
||||
public void setDataNum(long dataNum) {
|
||||
this.dataNum = dataNum;
|
||||
}
|
||||
|
||||
public void destory() {
|
||||
// TODO Auto-generated method stub
|
||||
// this.mappedByteBuffer
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新设置写位置
|
||||
*/
|
||||
public void resetFilePosition() {
|
||||
this.dataMappedFile.setWritePositionToFile(16);
|
||||
this.dataMappedFile.setWritePosition(new AtomicLong(16));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.middleware.race.mom.broker.function.Offset;
|
||||
|
||||
public class FileStoreManager {
|
||||
private static Logger logger = LoggerFactory
|
||||
.getLogger(FileStoreManager.class);
|
||||
// 索引文件
|
||||
private IndexMappedFile indexMappedFile;
|
||||
// 数据文件
|
||||
private DataMappedFile dataMappedFile;
|
||||
|
||||
private String filePath;
|
||||
// 最后一次刷数据的时间
|
||||
private long lastFlushTime = System.currentTimeMillis();
|
||||
private long lastIndexFlushTime = System.currentTimeMillis();
|
||||
// 读写锁保证线程安全
|
||||
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
// 统计已经写入但为落盘到磁盘的消息数目
|
||||
private static AtomicLong countMsg = new AtomicLong(0);
|
||||
// 落盘时,同步等待index和data文件的缓存落盘到磁盘
|
||||
private CountDownLatch countDownLatch;
|
||||
// index和data文件多线程并行落盘
|
||||
private ExecutorService flushPool = Executors.newFixedThreadPool(1);
|
||||
private ExecutorService flushIndexPool = Executors.newFixedThreadPool(1);
|
||||
|
||||
public FileStoreManager(String filePath) {
|
||||
this.filePath = filePath;
|
||||
this.indexMappedFile = new IndexMappedFile(filePath + "/index");
|
||||
this.dataMappedFile = new DataMappedFile(filePath + "/data");
|
||||
// logger.debug("初始化filestoremanager成功!" + filePath);
|
||||
}
|
||||
|
||||
public FileStoreManager(String filePath, String test) {
|
||||
this.filePath = filePath;
|
||||
this.indexMappedFile = new IndexMappedFile(filePath + "/index");
|
||||
this.dataMappedFile = new DataMappedFile(filePath + "/data");
|
||||
// logger.debug("初始化filestoremanager成功!" + filePath);
|
||||
this.recover();
|
||||
}
|
||||
|
||||
void recover() {
|
||||
long[] lastWrittePosition = this.indexMappedFile.getLastWriteData();
|
||||
// logger.error("long[] lastWrittePosition=="+lastWrittePosition.toString());
|
||||
List<long[]> rs = this.dataMappedFile.checkLastMsg(lastWrittePosition);
|
||||
if (rs == null || rs.size() == 0) {
|
||||
return;
|
||||
} else {
|
||||
for (long[] temp : rs) {
|
||||
logger.error("新恢复增加indexdata " + temp[0] + " / " + temp[1]
|
||||
+ " / " + temp[2]);
|
||||
this.indexMappedFile.addData(temp[0], (int) temp[1],
|
||||
(int) temp[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存一个字节到index和data文件中
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
private void saveMessage(byte[] msg) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// 在data文件中增加记录
|
||||
int dataWritePosition = (int) this.dataMappedFile.addData(msg);
|
||||
int size = msg.length;
|
||||
int fileNum = (int) this.dataMappedFile.getDataNum();
|
||||
// logger.debug("dataWritePosition,size,fileNum= " +
|
||||
// dataWritePosition
|
||||
// + "," + size + "," + fileNum);
|
||||
|
||||
// 写入index文件中
|
||||
boolean flag = this.indexMappedFile.addData(dataWritePosition,
|
||||
size, fileNum);
|
||||
if (!flag) {
|
||||
logger.warn("没有存成功,未知原因,在index文件中");
|
||||
}
|
||||
logger.debug("成功将字节写入文件");
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据 提供给其他调用的接口,必须是以List包装的字节数据,传入的参数最好用ArrayList
|
||||
*/
|
||||
static long savemessage = 0;
|
||||
|
||||
public boolean saveMessage(List<byte[]> msgList) {
|
||||
long t1 = System.currentTimeMillis();
|
||||
for (int i = 0; i < msgList.size(); i++) {
|
||||
byte[] temp = msgList.get(i);
|
||||
countMsg.addAndGet(1);
|
||||
saveMessage(temp);
|
||||
}
|
||||
|
||||
flush();
|
||||
long t2 = System.currentTimeMillis();
|
||||
// savemessage += (t2 - t1);
|
||||
logger.error("flush time t2-t1= " + (t2 - t1));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据 提供给其他调用的接口,必须是以List包装的字节数据,传入的参数最好用ArrayList
|
||||
*/
|
||||
|
||||
static long savemessagebyonce = 0;
|
||||
|
||||
public boolean saveMessageByOnce(List<byte[]> msgList) {
|
||||
long t1 = System.currentTimeMillis();
|
||||
|
||||
// 获取data文件存储结果
|
||||
logger.debug("开始组进入写data文件 msgList.size()==" + msgList.size());
|
||||
long[] dataResult = this.dataMappedFile.addDataByOnce(msgList);
|
||||
|
||||
long dataFileNum = this.dataMappedFile.getDataNum();
|
||||
logger.debug("开始进入写index文件 dataResult.length= " + dataResult.length);
|
||||
|
||||
boolean indexFlag = this.indexMappedFile.addDataByOnce(dataResult,
|
||||
(int) dataFileNum);
|
||||
|
||||
flush();
|
||||
long t2 = System.currentTimeMillis();
|
||||
logger.error("flush time t2-t1= " + (t2 - t1));
|
||||
// savemessagebyonce += (t2 - t1);
|
||||
return indexFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第0条到写的位置条数据, 恢复订阅数据用的函数
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<Offset> getSubscribe() {
|
||||
ByteBuffer indexByte = ByteBuffer.wrap(this.indexMappedFile.getData());
|
||||
ByteBuffer dataByte = ByteBuffer.wrap(this.dataMappedFile.getData());
|
||||
int count = indexByte.capacity() / 16;
|
||||
List<Offset> subscribeInfoList = new ArrayList<Offset>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
indexByte.position(i * 16);
|
||||
long dataStart = indexByte.getLong();
|
||||
int size = indexByte.getInt();
|
||||
dataByte.position((int) dataStart - 16 + 4);
|
||||
byte[] temp = new byte[size];
|
||||
dataByte.get(temp);
|
||||
subscribeInfoList
|
||||
.add((Offset) JSON.parseObject(temp, Offset.class));
|
||||
}
|
||||
return subscribeInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第startCount条到endCount条数据 采用一组数据一组数据的读
|
||||
*
|
||||
* @param startCount
|
||||
* @param endCount
|
||||
* @return
|
||||
*/
|
||||
public ArrayList<byte[]> getMessageByOnce(int offset, int maxOffset) {
|
||||
logger.warn("开始组获取数据了");
|
||||
List<long[]> dataIndexList = this.indexMappedFile.getIndexDataByOnce(
|
||||
offset, maxOffset);
|
||||
return (ArrayList<byte[]>) this.dataMappedFile
|
||||
.getDataByOnce(dataIndexList);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第startCount条到endCount条数据
|
||||
*
|
||||
* @param startCount
|
||||
* @param endCount
|
||||
* @return
|
||||
*/
|
||||
public List<byte[]> getMessage(int startCount, int endCount) {
|
||||
lock.readLock().lock();
|
||||
List<byte[]> msgList = null;
|
||||
try {
|
||||
msgList = new ArrayList<byte[]>(endCount - startCount + 1);
|
||||
// logger.debug(" return msgList.size() endCount - startCount + 1 = "
|
||||
// + (endCount - startCount + 1));
|
||||
for (int i = startCount; i <= endCount; i++) {
|
||||
// logger.debug("i= " + i);
|
||||
long[] returnIndexData = this.indexMappedFile.getData(i);
|
||||
// logger.debug(" returnIndexData= " + returnIndexData[0] +
|
||||
// " , "
|
||||
// + returnIndexData[1] + " , " + returnIndexData[2] + " , ");
|
||||
byte[] temp = this.dataMappedFile.getData(returnIndexData);
|
||||
// logger.debug("temp.size()= " + temp.length);
|
||||
msgList.add(temp);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
return msgList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多线程并行落盘
|
||||
*/
|
||||
public void flush() {
|
||||
lock.writeLock().lock();
|
||||
countDownLatch = new CountDownLatch(2);
|
||||
|
||||
flushIndexPool.execute(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
long t1 = System.currentTimeMillis();
|
||||
|
||||
// 索引文件异步
|
||||
try {
|
||||
FileStoreManager.this.indexMappedFile.flush();
|
||||
countDownLatch.countDown();
|
||||
} finally {
|
||||
// countDownLatch.countDown();
|
||||
}
|
||||
long t2 = System.currentTimeMillis();
|
||||
// System.out.println(" force index t2-t1==" + (t2 - t1));
|
||||
}
|
||||
});
|
||||
|
||||
flushPool.execute(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
long t1 = System.currentTimeMillis();
|
||||
try {
|
||||
FileStoreManager.this.dataMappedFile.flush();
|
||||
} finally {
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
long t2 = System.currentTimeMillis();
|
||||
// System.out.println(" force data t2-t1==" + (t2 - t1));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
countDownLatch.await();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
// 测试用的
|
||||
// flushPool.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* 多线程并行落盘
|
||||
*/
|
||||
/*
|
||||
* public void flush() { lock.writeLock().lock(); countDownLatch = new
|
||||
* CountDownLatch(2);
|
||||
*
|
||||
* flushIndexPool.execute(new Thread() {
|
||||
*
|
||||
* @Override public void run() { long t1 = System.currentTimeMillis();
|
||||
*
|
||||
* // 索引文件异步 try { FileStoreManager.this.indexMappedFile.flush();
|
||||
* countDownLatch.countDown(); } finally { // countDownLatch.countDown(); }
|
||||
* long t2 = System.currentTimeMillis(); //
|
||||
* System.out.println(" force index t2-t1==" + (t2 - t1)); } });
|
||||
*
|
||||
* flushPool.execute(new Thread() {
|
||||
*
|
||||
* @Override public void run() { long t1 = System.currentTimeMillis(); try {
|
||||
* FileStoreManager.this.dataMappedFile.flush(); } finally {
|
||||
* countDownLatch.countDown(); } long t2 = System.currentTimeMillis(); //
|
||||
* System.out.println(" force data t2-t1==" + (t2 - t1)); } });
|
||||
*
|
||||
* try { countDownLatch.await(); } catch (Exception e) {
|
||||
* logger.error(e.getMessage()); } finally { lock.writeLock().unlock(); }
|
||||
*
|
||||
* // 测试用的 // flushPool.shutdown(); }
|
||||
*/
|
||||
|
||||
/**
|
||||
* 释放资源
|
||||
*/
|
||||
public void destory() {
|
||||
this.indexMappedFile.destory();
|
||||
this.dataMappedFile.destory();
|
||||
}
|
||||
|
||||
public void resetFilePosition() {
|
||||
this.indexMappedFile.resetFilePosition();
|
||||
this.dataMappedFile.resetFilePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第0条到写的位置条数据, 恢复OffsetNum数据用的函数
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<OffsetNum> getOffsetNum() {
|
||||
ByteBuffer indexByte = ByteBuffer.wrap(this.indexMappedFile.getData());
|
||||
ByteBuffer dataByte = ByteBuffer.wrap(this.dataMappedFile.getData());
|
||||
int count = indexByte.capacity() / 16;
|
||||
List<OffsetNum> offsetNumList = new ArrayList<OffsetNum>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
indexByte.position(i * 16);
|
||||
long dataStart = indexByte.getLong();
|
||||
int size = indexByte.getInt();
|
||||
dataByte.position((int) dataStart - 16 + 4);
|
||||
byte[] temp = new byte[size];
|
||||
dataByte.get(temp);
|
||||
offsetNumList.add((OffsetNum) JSON.parseObject(temp,
|
||||
OffsetNum.class));
|
||||
}
|
||||
return offsetNumList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class IndexMappedFile {
|
||||
// 文件编号
|
||||
private long indexNum;
|
||||
// 文件大小
|
||||
private long fileSize;
|
||||
// 文件数目
|
||||
private long fileCount = 0;
|
||||
// index文件
|
||||
private MappedFileInfo indexMappedFile;
|
||||
|
||||
// 文件的fileChannel
|
||||
private FileChannel fileChannel;
|
||||
// 文件的MappedByteBuffer
|
||||
private MappedByteBuffer mappedByteBuffer;
|
||||
// 文件夹的路径
|
||||
private String filePath;
|
||||
// 保存已经打开的读文件列表
|
||||
ConcurrentHashMap<String, MappedFileInfo> readIndexFileMap = new ConcurrentHashMap<String, MappedFileInfo>();
|
||||
|
||||
private static Logger logger = LoggerFactory
|
||||
.getLogger(IndexMappedFile.class);
|
||||
|
||||
public long[] getLastWriteData() {
|
||||
long[] result=null;
|
||||
long pos = this.indexMappedFile.getWritePosition().get();
|
||||
if (pos == 16) {
|
||||
//初始状态
|
||||
return result;
|
||||
}
|
||||
this.indexMappedFile.getMappedByteBuffer().position((int) (this.indexMappedFile
|
||||
.getWritePosition().get() - 16));
|
||||
long offset = this.indexMappedFile.getMappedByteBuffer().getLong();
|
||||
|
||||
logger.error("从index 读出data 的位置offset====" + offset);
|
||||
int size = this.indexMappedFile.getMappedByteBuffer().getInt();
|
||||
int dataFileNum = this.indexMappedFile.getMappedByteBuffer().getInt();
|
||||
result = new long[] { offset, size, dataFileNum };
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param filePath
|
||||
*/
|
||||
public IndexMappedFile(String filePath) {
|
||||
this.filePath = filePath;
|
||||
this.initFileInfo();
|
||||
/*
|
||||
* this.mappedByteBuffer = this.indexMappedFile.getMappedByteBuffer();
|
||||
* this.fileChannel = this.indexMappedFile.getFileChannel();
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取每个文件的名称地址
|
||||
*
|
||||
*/
|
||||
private ArrayList<Integer> indexNumArray;
|
||||
|
||||
|
||||
/**
|
||||
* 创建或者获取当前使用的index文件 暂时以文件的数目来确定存储文件
|
||||
*/
|
||||
private void initFileInfo() {
|
||||
indexNumArray = Comm.getFileNum(this.filePath);
|
||||
if (this.indexNumArray == null || this.indexNumArray.size() == 0) {
|
||||
logger.warn("create index file");
|
||||
this.indexMappedFile = new MappedFileInfo(this.filePath + "/0");
|
||||
this.indexNum = 0;
|
||||
this.fileCount = 1;
|
||||
this.indexNumArray.add(0);
|
||||
} else {
|
||||
// 判断文件的最大编号
|
||||
int max = indexNumArray.get(indexNumArray.size() - 1);
|
||||
logger.error("max index " + max);
|
||||
this.indexNum = max;
|
||||
this.indexMappedFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ this.indexNum);
|
||||
this.fileCount = indexNumArray.size();
|
||||
}
|
||||
|
||||
logger.error("inti file createFile 创建了一个indexfile + this.indexnum==="
|
||||
+ this.indexNum);
|
||||
readIndexFileMap.put(this.filePath + "/" + this.indexNum,
|
||||
this.indexMappedFile);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 把数据在data文件的写入信息保存到index文件中
|
||||
*
|
||||
* @param dataOffset
|
||||
* 在data文件中的位置
|
||||
* @param size
|
||||
* 写入data文件中数据的大小
|
||||
* @param dataFileNum
|
||||
* 写入data文件的名称
|
||||
* @return
|
||||
*/
|
||||
public boolean addData(long dataOffset, int size, int dataFileNum) {
|
||||
// 保存当写入失败是上次写的位置
|
||||
long lastWritePosition = this.indexMappedFile.getWritePosition().get();
|
||||
// 依次写入data数据信息
|
||||
boolean[] addDataFlag = new boolean[3];
|
||||
addDataFlag[0] = this.indexMappedFile.saveData(dataOffset);
|
||||
addDataFlag[1] = this.indexMappedFile.saveData(size);
|
||||
addDataFlag[2] = this.indexMappedFile.saveData(dataFileNum);
|
||||
if (!(addDataFlag[0] && addDataFlag[1] && addDataFlag[2])) {
|
||||
logger.error("增加数据时,当前index文件空间不够 将新建index文件");
|
||||
// 恢复index的写位置
|
||||
this.indexMappedFile.setWritePosition(new AtomicLong(
|
||||
lastWritePosition));
|
||||
this.indexMappedFile.setWritePositionToFile(lastWritePosition);
|
||||
this.indexMappedFile.flush();
|
||||
// 新建index文件
|
||||
// 新建index文件,文件名为开始记录
|
||||
logger.error("index 增加数据时,当前index文件空间不够 将新建index文件 old this.indexNum="
|
||||
+ this.indexNum
|
||||
+ " /lastWritePosition=="
|
||||
+ lastWritePosition);
|
||||
this.indexNum += ((lastWritePosition / 16) - 1);
|
||||
logger.error("index 增加数据时,当前index文件空间不够 将新建index文件 new this.indexNum="
|
||||
+ this.indexNum);
|
||||
// 加入数组中,默认是加到数据末尾
|
||||
indexNumArray.add((int) this.indexNum);
|
||||
this.indexMappedFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ this.indexNum);
|
||||
logger.error("index 增加数据时,当前index文件空间不够 将新建index文件 his.indexNum"
|
||||
+ this.indexNum);
|
||||
this.fileCount++;
|
||||
// 重新写入文件
|
||||
return this.addData(dataOffset, size, dataFileNum);
|
||||
}
|
||||
logger.debug("增加index数据时 成功");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把数据在data文件的写入信息保存到index文件中
|
||||
*
|
||||
* @param dataOffset
|
||||
* 在data文件中的位置
|
||||
* @param size
|
||||
* 写入data文件中数据的大小
|
||||
* @param dataFileNum
|
||||
* 写入data文件的名称
|
||||
* @return
|
||||
*/
|
||||
public boolean addDataByOnce(long[] dataArray, int dataFileNum) {
|
||||
|
||||
// 保存当写入失败是上次写的位置
|
||||
long lastWritePosition = this.indexMappedFile.getWritePosition().get();
|
||||
// 依次写入data数据信息
|
||||
boolean addDataFlag = this.indexMappedFile.saveDataByOnce(dataArray,
|
||||
dataFileNum);
|
||||
if (!addDataFlag) {
|
||||
logger.error("增加数据时,当前index文件空间不够 将新建index文件");
|
||||
// 恢复index的写位置
|
||||
this.indexMappedFile.setWritePosition(new AtomicLong(
|
||||
lastWritePosition));
|
||||
this.indexMappedFile.setWritePositionToFile(lastWritePosition);
|
||||
this.indexMappedFile.flush();
|
||||
// 新建index文件,文件名为开始记录
|
||||
this.indexNum += ((lastWritePosition / 16) - 1);
|
||||
// 加入数组中,默认是加到数据末尾
|
||||
indexNumArray.add((int) this.indexNum);
|
||||
|
||||
logger.error("create index file name indexnum== " + this.indexNum);
|
||||
logger.error("index 增加数据时,当前index文件空间不够 将新建index文件 indexNumArray.SIZE()"
|
||||
+ indexNumArray.size());
|
||||
this.indexMappedFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ this.indexNum);
|
||||
this.fileCount++;
|
||||
// 重新写入文件
|
||||
return this.addDataByOnce(dataArray, dataFileNum);
|
||||
}
|
||||
logger.debug("增加index数据时 成功");
|
||||
return true;
|
||||
// this.indexMappedFile.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件buffer落盘保持到磁盘
|
||||
*/
|
||||
public long flush() {
|
||||
return this.indexMappedFile.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取信息,防护给data文件读取数据, 获取指定位置的一个单位(8+4+4)
|
||||
*
|
||||
*/
|
||||
public long[] getData(int countStart) {
|
||||
// 获取需要打开index文件名
|
||||
if (indexNumArray == null || indexNumArray.size() == 0) {
|
||||
logger.error(" 没有 index file 存在!countStart= " + countStart);
|
||||
return null;
|
||||
} else {
|
||||
// 判断文件的最大编号
|
||||
int fileCount = indexNumArray.size();
|
||||
int readIndexFileNum = 0;
|
||||
for (int i = (fileCount - 1); i >= 0; i--) {
|
||||
// logger.debug("get data create index filedir count tempNum= "
|
||||
// + readIndexFileNum);
|
||||
int t = indexNumArray.get(i);
|
||||
if (t > countStart) {
|
||||
continue;
|
||||
} else {
|
||||
readIndexFileNum = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 转化到当前文件的文件内地址
|
||||
|
||||
logger.debug("转化到当前文件的文件内地址 readIndexFileNum___" + readIndexFileNum);
|
||||
MappedFileInfo readForIndexFile = readIndexFileMap
|
||||
.get(this.filePath + "/" + readIndexFileNum);
|
||||
|
||||
if (readForIndexFile == null) {
|
||||
readForIndexFile = new MappedFileInfo(this.filePath + "/"
|
||||
+ readIndexFileNum);
|
||||
readIndexFileMap.put(this.filePath + "/" + readIndexFileNum,
|
||||
readForIndexFile);
|
||||
logger.error("创建读index文件 :" + this.filePath + "/"
|
||||
+ readIndexFileNum);
|
||||
}
|
||||
// logger.debug("读index文件 :" + this.filePath + "/" + fileNum);
|
||||
// 将countStart转化为指定index文件的相对地址,注意要偏移16位
|
||||
int tempPosition = (countStart - readIndexFileNum) * 16 + 16;
|
||||
MappedByteBuffer readMappedByteFile = readForIndexFile
|
||||
.getMappedByteBuffer();
|
||||
readMappedByteFile.position((int) tempPosition);
|
||||
logger.error("在读出index 数据的 位置 tempPosition====" + tempPosition);
|
||||
// 获取与data文件记录相关信息
|
||||
long offset = readMappedByteFile.getLong();
|
||||
logger.error("从index 读出data 的位置offset====" + offset);
|
||||
int size = readMappedByteFile.getInt();
|
||||
int dataFileNum = readMappedByteFile.getInt();
|
||||
long[] result = new long[] { offset, size, dataFileNum };
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取信息,防护给data文件读取数据, 获取指定位置的一个单位(8+4+4)
|
||||
*
|
||||
*/
|
||||
public List<long[]> getIndexDataByOnce(int countStart, int countEnd) {
|
||||
List<long[]> result = new ArrayList<long[]>();
|
||||
for (int i = countStart; i <= countEnd; i++) {
|
||||
logger.error("int i = countStart; i <= countEnd" + i
|
||||
+ " int countStart, int countEnd== " + countStart + " , "
|
||||
+ countEnd);
|
||||
result.add(this.getData(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取从读位置到写位置的所有的数据
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return this.indexMappedFile.getData();
|
||||
|
||||
}
|
||||
|
||||
public void destory() {
|
||||
// this.indexMappedFile
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新设置写位置
|
||||
*/
|
||||
public void resetFilePosition() {
|
||||
this.indexMappedFile.setWritePositionToFile(16);
|
||||
this.indexMappedFile.setWritePosition(new AtomicLong(16));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileChannel.MapMode;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MappedFileInfo {
|
||||
// 文件大小
|
||||
private static long MAX_FILE_SIZE = 1024 * 1024 * 1024;
|
||||
// 文件读写位置
|
||||
private AtomicLong writePosition = new AtomicLong(0);
|
||||
private AtomicLong readPosition = new AtomicLong(0);
|
||||
// 上一次刷的文件位置
|
||||
private AtomicLong lastFlushFilePosition = new AtomicLong(0);
|
||||
// 最后一次刷数据的时间
|
||||
private long lastFlushTime = System.currentTimeMillis();
|
||||
|
||||
// 文件路径和名称全称
|
||||
private String filePathAndName;
|
||||
// 文件的读取方式
|
||||
private RandomAccessFile raf;
|
||||
private MappedByteBuffer mappedByteBuffer;
|
||||
// 映射的FileChannel对象
|
||||
private FileChannel fileChannel;
|
||||
private static Logger logger = LoggerFactory
|
||||
.getLogger(MappedFileInfo.class);
|
||||
|
||||
/**
|
||||
* 构建一个mappedFile文件
|
||||
*
|
||||
* @param filePathAndName
|
||||
* 传入文件路径和名称
|
||||
*/
|
||||
public MappedFileInfo(String filePathAndName) {
|
||||
this.filePathAndName = filePathAndName;
|
||||
createRandomAccessFile();
|
||||
getFileMappedByteBuffer(0, MAX_FILE_SIZE);
|
||||
setInitPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个mappedFile文件
|
||||
*
|
||||
* @param filePathAndName
|
||||
* 文件路径和名称
|
||||
* @param fileSize
|
||||
* 文件大小
|
||||
*/
|
||||
public MappedFileInfo(String filePathAndName, long fileSize) {
|
||||
this.filePathAndName = filePathAndName;
|
||||
this.createRandomAccessFile();
|
||||
this.getFileMappedByteBuffer(0, fileSize);
|
||||
this.setInitPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化文件的读写位置变量 读位置(8)+写位置(8) 根据文件的前16个字节获取当前读和写的位置
|
||||
*
|
||||
*/
|
||||
private void setInitPosition() {
|
||||
// 文件前16字节存放读地址和写地址
|
||||
this.mappedByteBuffer.position(0);
|
||||
long position;
|
||||
// 如果position=0则为起始地址16
|
||||
if ((position = this.mappedByteBuffer.getLong()) == 0) {
|
||||
this.readPosition.set(16);
|
||||
} else {
|
||||
this.readPosition.set(position);
|
||||
}
|
||||
// 更新读位置
|
||||
this.mappedByteBuffer.position(0);
|
||||
this.mappedByteBuffer.putLong(this.readPosition.get());
|
||||
// logger.debug("this.readPosition= "+this.readPosition);
|
||||
// 如果position=0则为起始地址16
|
||||
if ((position = this.mappedByteBuffer.getLong()) == 0) {
|
||||
this.writePosition.set(16);
|
||||
this.lastFlushFilePosition.set(16);
|
||||
} else {
|
||||
this.writePosition.set(position);
|
||||
this.lastFlushFilePosition.set(16);
|
||||
}
|
||||
// 更新写位置
|
||||
this.mappedByteBuffer.position(8);
|
||||
this.mappedByteBuffer.putLong(this.writePosition.get());
|
||||
// logger.debug("this.writePosition= "+this.writePosition.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 建立和确保文件存在
|
||||
*/
|
||||
private synchronized void createRandomAccessFile() {
|
||||
File f = new File(filePathAndName);
|
||||
try {
|
||||
if (!f.exists()) {
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
f.createNewFile();
|
||||
}
|
||||
this.raf = new RandomAccessFile(f, "rw");
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开文件映射
|
||||
*
|
||||
* @param startPos
|
||||
* @param fileSize
|
||||
*/
|
||||
private void getFileMappedByteBuffer(long startPos, long fileSize) {
|
||||
try {
|
||||
this.fileChannel = this.raf.getChannel();
|
||||
this.mappedByteBuffer = this.fileChannel.map(MapMode.READ_WRITE,
|
||||
startPos, fileSize);
|
||||
} catch (Exception e) {
|
||||
logger.error("内存映射沒有建立");
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存一组index数据 ,index文件调用
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public boolean saveDataByOnce(long[] dataArray, int fileNum) {
|
||||
long lastposition = this.writePosition.get();
|
||||
long currentPosition = lastposition;
|
||||
if ((currentPosition + (dataArray.length / 3) * 16) > this.MAX_FILE_SIZE) {
|
||||
// 恢复到存数据的开始
|
||||
logger.warn("文件空间不够了,需要新建文件");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < dataArray.length; i++) {
|
||||
// 一定要定位写的位置
|
||||
mappedByteBuffer.position((int) currentPosition);
|
||||
mappedByteBuffer.putLong(dataArray[i]);
|
||||
i++;
|
||||
mappedByteBuffer.putInt((int) dataArray[i]);
|
||||
i++;
|
||||
mappedByteBuffer.putInt(fileNum);
|
||||
currentPosition += 16;
|
||||
}
|
||||
// 完成后,设置写的写位置
|
||||
this.writePosition.set(currentPosition);
|
||||
this.lastFlushFilePosition.set(currentPosition);
|
||||
mappedByteBuffer.position(8);
|
||||
mappedByteBuffer.putLong(currentPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存一组data数据
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public long[] saveDataByOnce(List<byte[]> dataList) {
|
||||
long lastposition = this.writePosition.get();
|
||||
long currentPosition = lastposition;
|
||||
int count = dataList.size();
|
||||
// 保存返回结果
|
||||
long[] result = new long[count * 3];
|
||||
for (int i = 0; i < count; i++) {
|
||||
byte[] data = dataList.get(i);
|
||||
if ((currentPosition + data.length + 4) > this.MAX_FILE_SIZE) {
|
||||
// 恢复到存数据的开始
|
||||
logger.warn("保存消息的数据文件空间不够了!");
|
||||
currentPosition = lastposition;
|
||||
result = null;
|
||||
break;
|
||||
} else {
|
||||
// 表示有空余空间
|
||||
if (i == 0) {
|
||||
// 加快速度
|
||||
mappedByteBuffer.position((int) currentPosition);
|
||||
}
|
||||
result[i * 3] = currentPosition;
|
||||
result[3 * i + 1] = data.length;
|
||||
mappedByteBuffer.putInt(data.length);
|
||||
mappedByteBuffer.put(data);
|
||||
currentPosition += data.length + 4;
|
||||
}
|
||||
}
|
||||
// 设置写的写位置
|
||||
this.writePosition.set(currentPosition);
|
||||
this.lastFlushFilePosition.set(currentPosition);
|
||||
mappedByteBuffer.position(8);
|
||||
mappedByteBuffer.putLong(currentPosition);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存单条数据
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public boolean saveData(byte[] data) {
|
||||
long position = this.writePosition.get();
|
||||
logger.debug("save data[] this.writePosition.get() " + position);
|
||||
// 表示有空余空间
|
||||
if ((position + data.length + 4) <= this.MAX_FILE_SIZE) {
|
||||
mappedByteBuffer.position((int) position);
|
||||
mappedByteBuffer.putInt(data.length);
|
||||
mappedByteBuffer.put(data);
|
||||
this.writePosition.addAndGet(data.length + 4);
|
||||
this.lastFlushFilePosition.addAndGet(data.length + 4);
|
||||
// 更新文件前16字节writePostion
|
||||
mappedByteBuffer.position(8);
|
||||
mappedByteBuffer.putLong(this.writePosition.get());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存单条数据 data文件 带长度位置的msg存储
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public boolean saveData_new(byte[] data) {
|
||||
int length = data.length;
|
||||
long position = this.writePosition.get();
|
||||
logger.error("save data[] this.writePosition.get() " + position);
|
||||
logger.error("save data[] length() " + length);
|
||||
// 表示有空余空间4byte镖师长度
|
||||
if ((position + length + 4) <= this.MAX_FILE_SIZE) {
|
||||
mappedByteBuffer.position((int) position);
|
||||
mappedByteBuffer.putInt(length);
|
||||
mappedByteBuffer.put(data);
|
||||
this.writePosition.addAndGet(length + 4);
|
||||
this.lastFlushFilePosition.addAndGet(length + 4);
|
||||
// 更新文件前16字节writePostion
|
||||
mappedByteBuffer.position(8);
|
||||
mappedByteBuffer.putLong(position + length + 4);
|
||||
mappedByteBuffer.position(8);
|
||||
// logger.error(" 写入 mappedByteBuffer.position(8)=="+mappedByteBuffer.getLong());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文件的首写地址
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public void setWritePositionToFile(long pos) {
|
||||
mappedByteBuffer.position(8);
|
||||
mappedByteBuffer.putLong(pos);
|
||||
}
|
||||
|
||||
public boolean saveData(long data) {
|
||||
long position = this.writePosition.get();
|
||||
logger.debug("save long this.writePosition.get() " + position);
|
||||
// 表示有空余空间
|
||||
if ((position + 8) <= this.MAX_FILE_SIZE) {
|
||||
mappedByteBuffer.position((int) position);
|
||||
mappedByteBuffer.putLong(data);
|
||||
this.writePosition.addAndGet(8);
|
||||
this.lastFlushFilePosition.addAndGet(8);
|
||||
// 更新文件前16字节writePostion
|
||||
mappedByteBuffer.position(8);
|
||||
mappedByteBuffer.putLong(this.writePosition.get());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean saveData(int data) {
|
||||
long position = this.writePosition.get();
|
||||
logger.debug("save int this.writePosition.get() " + position);
|
||||
// 表示有空余空间
|
||||
if ((position + 4) <= this.MAX_FILE_SIZE) {
|
||||
mappedByteBuffer.position((int) position);
|
||||
mappedByteBuffer.putInt(data);
|
||||
this.writePosition.addAndGet(4);
|
||||
// 更新文件前16字节writePostion
|
||||
mappedByteBuffer.position(8);
|
||||
mappedByteBuffer.putLong(this.writePosition.get());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行刷新,将数据写入磁盘
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long flush() {
|
||||
this.mappedByteBuffer.force();
|
||||
lastFlushFilePosition.set(this.writePosition.get());
|
||||
this.lastFlushFilePosition = this.writePosition;
|
||||
return this.lastFlushFilePosition.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的位置的文件字节数据 暂时不用
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
public byte[] getData(long start, long end) {
|
||||
if (start < 0 || start > this.writePosition.get()) {
|
||||
return null;
|
||||
}
|
||||
if (end > this.writePosition.get()) {
|
||||
end = this.writePosition.get();
|
||||
}
|
||||
int size = (int) (end - start);
|
||||
byte[] temp = new byte[size];
|
||||
this.mappedByteBuffer.position((int) start);
|
||||
this.mappedByteBuffer.get(temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取从读位置到写位置的所有数据作为一个数组 恢复订阅关系使用
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public byte[] getData() {
|
||||
int size = (int) (this.writePosition.get() - this.readPosition.get());
|
||||
logger.debug(this.writePosition.get() + " , "
|
||||
+ this.readPosition.get());
|
||||
byte[] temp = new byte[size];
|
||||
this.mappedByteBuffer.position((int) this.readPosition.get());
|
||||
this.mappedByteBuffer.get(temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
public AtomicLong getWritePosition() {
|
||||
return writePosition;
|
||||
}
|
||||
|
||||
public void setWritePosition(AtomicLong writePosition) {
|
||||
this.writePosition = writePosition;
|
||||
}
|
||||
|
||||
public AtomicLong getReadPosition() {
|
||||
return readPosition;
|
||||
}
|
||||
|
||||
public void setReadPosition(AtomicLong readPosition) {
|
||||
this.readPosition = readPosition;
|
||||
}
|
||||
|
||||
public AtomicLong getLastFlushFilePosition() {
|
||||
return lastFlushFilePosition;
|
||||
}
|
||||
|
||||
public void setLastFlushFilePosition(AtomicLong lastFlushFilePosition) {
|
||||
this.lastFlushFilePosition = lastFlushFilePosition;
|
||||
}
|
||||
|
||||
public long getLastFlushTime() {
|
||||
return lastFlushTime;
|
||||
}
|
||||
|
||||
public void setLastFlushTime(long lastFlushTime) {
|
||||
this.lastFlushTime = lastFlushTime;
|
||||
}
|
||||
|
||||
public String getFilePathAndName() {
|
||||
return filePathAndName;
|
||||
}
|
||||
|
||||
public void setFilePathAndName(String filePathAndName) {
|
||||
this.filePathAndName = filePathAndName;
|
||||
}
|
||||
|
||||
public RandomAccessFile getRaf() {
|
||||
return raf;
|
||||
}
|
||||
|
||||
public void setRaf(RandomAccessFile raf) {
|
||||
this.raf = raf;
|
||||
}
|
||||
|
||||
public MappedByteBuffer getMappedByteBuffer() {
|
||||
return mappedByteBuffer;
|
||||
}
|
||||
|
||||
public void setMappedByteBuffer(MappedByteBuffer mappedByteBuffer) {
|
||||
this.mappedByteBuffer = mappedByteBuffer;
|
||||
}
|
||||
|
||||
public FileChannel getFileChannel() {
|
||||
return fileChannel;
|
||||
}
|
||||
|
||||
public void setFileChannel(FileChannel fileChannel) {
|
||||
this.fileChannel = fileChannel;
|
||||
}
|
||||
|
||||
public static long getMaxFileSize() {
|
||||
return MAX_FILE_SIZE;
|
||||
}
|
||||
|
||||
}
|
||||
129
src/main/java/com/alibaba/middleware/race/mom/store/Message.java
Normal file
129
src/main/java/com/alibaba/middleware/race/mom/store/Message.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Message implements Serializable{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 5295808332504208830L;
|
||||
private String topic;
|
||||
private byte[] body;
|
||||
//全局唯一的消息id,不同消息不能重复
|
||||
private String msgId;
|
||||
private int offset;
|
||||
private long bornTime;
|
||||
|
||||
private Map<String, String> properties = new HashMap<String, String>();
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
public void setOffset(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
public String getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
public void setMsgId(String msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setBody(byte[] body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public byte[] getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
return properties.get(key);
|
||||
}
|
||||
/**
|
||||
* 设置消息属性
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setProperty(String key, String value) {
|
||||
properties.put(key, value);
|
||||
}
|
||||
/**
|
||||
* 删除消息属性
|
||||
* @param key
|
||||
*/
|
||||
public void removeProperty(String key) {
|
||||
properties.remove(key);
|
||||
}
|
||||
public long getBornTime() {
|
||||
return bornTime;
|
||||
}
|
||||
public void setBornTime(long bornTime) {
|
||||
this.bornTime = bornTime;
|
||||
}
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
public void setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(body);
|
||||
result = prime * result + (int) (bornTime ^ (bornTime >>> 32));
|
||||
result = prime * result + ((msgId == null) ? 0 : msgId.hashCode());
|
||||
result = prime * result + ((properties == null) ? 0 : properties.hashCode());
|
||||
result = prime * result + ((topic == null) ? 0 : topic.hashCode());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Message other = (Message) obj;
|
||||
if (!Arrays.equals(body, other.body))
|
||||
return false;
|
||||
if (bornTime != other.bornTime)
|
||||
return false;
|
||||
if (msgId == null) {
|
||||
if (other.msgId != null)
|
||||
return false;
|
||||
} else if (!msgId.equals(other.msgId))
|
||||
return false;
|
||||
if (properties == null) {
|
||||
if (other.properties != null)
|
||||
return false;
|
||||
} else if (!properties.equals(other.properties))
|
||||
return false;
|
||||
if (topic == null) {
|
||||
if (other.topic != null)
|
||||
return false;
|
||||
} else if (!topic.equals(other.topic))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Message [topic=" + topic + ", body=" + Arrays.toString(body) + ", msgId=" + msgId + ", bornTime="
|
||||
+ bornTime + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.middleware.race.mom.Message;
|
||||
/**
|
||||
*测试用的代码,是这样提供功能吗?
|
||||
*没有考虑内存映射,优化,锁,并发,只是功能,
|
||||
*大家看一下,这些接口提供了你们想的信息吗?
|
||||
* 不明白,offset ,我暂时理解为在index中的实际位置,根据这个位置可以读一个long值代表datafile中的实际数据的地址。
|
||||
*/
|
||||
public class MsgFileUtil {
|
||||
Logger logger = LoggerFactory.getLogger(MsgFileUtil.class);
|
||||
static String fileRoot = System.getProperty("user.home");
|
||||
static String fileSpt = System.getProperty("file.separator");
|
||||
|
||||
private RandomAccessFile getFile(String topic, String groupId, String queueId, String fileName) throws IOException {
|
||||
String fileStr;
|
||||
if (queueId == null || queueId.equals("") || queueId.isEmpty()) {
|
||||
// fileStr = fileRoot + fileSpt + "store" + fileSpt + "normal" +
|
||||
// fileSpt + topic + fileSpt + queueId + fileSpt +fileName;
|
||||
fileStr = fileRoot + "/store/normal/" + topic + "/" + queueId + "/" + fileName;
|
||||
} else {
|
||||
// fileStr = fileRoot + fileSpt + "store" + fileSpt + "retry" +
|
||||
// fileSpt + groupId + fileSpt + fileName;
|
||||
fileStr = fileRoot + "/store/retry/" + groupId + "/" + fileName;
|
||||
}
|
||||
File f = new File(fileStr);
|
||||
if (!f.exists()) {
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
f.createNewFile();
|
||||
}
|
||||
return new RandomAccessFile(f, "rw");
|
||||
}
|
||||
|
||||
/*
|
||||
* 给定 索引文件的地址offset,返回对应data文件的msg
|
||||
*
|
||||
*/
|
||||
public List<Message> getMsg(String topic, String queueId, String groupId, Long offset) throws IOException {
|
||||
if (offset < 0) {
|
||||
return null;
|
||||
}
|
||||
return getMsg(topic, queueId, groupId, offset, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* 根据请求参数传回List<Message>
|
||||
*
|
||||
*
|
||||
*/
|
||||
public List<Message> getMsg(String topic, String queueId, String groupId, Long offset, Long maxOffset)
|
||||
throws IOException {
|
||||
List<Message> resultMsg = new LinkedList<Message>();
|
||||
RandomAccessFile indexFile, dataFile;
|
||||
// 根据是否有queueId判定文件位置
|
||||
if (queueId == null || queueId.equals("") || queueId.isEmpty()) {
|
||||
indexFile = getFile(topic, null, queueId, "index.file");
|
||||
dataFile = getFile(topic, null, queueId, "data.file");
|
||||
} else {
|
||||
indexFile = getFile(null, groupId, null, "index.file");
|
||||
dataFile = getFile(null, groupId, null, "data.file");
|
||||
}
|
||||
|
||||
// 找到index文件的offset读出真正的data文件的offset
|
||||
|
||||
while (offset <= maxOffset && offset < indexFile.length()) {
|
||||
indexFile.seek(offset);
|
||||
long dataOffset = indexFile.readLong();
|
||||
int dataSize = indexFile.readInt();
|
||||
// 根据dataoffset读出 msg
|
||||
dataFile.seek(dataOffset);
|
||||
byte[] msgBody = new byte[dataSize];
|
||||
dataFile.read(msgBody);
|
||||
// 读出内容将index文件的type类型设置为已经读“1”
|
||||
indexFile.seek(offset + 12);
|
||||
indexFile.writeInt(1);
|
||||
Message msgTemp = JSON.parseObject(msgBody, Message.class);
|
||||
resultMsg.add(msgTemp);
|
||||
offset += 16;
|
||||
}
|
||||
return resultMsg;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 返回一个队列中所有消息
|
||||
*/
|
||||
public List<Message> getMsg(String topic, String queueId, String groupId) throws IOException {
|
||||
long offset = 0;
|
||||
long maxOffset = Long.MAX_VALUE;
|
||||
return getMsg(topic, queueId, groupId, offset, maxOffset);
|
||||
}
|
||||
|
||||
/*
|
||||
* 给定 indexfile unit { offen(long),length(int),type(int)}
|
||||
*/
|
||||
public boolean setMsg(String topic, String queueId, String groupId, Message msg) throws Exception {
|
||||
RandomAccessFile indexFile, dataFile;
|
||||
if (queueId == null || queueId.equals("") || queueId.isEmpty()) {
|
||||
indexFile = getFile(topic, null, queueId, "index.file");
|
||||
dataFile = getFile(topic, null, queueId, "data.file");
|
||||
} else {
|
||||
indexFile = getFile(null, groupId, null, "index.file");
|
||||
dataFile = getFile(null, groupId, null, "data.file");
|
||||
}
|
||||
byte[] msgByte = JSON.toJSONBytes(msg);
|
||||
long dataPos = dataFile.length();
|
||||
long indexPos = indexFile.length();
|
||||
//System.out.println("indexpos,datapos=" + indexPos + "," + dataPos);
|
||||
// 写入index
|
||||
indexFile.seek(indexPos);
|
||||
indexFile.writeLong(dataPos);
|
||||
indexFile.writeInt(msgByte.length);
|
||||
indexFile.writeInt(0);
|
||||
// 写入data
|
||||
dataFile.seek(dataPos);
|
||||
dataFile.write(msgByte);
|
||||
//System.out.println("indexpos,datapos=" + indexFile.length() + "," + dataFile.length());
|
||||
/*
|
||||
* //恢复数据 test dataPos=dataFile.length(); indexPos=indexFile.length();
|
||||
* for(long i=0;i<indexFile.length();){ indexFile.seek(i); long
|
||||
* offset=indexFile.readLong(); int size=indexFile.readInt(); int
|
||||
* type=indexFile.readInt(); System.out.println("offset="+offset+
|
||||
* " ,size="+size+" ,type="+type); dataFile.seek(offset); byte[]
|
||||
* bufferTemp=new byte[size]; dataFile.read(bufferTemp);
|
||||
* msg=JSON.parseObject(bufferTemp,Message.class);
|
||||
* System.out.println(msg.toString()); i=indexFile.getFilePointer(); }
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Message msg = new Message();
|
||||
// msg.setMsgId("msgID");
|
||||
msg.setBody("dddddfgf".getBytes());
|
||||
msg.setBornTime(System.currentTimeMillis());
|
||||
long startTime = System.currentTimeMillis();
|
||||
new MsgFileUtil().setMsg("topic", "queueId", null, msg);
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("统计 存一条数据 时间 endTime-startTime= " + (endTime - startTime));
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
List<Message> msgList = new MsgFileUtil().getMsg("topic", "queueId", null, 0L);
|
||||
endTime = System.currentTimeMillis();
|
||||
System.out.println("统计 取一条数据 时间 endTime-startTime= " + (endTime - startTime));
|
||||
// test
|
||||
System.out.println("main " + msgList.get(0).toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* By ChenQi
|
||||
* 消息持久化接口
|
||||
* */
|
||||
public interface MsgStore {
|
||||
|
||||
/*
|
||||
* index baseDir/Normal/Topic/GroupId/QueueId/index/ 0,1,2 data
|
||||
* baseDir/Normal/Topic/GroupId/QueueId/data/ 0,1,2
|
||||
*
|
||||
*
|
||||
* baseDir/Retry/Group/index/0,1,2 baseDir/Retry/Group/data/0,1,2
|
||||
*/
|
||||
// 一些参数定义初始化
|
||||
public void init();
|
||||
|
||||
// 关闭
|
||||
public void close();
|
||||
|
||||
// 存储正常消息, 成功true,失败false
|
||||
public boolean writeByteNormal(String topicId, String queueId,
|
||||
List<byte[]> msgList);
|
||||
|
||||
// 取消息每个消息的字节码存为list返回
|
||||
public List<byte[]> readByteNormal(String topicID, String queueId,
|
||||
int offset, int MaxOffset);
|
||||
|
||||
// 存储重试消息, 成功true,失败false
|
||||
public boolean writeByteRetry(String groupId, List<byte[]> msgList);
|
||||
|
||||
// 取重试消息每个消息的字节码存为list返回
|
||||
public ArrayList<byte[]> readByteRetry(String groupId, int offset,
|
||||
int MaxOffset);
|
||||
|
||||
// 组写 为数据一组为单位,在index 与data 文件分开写
|
||||
boolean writeByteNormalByOnce(String topicId, String queueId,
|
||||
List<byte[]> msgList);
|
||||
|
||||
// 组写 为数据一组为单位,在index 与data 文件分开写
|
||||
boolean writeByteRetryByOnce(String groupId, List<byte[]> msgList);
|
||||
|
||||
// 组读 为数据一组为单位,在index 与data 文件分开读
|
||||
List<byte[]> readByteNormalByOnce(String topicId, String queueId,
|
||||
int offset, int MaxOffset);
|
||||
|
||||
// 组读 为数据一组为单位,在index 与data 文件分开读
|
||||
List<byte[]> readByteRetryByOnce(String queueId, int offset, int MaxOffset);
|
||||
|
||||
// 落盘-临时文件缓冲
|
||||
/**
|
||||
* 我传你的文件,你按顺序加入你的落盘逻辑,文件里的格式( 消息大小(int)+消息实体(body))
|
||||
* 你需要一条一条读,然后读完一个文件刷一次就行。刷完就删除哈。 记住,可能你还没读完的时候
|
||||
* 我又调用这个api传你另外一个文件,所以你需要加入一个链表。 按顺序用完就删,不断的取。
|
||||
*
|
||||
* @param filename
|
||||
*/
|
||||
public void writeMappedFileTemp(String filename);
|
||||
|
||||
// test use
|
||||
public void testprintlong();
|
||||
|
||||
public void loadAndRecover();
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
//package com.alibaba.middleware.race.mom.store;
|
||||
//
|
||||
//import java.io.File;
|
||||
//import java.io.FileDescriptor;
|
||||
//import java.io.IOException;
|
||||
//import java.io.RandomAccessFile;
|
||||
//import java.nio.MappedByteBuffer;
|
||||
//import java.nio.channels.FileChannel;
|
||||
//import java.nio.channels.FileChannel.MapMode;
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
//import java.util.concurrent.ConcurrentHashMap;
|
||||
//import java.util.concurrent.CountDownLatch;
|
||||
//import java.util.concurrent.ExecutorService;
|
||||
//import java.util.concurrent.Executors;
|
||||
//import java.util.concurrent.atomic.AtomicLong;
|
||||
//
|
||||
//import org.slf4j.Logger;
|
||||
//import org.slf4j.LoggerFactory;
|
||||
//
|
||||
///*
|
||||
// * baseDir/Normal/Topic/Group/Consumer/Queue/(index.file,data.file)
|
||||
// * baseDir/Retry/Group/(index.file,data.file)
|
||||
// * 题目没有consumerID就默认生成和groupID一样的
|
||||
// * */
|
||||
//
|
||||
//public class MsgStoreImp_MappedBuffer implements MsgStore {
|
||||
// private static Logger logger = LoggerFactory.getLogger(MsgStoreImp_MappedBuffer.class);
|
||||
// public static final int OS_PAGE_SIZE = 1024 * 4;
|
||||
// // 当前JVM中映射的虚拟内存总大小
|
||||
// /*private static final AtomicLong TotalMapedVitualMemory = new AtomicLong(0);
|
||||
// // 当前JVM中mmap句柄数量
|
||||
// private static final AtomicInteger TotalMapedFiles = new AtomicInteger(0);*/
|
||||
// // 文件大小
|
||||
// private final static long MAX_FILE_SIZE = 1024 * 1024 * 1024;
|
||||
// // 文件写入位置
|
||||
// private AtomicLong indexWritePosition = new AtomicLong(0);
|
||||
// private AtomicLong dataWritePosition = new AtomicLong(0);
|
||||
// // 最后一次刷数据的时间
|
||||
// private long lastFlushTime = System.currentTimeMillis();
|
||||
// // 上一次刷的文件位置
|
||||
// private AtomicLong lastFlushFilePosition = new AtomicLong(0);
|
||||
// // 最大的脏数据量,系统必须触发一次强制刷
|
||||
// private long MAX_INDEX_BYTE=16;
|
||||
// private long MAX_FLUSH_DATA_SIZE = 16 *1; // indexsize=16byte 时间和字节共同
|
||||
// // 最大的时间间隔,系统必须触发一次强制刷
|
||||
// private long MAX_FLUSH_TIME_GAP = 25; //30ms 因为一次记录写入时间基本在30ms 误差一两条
|
||||
// RandomAccessFile indexFile, dataFile;
|
||||
// FileDescriptor indexFileDescriptor;
|
||||
// FileDescriptor dataFileDescriptor;
|
||||
// ConcurrentHashMap<String, RandomAccessFile> fileHashMap = new ConcurrentHashMap<String, RandomAccessFile>();
|
||||
// ConcurrentHashMap<RandomAccessFile, MappedByteBuffer> mappedByteBufferHashMap = new ConcurrentHashMap<RandomAccessFile, MappedByteBuffer>();
|
||||
// MappedByteBuffer dataMappedByteBuffer, indexMappedByteBuffer;
|
||||
//
|
||||
// @Override
|
||||
// public void init() {
|
||||
// // TODO Auto-generated method stub
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void close() {
|
||||
// // TODO Auto-generated method stub
|
||||
// try {
|
||||
//
|
||||
// if (indexFile != null) {
|
||||
// indexFile.close();
|
||||
// }
|
||||
// if (dataFile != null) {
|
||||
// dataFile.close();
|
||||
// }
|
||||
//
|
||||
// } catch (IOException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// } finally {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public synchronized void getPositionForIndexAndData() {
|
||||
// try {
|
||||
// // 开始定位到第一条没消费记录
|
||||
// if (this.indexWritePosition.get() == 0) {
|
||||
// long i = 0;
|
||||
// long length = indexFile.length();
|
||||
// // logger.debug("indexfile.length= " + length);
|
||||
// while (i < length) {
|
||||
// // 定位到未读的标示8+4+4
|
||||
// indexMappedByteBuffer.position((int) (i + 12));
|
||||
// int temp = indexMappedByteBuffer.getInt();
|
||||
// if (temp == -1) {
|
||||
// i += 16;
|
||||
// continue;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// if (i < 16) {
|
||||
// this.dataWritePosition.set(0);
|
||||
// this.indexWritePosition.set(0);
|
||||
// } else {
|
||||
// indexMappedByteBuffer.position((int) (i - 16));
|
||||
// this.dataWritePosition.set(indexMappedByteBuffer.getLong() + indexMappedByteBuffer.getInt());
|
||||
// this.indexWritePosition.set(i);
|
||||
// }
|
||||
//
|
||||
// logger.debug("this.idnexwirtepostion== ,this.dataWritePostion== " + this.indexWritePosition.get() + ", "
|
||||
// + this.dataWritePosition.get());
|
||||
// }
|
||||
//
|
||||
// } catch (IOException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public synchronized void getMappedByteBuffer(String topicId, String groupId, String consumerId, String queueId) {
|
||||
// if (topicId == null || topicId.isEmpty()) {
|
||||
// indexFile = getFile(null, groupId, null, null, StoreConfig.indexFile);
|
||||
// dataFile = getFile(null, groupId, null, null, StoreConfig.dataFile);
|
||||
// } else {
|
||||
// if (consumerId == null || consumerId.isEmpty()) {
|
||||
// consumerId = groupId;
|
||||
// }
|
||||
// indexFile = getFile(topicId, groupId, consumerId, queueId, StoreConfig.indexFile);
|
||||
// dataFile = getFile(topicId, groupId, consumerId, queueId, StoreConfig.dataFile);
|
||||
// }
|
||||
//
|
||||
// indexMappedByteBuffer = MapedFile(indexFile, 0, MAX_FILE_SIZE);
|
||||
// dataMappedByteBuffer = MapedFile(dataFile, 0, MAX_FILE_SIZE);
|
||||
// }
|
||||
//
|
||||
// public boolean saveMessage(String topicId, String groupId, String consumerId, String queueId,
|
||||
// List<byte[]> msgList) {
|
||||
//
|
||||
// getMappedByteBuffer(topicId, groupId, consumerId, queueId);
|
||||
// getPositionForIndexAndData();
|
||||
// byte[] tempMsg = null;
|
||||
// for (int i = 0; i < msgList.size(); i++) {
|
||||
// tempMsg = msgList.get(i);
|
||||
// // 写入 index
|
||||
// if (i == 0) {
|
||||
// indexMappedByteBuffer.position((int) this.indexWritePosition.get());
|
||||
// }
|
||||
//
|
||||
// indexMappedByteBuffer.putLong(this.dataWritePosition.get());
|
||||
// indexMappedByteBuffer.putInt(tempMsg.length);
|
||||
// indexMappedByteBuffer.putInt(-1);
|
||||
// this.indexWritePosition.addAndGet(16);
|
||||
// // this.indexWritePosition.set(indexMappedByteBuffer.position());
|
||||
// // 写入data
|
||||
// if (i == 0) {
|
||||
// dataMappedByteBuffer.position((int) this.dataWritePosition.get());
|
||||
// }
|
||||
//
|
||||
// dataMappedByteBuffer.put(tempMsg);
|
||||
// // this.dataWritePosition += msg.length;
|
||||
// this.dataWritePosition.addAndGet(tempMsg.length);
|
||||
// // this.dataWritePosition.set(dataMappedByteBuffer.position());
|
||||
// }
|
||||
// if (this.dataWritePosition.get() >= MAX_FILE_SIZE || this.indexWritePosition.get() >= MAX_FILE_SIZE) {
|
||||
// // this.dataWritePosition -= msg.length;
|
||||
// // this.indexWritePosition -= 16;
|
||||
// this.dataWritePosition.addAndGet(-(tempMsg.length));
|
||||
// this.indexWritePosition.addAndGet(-16);
|
||||
// logger.debug("文件内存空间不够!");
|
||||
// flush();
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// long st1=System.currentTimeMillis();
|
||||
// flush();
|
||||
//
|
||||
// return true;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public boolean saveMessage(String topicId, String groupId, String consumerId, String queueId, byte[] msg) {
|
||||
//
|
||||
// getMappedByteBuffer(topicId, groupId, consumerId, queueId);
|
||||
// getPositionForIndexAndData();
|
||||
// // 写入 index
|
||||
// indexMappedByteBuffer.position((int) this.indexWritePosition.get());
|
||||
// indexMappedByteBuffer.putLong(this.dataWritePosition.get());
|
||||
// indexMappedByteBuffer.putInt(msg.length);
|
||||
// indexMappedByteBuffer.putInt(-1);
|
||||
// this.indexWritePosition.addAndGet(16);
|
||||
// // 写入data
|
||||
// dataMappedByteBuffer.position((int) this.dataWritePosition.get());
|
||||
// dataMappedByteBuffer.put(msg);
|
||||
// // this.dataWritePosition += msg.length;
|
||||
// this.dataWritePosition.addAndGet(msg.length);
|
||||
// if (this.dataWritePosition.get() >= MAX_FILE_SIZE || this.indexWritePosition.get() >= MAX_FILE_SIZE) {
|
||||
// // this.dataWritePosition -= msg.length;
|
||||
// // this.indexWritePosition -= 16;
|
||||
// this.dataWritePosition.addAndGet(-msg.length);
|
||||
// this.indexWritePosition.addAndGet(-16);
|
||||
// logger.debug("文件内存空间不够!");
|
||||
// flush();
|
||||
// return false;
|
||||
// }
|
||||
// flush();
|
||||
// return true;
|
||||
//
|
||||
// }
|
||||
// CountDownLatch countDownLatch;
|
||||
// ExecutorService flushPool = Executors.newFixedThreadPool(2);
|
||||
// public synchronized void flush() {
|
||||
// countDownLatch=new CountDownLatch(2);
|
||||
// flushPool.execute(new Thread(){
|
||||
// @Override
|
||||
// public void run() {
|
||||
// // TODO Auto-generated method stub
|
||||
// // long t1=System.currentTimeMillis();
|
||||
// try{
|
||||
// MsgStoreImp_MappedBuffer.this.indexMappedByteBuffer.force();
|
||||
// }finally{
|
||||
// countDownLatch.countDown();
|
||||
// }
|
||||
// // long t2=System.currentTimeMillis();
|
||||
// // System.out.println(" force index t2-t1=="+(t2-t1));
|
||||
// }
|
||||
// });
|
||||
// flushPool.execute(new Thread(){
|
||||
// @Override
|
||||
// public void run() {
|
||||
// // TODO Auto-generated method stub
|
||||
// //long t1=System.currentTimeMillis();
|
||||
// try{
|
||||
// MsgStoreImp_MappedBuffer.this.dataMappedByteBuffer.force();
|
||||
// }finally{
|
||||
// countDownLatch.countDown();
|
||||
//
|
||||
// }
|
||||
// // long t2=System.currentTimeMillis();
|
||||
// //System.out.println(" force data t2-t1=="+(t2-t1));
|
||||
// }
|
||||
// });
|
||||
// try {
|
||||
// countDownLatch.await();
|
||||
// } catch (InterruptedException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// // flushPool.shutdown();
|
||||
// this.lastFlushTime = System.currentTimeMillis();
|
||||
// this.lastFlushFilePosition = this.indexWritePosition;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean writeByteNormal(String topicId, String groupId, String consumerId, String queueId, byte[] msg) {
|
||||
// // TODO Auto-generated method stub
|
||||
//
|
||||
// return saveMessage(topicId, groupId, consumerId, queueId, msg);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public boolean writeByteNormal(String topicId, String groupId, String consumerId, String queueId,
|
||||
// List<byte[]> msgList) {
|
||||
// // TODO Auto-generated method stub
|
||||
// return saveMessage(topicId, groupId, consumerId, queueId, msgList);
|
||||
// }
|
||||
//
|
||||
// public synchronized MappedByteBuffer MapedFile(RandomAccessFile raf, long startPos, long fileSize) {
|
||||
// MappedByteBuffer mappedByteBuffer = mappedByteBufferHashMap.get(raf);
|
||||
// if (mappedByteBuffer != null) {
|
||||
// return mappedByteBuffer;
|
||||
// }
|
||||
// try {
|
||||
// FileChannel fileChannel = raf.getChannel();
|
||||
// mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, startPos, fileSize);
|
||||
// /*TotalMapedVitualMemory.addAndGet(fileSize);
|
||||
// TotalMapedFiles.incrementAndGet();*/
|
||||
// } catch (Exception e) {
|
||||
// logger.debug("内存映射沒有建立");
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// mappedByteBufferHashMap.put(raf, mappedByteBuffer);
|
||||
// return mappedByteBuffer;
|
||||
// }
|
||||
//
|
||||
// private RandomAccessFile getFile(String topicId, String groupId, String consumerId, String queueId,
|
||||
// String fileName) {
|
||||
// String file;
|
||||
// if (topicId == null || topicId.isEmpty()) {
|
||||
// file = StoreConfig.baseDir + StoreConfig.retryDir + groupId + "/" + fileName;
|
||||
// } else {
|
||||
// file = StoreConfig.baseDir + StoreConfig.normalDir + topicId + "/" + groupId + "/" + consumerId + "/"
|
||||
// + queueId + "/" + fileName;
|
||||
// }
|
||||
// // 查找hashmap
|
||||
// RandomAccessFile raf = fileHashMap.get(file);
|
||||
// if (raf != null) {
|
||||
// return raf;
|
||||
// }
|
||||
// File f = new File(file);
|
||||
// try {
|
||||
// if (!f.exists()) {
|
||||
// if (!f.getParentFile().exists()) {
|
||||
// f.getParentFile().mkdirs();
|
||||
// }
|
||||
// f.createNewFile();
|
||||
// }
|
||||
// raf = new RandomAccessFile(f, "rw");
|
||||
// } catch (Exception e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// fileHashMap.put(file, raf);
|
||||
// return raf;
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// * 根据请求参数传回List<Message>
|
||||
// *
|
||||
// *
|
||||
// */
|
||||
// public ArrayList<byte[]> getMessage(String topicId, String groupId, String consumerId, String queueId, int offset,
|
||||
// int maxOffset) {
|
||||
// ArrayList<byte[]> resultList = new ArrayList<byte[]>();
|
||||
// if (topicId == null || topicId.isEmpty()) {
|
||||
// indexFile = getFile(null, groupId, null, null, StoreConfig.indexFile);
|
||||
// dataFile = getFile(null, groupId, null, null, StoreConfig.dataFile);
|
||||
// } else {
|
||||
// if (consumerId == null || consumerId.isEmpty()) {
|
||||
// consumerId = groupId;
|
||||
// }
|
||||
// indexFile = getFile(topicId, groupId, consumerId, queueId, StoreConfig.indexFile);
|
||||
// dataFile = getFile(topicId, groupId, consumerId, queueId, StoreConfig.dataFile);
|
||||
// }
|
||||
//
|
||||
// long len = 0;
|
||||
// try {
|
||||
// len = indexFile.length() / 16;
|
||||
// } catch (IOException e1) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e1.printStackTrace();
|
||||
// }
|
||||
//
|
||||
// while (offset <= maxOffset && offset < len) {
|
||||
// // long+int+int =16
|
||||
// try {
|
||||
// int indexoffset = offset * 16;
|
||||
// indexFile.seek(indexoffset);
|
||||
// long dataOffset = indexFile.readLong();
|
||||
// int dataSize = indexFile.readInt();
|
||||
// // 根据dataoffset读出 msg
|
||||
// dataFile.seek(dataOffset);
|
||||
// byte[] msgBody = new byte[dataSize];
|
||||
// dataFile.read(msgBody);
|
||||
// // 读出内容将index文件的type类型设置为已经读“1”
|
||||
// indexFile.seek(indexoffset + 12);
|
||||
// indexFile.writeInt(1);
|
||||
// resultList.add(msgBody);
|
||||
// offset += 1;
|
||||
// } catch (IOException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// return resultList;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return resultList;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ArrayList<byte[]> readByteNormal(String topicId, String groupId, String consumerId, String queueId,
|
||||
// int offset, int maxOffset) {
|
||||
// // TODO Auto-generated method stub
|
||||
// return getMessage(topicId, groupId, consumerId, queueId, offset, maxOffset);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean writeByteRetry(String groupId, byte[] msg) {
|
||||
// // TODO Auto-generated method stub
|
||||
// return saveMessage(null, groupId, null, null, msg);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ArrayList<byte[]> readByteRetry(String groupId, int offset, int maxOffset) {
|
||||
//
|
||||
// return getMessage(null, groupId, null, null, offset, maxOffset);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public static void main(String[] args) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<byte[]> readByteByOffsets(String topicID, String group,
|
||||
// String consumer, String queueID, Integer[] offsets) {
|
||||
// // TODO Auto-generated method stub
|
||||
// return null;
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,239 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/*
|
||||
* baseDir/Normal/Topic/Group/Consumer/Queue/(index.file,data.file)
|
||||
* baseDir/Retry/Group/(index.file,data.file)
|
||||
* 题目没有consumerID就默认生成和groupID一样的
|
||||
* */
|
||||
|
||||
public class MsgStoreImp_MappedBuffer2 implements MsgStore {
|
||||
private static Logger logger = LoggerFactory
|
||||
.getLogger(MsgStoreImp_MappedBuffer2.class);
|
||||
private static ConcurrentHashMap<String, FileStoreManager> fileStoreManagerMap = new ConcurrentHashMap<String, FileStoreManager>();
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// TODO Auto-generated method stub
|
||||
fileStoreManagerMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeByteNormal(String topicId, String queueId,
|
||||
List<byte[]> msgList) {
|
||||
// 存储文件路径
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.normalDir + topicId
|
||||
+ "/" + queueId;
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath);
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
logger.debug("建立filestoremanager= " + filePath);
|
||||
}
|
||||
return fileStoreManager.saveMessage(msgList);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testprintlong() {
|
||||
System.out.println("FileStoreManager.savemessage"
|
||||
+ FileStoreManager.savemessage);
|
||||
System.err.println("FileStoreManager.savemessagebyonce"
|
||||
+ FileStoreManager.savemessagebyonce);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeByteNormalByOnce(String topicId, String queueId,
|
||||
List<byte[]> msgList) {
|
||||
// 存储文件路径
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.normalDir + topicId
|
||||
+ "/" + queueId;
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath);
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
logger.debug("建立filestoremanager= " + filePath);
|
||||
}
|
||||
return fileStoreManager.saveMessageByOnce(msgList);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeByteRetry(String groupId, List<byte[]> msgList) {
|
||||
// TODO Auto-generated method stub
|
||||
// 存储文件路径
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.retryDir + groupId;
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath);
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
}
|
||||
return fileStoreManager.saveMessage(msgList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeByteRetryByOnce(String groupId, List<byte[]> msgList) {
|
||||
// TODO Auto-generated method stub
|
||||
// 存储文件路径
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.retryDir + groupId;
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath);
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
}
|
||||
return fileStoreManager.saveMessageByOnce(msgList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<byte[]> readByteRetry(String groupId, int offset,
|
||||
int MaxOffset) {
|
||||
// 存储文件路径
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.retryDir + groupId;
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath);
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
}
|
||||
return (ArrayList) fileStoreManager.getMessage(offset, MaxOffset);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<byte[]> readByteNormal(String topicId, String queueId,
|
||||
int offset, int MaxOffset) {
|
||||
// 存储文件路径
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.normalDir + topicId
|
||||
+ "/" + queueId;
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath);
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
logger.debug("读数据建立filestoremanage");
|
||||
}
|
||||
logger.debug("读数据的起始地址为" + offset + " " + MaxOffset);
|
||||
return (ArrayList) fileStoreManager.getMessage(offset, MaxOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<byte[]> readByteNormalByOnce(String topicId, String queueId,
|
||||
int offset, int MaxOffset) {
|
||||
// 存储文件路径
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.normalDir + topicId
|
||||
+ "/" + queueId;
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath);
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
logger.debug("读数据建立filestoremanage");
|
||||
}
|
||||
logger.debug("读数据的起始地址为" + offset + " " + MaxOffset);
|
||||
return (ArrayList) fileStoreManager.getMessageByOnce(offset, MaxOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<byte[]> readByteRetryByOnce(String queueId, int offset,
|
||||
int MaxOffset) {
|
||||
// 存储文件路径
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.normalDir + "/"
|
||||
+ queueId;
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath);
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
logger.debug("读数据建立filestoremanage");
|
||||
}
|
||||
logger.debug("读数据的起始地址为" + offset + " " + MaxOffset);
|
||||
return (ArrayList) fileStoreManager.getMessageByOnce(offset, MaxOffset);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMappedFileTemp(String filename) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAndRecover() {
|
||||
// 恢复正常消息队列
|
||||
String filePath = StoreConfig.baseDir + StoreConfig.normalDir;
|
||||
ArrayList<String[]> rsPath = this.scanFilePathForNormal(filePath);
|
||||
if (rsPath == null)
|
||||
return;
|
||||
for (String[] path : rsPath) {
|
||||
// 存储文件路径
|
||||
logger.error(filePath.toString());
|
||||
filePath = StoreConfig.baseDir + StoreConfig.normalDir+path[0] + "/" + path[1];
|
||||
logger.error(filePath.toString());
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap
|
||||
.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath, "testrecover");
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
}
|
||||
}
|
||||
// 恢复订阅关系
|
||||
filePath = StoreConfig.baseDir + StoreConfig.subscribeDir;
|
||||
logger.error("subFfilepath="+filePath);
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap.get(filePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(filePath, "testrecover");
|
||||
fileStoreManagerMap.put(filePath, fileStoreManager);
|
||||
}
|
||||
//恢复retry关系
|
||||
//filePath = StoreConfig.baseDir + StoreConfig.;
|
||||
}
|
||||
|
||||
public ArrayList<String[]> scanFilePathForNormal(String filePath) {
|
||||
ArrayList<String[]> path = new ArrayList<String[]>();
|
||||
File[] fileList = new File(filePath).listFiles();
|
||||
if (fileList == null)
|
||||
return null;
|
||||
for (File tempFile : fileList) {
|
||||
if (tempFile.isDirectory()) {
|
||||
for (File temp : tempFile.listFiles()) {
|
||||
String[] tq = new String[2];
|
||||
String fs = System.getProperty("file.separator");
|
||||
System.out.println(temp.toString() + " fs= " + fs);
|
||||
String t = temp.toString();
|
||||
int i = t.lastIndexOf(fs);
|
||||
tq[1] = t.substring(i + 1);
|
||||
int j = t.lastIndexOf(fs, i - 1);
|
||||
tq[0] = t.substring(j + 1, i);
|
||||
System.out.println(i + "-----" + j);
|
||||
path.add(tq);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String[] t : path) {
|
||||
System.out.println(t[0] + " / " + t[1]);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class OffsetNum implements Serializable {
|
||||
/**
|
||||
* offset 的数值
|
||||
*/
|
||||
private static final long serialVersionUID = -352056806960750564L;
|
||||
int i;
|
||||
public int getI() {
|
||||
return i;
|
||||
}
|
||||
public OffsetNum setI(int i) {
|
||||
this.i = i;
|
||||
return this;
|
||||
}
|
||||
public String toString(){
|
||||
return String.valueOf(i);
|
||||
}
|
||||
}
|
||||
133
src/main/java/com/alibaba/middleware/race/mom/store/Redo.java
Normal file
133
src/main/java/com/alibaba/middleware/race/mom/store/Redo.java
Normal file
@@ -0,0 +1,133 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.Serializable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileChannel.MapMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
public class Redo {
|
||||
public static String redoFile = System.getProperty("user.home")
|
||||
+ File.separator +"0";
|
||||
public MappedByteBuffer mbb;
|
||||
public File file;
|
||||
public RandomAccessFile raf;
|
||||
public AtomicLong filename = new AtomicLong(1);
|
||||
public int filesize = 1024*1024 * 1024;
|
||||
private RedoBean rb=new RedoBean();
|
||||
|
||||
|
||||
public void writeLog(String msgid,String topicAndfilter,byte[] body)
|
||||
{
|
||||
rb.getBody().add(body);
|
||||
rb.getMsgId().add(msgid);
|
||||
rb.getTopicAfilter().add(topicAndfilter);
|
||||
}
|
||||
public void commitLog(RedoBean rb) {
|
||||
byte[] smByte = JSON.toJSONBytes(rb);
|
||||
if (mbb.position() + smByte.length > filesize) {
|
||||
filename.getAndIncrement();
|
||||
try {
|
||||
mbb = new RandomAccessFile(getNewFile(), "rw").getChannel().map(
|
||||
FileChannel.MapMode.READ_WRITE, 0, filesize);;
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
mbb.putInt(smByte.length);
|
||||
mbb.put(smByte);
|
||||
}
|
||||
|
||||
public void flushLog() {
|
||||
commitLog(rb);
|
||||
mbb.force();
|
||||
rb=new RedoBean();
|
||||
}
|
||||
|
||||
public Redo() {
|
||||
file = new File(redoFile);
|
||||
ensureDirOK(file.getParent());
|
||||
RandomAccessFile raf;
|
||||
try {
|
||||
raf = new RandomAccessFile(file, "rw");
|
||||
FileChannel fileChannel = raf.getChannel();
|
||||
mbb = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, filesize);
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
public File getNewFile() throws FileNotFoundException,
|
||||
IOException {
|
||||
String newfilename = new String(""
|
||||
+ (Long.parseLong(file.getName()) + filesize));
|
||||
file = new File(System.getProperty("user.home") + File.separator
|
||||
+ "store" + File.separator + newfilename);
|
||||
return file;
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws IOException {
|
||||
for(int j=0;j<100;j++)
|
||||
{
|
||||
Redo redo = new Redo();
|
||||
long begin=System.currentTimeMillis();
|
||||
for(int i=0;i<200;i++)
|
||||
{
|
||||
redo.writeLog("msg-"+i, "topic-and-filter-y", "hello mom I'm body".getBytes());
|
||||
}
|
||||
redo.flushLog();
|
||||
System.out.println(System.currentTimeMillis()-begin);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public void ensureDirOK(final String dirName) {
|
||||
if (dirName != null) {
|
||||
File f = new File(dirName);
|
||||
if (!f.exists()) {
|
||||
f.mkdirs();
|
||||
}
|
||||
}
|
||||
}
|
||||
class RedoBean implements Serializable{
|
||||
|
||||
/**
|
||||
* 存储的实体
|
||||
*/
|
||||
private static final long serialVersionUID = -5597911942382576497L;
|
||||
List<String> topicAfilter=new ArrayList<>();
|
||||
List<String> msgId=new ArrayList<>();
|
||||
List<byte[]> body=new ArrayList<>();
|
||||
public List<String> getTopicAfilter() {
|
||||
return topicAfilter;
|
||||
}
|
||||
public void setTopicAfilter(List<String> topicAfilter) {
|
||||
this.topicAfilter = topicAfilter;
|
||||
}
|
||||
public List<String> getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
public void setMsgId(List<String> msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
public List<byte[]> getBody() {
|
||||
return body;
|
||||
}
|
||||
public void setBody(List<byte[]> body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
163
src/main/java/com/alibaba/middleware/race/mom/store/RedoLog.java
Normal file
163
src/main/java/com/alibaba/middleware/race/mom/store/RedoLog.java
Normal file
@@ -0,0 +1,163 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel.MapMode;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
public class RedoLog {
|
||||
private static String redoFile = StoreConfig.baseDir
|
||||
+ "oo33o" + "r2222edo";
|
||||
public MappedByteBuffer mbb;
|
||||
public int fileSize = repeatNum*(msgSize+1);
|
||||
public static int repeatNum = 512;
|
||||
public static int msgSize = 64;
|
||||
public char FLAG = 0;
|
||||
//god bless
|
||||
public RedoLog() {
|
||||
this.mbb = this.getFileMappedByteBuffer(redoFile, 0, fileSize);
|
||||
}
|
||||
|
||||
// msg=head +body :
|
||||
public boolean writeLog(String topic, String filter, int queueIndex,
|
||||
ArrayList<byte[]> bodys) {
|
||||
// LogBean lb=new LogBean(topic, filter, 0,bodys);
|
||||
// byte[] data=JSON.toJSONBytes(lb);
|
||||
for (int i = 0; i < bodys.size(); i++) {
|
||||
String head = topic + FLAG + filter + FLAG + queueIndex;
|
||||
byte[] headByte = head.getBytes();
|
||||
// head.size head body.szie body
|
||||
ByteBuffer msgByteBuffer = ByteBuffer.allocate(msgSize);
|
||||
|
||||
msgByteBuffer.putInt(headByte.length);
|
||||
msgByteBuffer.put(headByte);
|
||||
msgByteBuffer.putInt(bodys.get(i).length);
|
||||
msgByteBuffer.put(bodys.get(i));
|
||||
boolean flag = this.writeLog(msgByteBuffer);
|
||||
if (!flag) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
//this.flushLog();
|
||||
|
||||
}
|
||||
|
||||
private boolean writebytes(byte[] smByte)
|
||||
{
|
||||
if (mbb.position() + smByte.length > fileSize) {
|
||||
return false;
|
||||
}
|
||||
mbb.putInt(smByte.length);
|
||||
mbb.put(smByte);
|
||||
return true;
|
||||
}
|
||||
private boolean writeLog(ByteBuffer smByte) {
|
||||
if (mbb.position() + smByte.limit() > fileSize) {
|
||||
return false;
|
||||
}
|
||||
mbb.putInt(smByte.limit());
|
||||
smByte.flip();
|
||||
mbb.put(smByte);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void flushLog() {
|
||||
mbb.force();
|
||||
if (mbb.position() > repeatNum * msgSize)
|
||||
mbb.position(0);
|
||||
}
|
||||
private MappedByteBuffer getFileMappedByteBuffer(String filePathAndName,
|
||||
int startPos, int fileSize) {
|
||||
File f = new File(filePathAndName);
|
||||
try {
|
||||
if (!f.exists()) {
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
f.createNewFile();
|
||||
}
|
||||
return mbb = new RandomAccessFile(f, "rw").getChannel().map(
|
||||
MapMode.READ_WRITE, startPos, fileSize);
|
||||
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws IOException {
|
||||
RedoLog redo = new RedoLog();
|
||||
char FLAG = 0;
|
||||
for (int j = 0; j < 500; j++) {
|
||||
long begin = System.currentTimeMillis();
|
||||
for (int i = 0; i < 200; i++) {
|
||||
/*
|
||||
* String data = "topic" + i + FLAG + "fitler" + FLAG +
|
||||
* "hello mom Im baby"; ByteBuffer smByte =
|
||||
* ByteBuffer.allocate(redo.msgSize);
|
||||
* smByte.put(data.getBytes()); redo.writeLog(smByte);
|
||||
*/
|
||||
ArrayList<byte[]> list = new ArrayList<byte[]>();
|
||||
list.add("1234".getBytes());
|
||||
|
||||
redo.writeLog("topic", "filter", 0, list);
|
||||
}
|
||||
long flushtime = System.currentTimeMillis();
|
||||
redo.flushLog();
|
||||
System.out.println("writetime:" + (flushtime - begin)
|
||||
+ " flushtime:" + (System.currentTimeMillis() - flushtime));
|
||||
}
|
||||
|
||||
}
|
||||
class LogBean
|
||||
{
|
||||
String topic;
|
||||
String filter;
|
||||
int queueid;
|
||||
ArrayList<byte[]> list=new ArrayList<>();
|
||||
|
||||
public LogBean(String topic, String filter, int queueid, ArrayList<byte[]> list) {
|
||||
super();
|
||||
this.topic = topic;
|
||||
this.filter = filter;
|
||||
this.queueid = queueid;
|
||||
this.list = list;
|
||||
}
|
||||
public int getQueueid() {
|
||||
return queueid;
|
||||
}
|
||||
public void setQueueid(int queueid) {
|
||||
this.queueid = queueid;
|
||||
}
|
||||
public LogBean(String topic, String filter, ArrayList<byte[]> list) {
|
||||
super();
|
||||
this.topic = topic;
|
||||
this.filter = filter;
|
||||
this.list = list;
|
||||
}
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
public String getFilter() {
|
||||
return filter;
|
||||
}
|
||||
public void setFilter(String filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
public ArrayList<byte[]> getList() {
|
||||
return list;
|
||||
}
|
||||
public void setList(ArrayList<byte[]> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
public class StoreConfig {
|
||||
// 主目录
|
||||
public static String fileRoot = System.getProperty("user.home");
|
||||
// public static String fileSpt = System.getProperty("file.separator");
|
||||
public static String baseDir = fileRoot + "/store/";
|
||||
// 正常消息文件
|
||||
public static String normalDir = "normal/";
|
||||
// 重试消息文件
|
||||
public static String retryDir = "retry/";
|
||||
// 订阅关系
|
||||
public static String subscribeDir = "subscribe/";
|
||||
//
|
||||
public static String arrayDir = "array/";
|
||||
|
||||
// 索引文件名
|
||||
/*
|
||||
* 默认 offset(8byte) size(4byte) extra(4byte)
|
||||
*/
|
||||
public static int offsetByte = 8, sizeByte = 4, extraByte = 4;
|
||||
public static String indexFile = "index.file";
|
||||
// 消息文件名
|
||||
public static String dataFile = "data.file";
|
||||
// 订阅关系文件名
|
||||
public static String subDataFile = "subData.file";
|
||||
public static String subIndexFile = "subIndex.file";
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class StoreMsg implements Serializable{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -2571744737752908548L;
|
||||
String id;
|
||||
String topic;
|
||||
int queueid;
|
||||
byte[] body;
|
||||
|
||||
public StoreMsg(String id, String topic, int queueid, byte[] body) {
|
||||
this.id = id;
|
||||
this.topic = topic;
|
||||
this.queueid = queueid;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.alibaba.middleware.race.mom.broker.function.Offset;
|
||||
|
||||
/*
|
||||
* by ChenQi
|
||||
* 订阅关系持久化
|
||||
* */
|
||||
public interface SubscribeStore {
|
||||
//读出所有的订阅关系
|
||||
public ArrayList<Offset> read();
|
||||
//存所有的订阅关系
|
||||
public boolean write(ArrayList<Offset> subInfoList);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.middleware.race.mom.broker.function.Offset;
|
||||
|
||||
public class SubscribeStoreImp implements SubscribeStore {
|
||||
|
||||
@Override
|
||||
public ArrayList<Offset> read() {
|
||||
// TODO Auto-generated method stub
|
||||
ArrayList<Offset> rs = new ArrayList<Offset>();
|
||||
RandomAccessFile subIndexFile = getSubFile(StoreConfig.subIndexFile, false);
|
||||
RandomAccessFile subDataFile = getSubFile(StoreConfig.subDataFile, false);
|
||||
try {
|
||||
long indexLength = subIndexFile.length();
|
||||
long index = 0;
|
||||
long dataPos = 0;
|
||||
int dataSize = 0;
|
||||
byte[] subByte;
|
||||
while (index < indexLength) {
|
||||
subIndexFile.seek(index);
|
||||
dataPos = subIndexFile.readLong();
|
||||
dataSize = subIndexFile.readInt();
|
||||
subDataFile.seek(dataPos);
|
||||
subByte = new byte[dataSize];
|
||||
subDataFile.read(subByte);
|
||||
rs.add(((Offset) JSON.parseObject(subByte, Offset.class)));
|
||||
index += 16;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return rs;
|
||||
}finally{
|
||||
try {
|
||||
subIndexFile.close();
|
||||
subDataFile.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean write(ArrayList<Offset> subInfoList) {
|
||||
// TODO Auto-generated method stub
|
||||
RandomAccessFile subIndexFile = getSubFile(StoreConfig.subIndexFile, true);
|
||||
RandomAccessFile subDataFile = getSubFile(StoreConfig.subDataFile, true);
|
||||
try {
|
||||
long dataPos = 0;
|
||||
long indexPos = 0;
|
||||
|
||||
// 每次重头写
|
||||
for (Offset sb : subInfoList) {
|
||||
indexPos = subIndexFile.length();
|
||||
dataPos = subDataFile.length();
|
||||
// 写入index
|
||||
subIndexFile.seek(indexPos);
|
||||
subIndexFile.writeLong(dataPos);
|
||||
byte[] sbByte = JSON.toJSONBytes(sb);
|
||||
subIndexFile.writeInt(sbByte.length);
|
||||
subIndexFile.writeInt(0);
|
||||
// 写入data
|
||||
subDataFile.seek(dataPos);
|
||||
subDataFile.write(sbByte);
|
||||
System.out.println("indexpos,datapos=" + subIndexFile.length() + "," + subDataFile.length());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}finally{
|
||||
try {
|
||||
subIndexFile.close();
|
||||
subDataFile.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public RandomAccessFile getSubFile(String fileName, boolean isWriteMethod) {
|
||||
String file;
|
||||
RandomAccessFile raf = null;
|
||||
file = StoreConfig.baseDir + StoreConfig.subscribeDir + fileName;
|
||||
System.out.println("file=" + file);
|
||||
File f = new File(file);
|
||||
if (isWriteMethod) {
|
||||
try {
|
||||
if (!f.exists()) {
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
f.createNewFile();
|
||||
} else {
|
||||
// 删除老文件
|
||||
f.delete();
|
||||
f.createNewFile();
|
||||
}
|
||||
raf = new RandomAccessFile(f, "rw");
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (!f.exists()) {
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
f.createNewFile();
|
||||
}
|
||||
raf = new RandomAccessFile(f, "rw");
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
return raf;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.middleware.race.mom.broker.function.Offset;
|
||||
|
||||
public class SubscribeStoreImp_MappedBuffer2 implements SubscribeStore {
|
||||
private static Logger logger = LoggerFactory
|
||||
.getLogger(SubscribeStoreImp_MappedBuffer2.class);
|
||||
private static String subscribeFilePath = StoreConfig.baseDir
|
||||
+ StoreConfig.subscribeDir;
|
||||
private static ConcurrentHashMap<String, FileStoreManager> fileStoreManagerMap = new ConcurrentHashMap<String, FileStoreManager>();
|
||||
|
||||
@Override
|
||||
public ArrayList<Offset> read() {
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap
|
||||
.get(subscribeFilePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(subscribeFilePath);
|
||||
// 为恢复
|
||||
fileStoreManagerMap.put(subscribeFilePath, fileStoreManager);
|
||||
}
|
||||
return (ArrayList<Offset>) fileStoreManager.getSubscribe();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean write(ArrayList<Offset> subInfoList) {
|
||||
// 对应的文件管理器
|
||||
FileStoreManager fileStoreManager = fileStoreManagerMap
|
||||
.get(subscribeFilePath);
|
||||
if (fileStoreManager == null) {
|
||||
fileStoreManager = new FileStoreManager(subscribeFilePath);
|
||||
// 为恢复
|
||||
fileStoreManagerMap.put(subscribeFilePath, fileStoreManager);
|
||||
}
|
||||
// 将写位置重置为16
|
||||
fileStoreManager.resetFilePosition();
|
||||
|
||||
ArrayList<byte[]> dataList = new ArrayList<byte[]>(subInfoList.size());
|
||||
int count = 1;
|
||||
for (Offset tmp : subInfoList) {
|
||||
logger.debug("保存订阅关系 i+== " + (count++) + " , " + tmp.getTopic());
|
||||
|
||||
dataList.add(JSON.toJSONBytes(tmp));
|
||||
}
|
||||
|
||||
//return fileStoreManager.saveMessage(dataList);
|
||||
return fileStoreManager.saveMessageByOnce(dataList);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* private void deleteOldSubscribeFile() { deleteFile(subscribeFilePath +
|
||||
* "/index/0", true); deleteFile(subscribeFilePath + "/data/0", true); }
|
||||
*
|
||||
* public void deleteFile(String file, boolean isWriteOverride) { File f =
|
||||
* new File(file); if (isWriteOverride) { try { if (f.exists()) { // 删除老文件
|
||||
* f.delete(); } } catch (Exception e) { // TODO Auto-generated catch block
|
||||
* logger.error(e.getMessage()); } } }
|
||||
*/
|
||||
}
|
||||
231
src/main/java/com/alibaba/middleware/race/mom/store/Test.java
Normal file
231
src/main/java/com/alibaba/middleware/race/mom/store/Test.java
Normal file
@@ -0,0 +1,231 @@
|
||||
//package com.alibaba.middleware.race.mom.store;
|
||||
//
|
||||
//import java.io.File;
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
//import java.util.Random;
|
||||
//import java.util.concurrent.BlockingQueue;
|
||||
//import java.util.concurrent.LinkedBlockingQueue;
|
||||
//
|
||||
//import com.alibaba.fastjson.JSON;
|
||||
//import com.alibaba.middleware.race.mom.Message;
|
||||
//import com.alibaba.middleware.race.mom.broker.function.Offset;
|
||||
//
|
||||
//public class Test {
|
||||
// String topic = "topic1", group = "group1", consumer = "group1";
|
||||
// int queue = 0;
|
||||
// BlockingQueue<Message> q = new LinkedBlockingQueue<>();
|
||||
// // 进度
|
||||
// int offset = 0, maxoffset = 9;
|
||||
//
|
||||
// static class Msg {
|
||||
// String a = "a";
|
||||
// int b = 3;
|
||||
// }
|
||||
//
|
||||
// public static void main(String args[]) {
|
||||
// Test t = new Test();
|
||||
// // 内存测试
|
||||
// // t.init();
|
||||
// // t.show();
|
||||
// // 存储恢复测试
|
||||
// // t.q.clear();
|
||||
// // t.init();
|
||||
// // t.store();
|
||||
// // t.recover();
|
||||
// // t.q.clear();
|
||||
//
|
||||
// // t.init();
|
||||
// /*
|
||||
// * t.store2_new(); t.recover2_new();
|
||||
// */
|
||||
// t.q.clear();
|
||||
// t.init();
|
||||
// t.store2byonce();
|
||||
// t.recover2();
|
||||
//
|
||||
// // t.testSub();
|
||||
// // t.testArrayList();
|
||||
// t.testFile2();
|
||||
// //t.testFile();
|
||||
// //t.scanFilePath(StoreConfig.baseDir+StoreConfig.subscribeDir);
|
||||
// }
|
||||
//
|
||||
// public void testFile2() {
|
||||
// mstore2.loadAndRecover();
|
||||
// }
|
||||
//
|
||||
// public void testFile() {
|
||||
// // 对正常消息
|
||||
// String filePath = StoreConfig.baseDir + StoreConfig.normalDir;
|
||||
// ArrayList<String[]> rsPath=this.scanFilePathForNormal(filePath);
|
||||
// for(String[] path:rsPath){
|
||||
// System.out.println();
|
||||
// }
|
||||
// filePath=StoreConfig.baseDir + StoreConfig.subscribeDir;
|
||||
// System.out.println(filePath);
|
||||
// String[] rs=scanFilePath(filePath);
|
||||
// }
|
||||
//
|
||||
// public String[] scanFilePath(String filePath) {
|
||||
// ArrayList<String> path = new ArrayList<String>();
|
||||
// String[] fileList = new File(filePath).list();
|
||||
// if (fileList == null)
|
||||
// {
|
||||
// System.out.println("null");
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// for (String t : fileList) {
|
||||
// System.out.println(t);
|
||||
// }
|
||||
// return fileList;
|
||||
// }
|
||||
// public ArrayList<String[]> scanFilePathForNormal(String filePath) {
|
||||
// ArrayList<String[]> path = new ArrayList<String[]>();
|
||||
// File[] fileList = new File(filePath).listFiles();
|
||||
// for (File tempFile : fileList) {
|
||||
// if (tempFile.isDirectory()) {
|
||||
// for (File temp : tempFile.listFiles()) {
|
||||
// String[] tq = new String[2];
|
||||
// String fs = System.getProperty("file.separator");
|
||||
// System.out.println(temp.toString() + " fs= " + fs);
|
||||
// String t = temp.toString();
|
||||
// int i = t.lastIndexOf(fs);
|
||||
// tq[1] = t.substring(i + 1);
|
||||
// int j = t.lastIndexOf(fs, i - 1);
|
||||
// tq[0] = t.substring(j + 1, i);
|
||||
// System.out.println(i + "-----" + j);
|
||||
// path.add(tq);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for (String[] t : path) {
|
||||
// System.out.println(t[0] + " / " + t[1]);
|
||||
// }
|
||||
// return path;
|
||||
// }
|
||||
//
|
||||
// public void show() {
|
||||
// // 显示
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// Message m = q.poll();
|
||||
// if (i < offset)
|
||||
// System.out.println("已消费:" + m.getMsgId());
|
||||
// else
|
||||
// System.out.println("未消费:" + m.getMsgId());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// MsgStore mstore2 = new MsgStoreImp_MappedBuffer2();
|
||||
//
|
||||
// public void store2() {
|
||||
//
|
||||
// List<byte[]> msgList = new ArrayList<byte[]>(10);
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// Message msg = q.poll();
|
||||
// byte[] msgbyte = JSON.toJSONBytes(msg);// 这里用fjson序列化一下
|
||||
// msgList.add(msgbyte);
|
||||
// }
|
||||
// long startTime = System.currentTimeMillis();
|
||||
// mstore2.writeByteNormal(topic, queue + "", msgList);
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println("写入msgList数据时间 endTime-StartTime="
|
||||
// + (endTime - startTime));
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void store2byonce() {
|
||||
//
|
||||
// List<byte[]> msgList = new ArrayList<byte[]>(10);
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// Message msg = q.poll();
|
||||
// byte[] msgbyte = JSON.toJSONBytes(msg);// 这里用fjson序列化一下
|
||||
// msgList.add(msgbyte);
|
||||
// }
|
||||
// long startTime = System.currentTimeMillis();
|
||||
// mstore2.writeByteNormalByOnce(topic, queue + "", msgList);
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println("写入msgList数据时间 endTime-StartTime="
|
||||
// + (endTime - startTime));
|
||||
// }
|
||||
//
|
||||
// public void recover2() {
|
||||
// long startTime = System.currentTimeMillis();
|
||||
// ArrayList<byte[]> blist = (ArrayList<byte[]>) mstore2.readByteNormal(
|
||||
// topic, queue + "", offset, maxoffset);
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println(" 读出数据时间 endTime-StartTime="
|
||||
// + (endTime - startTime));
|
||||
//
|
||||
// for (byte[] b : blist) {
|
||||
// Message msg = JSON.parseObject(b, Message.class);// obj 把 byte转为对象
|
||||
// System.out.println("未消费:" + msg.getMsgId());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void init() {
|
||||
// // queue: 头-> 0 1 2 3 4(offset) 5 6 7 8 9(maxoffset)
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// Message msg = new Message();
|
||||
// msg.setTopic(topic);
|
||||
// msg.setBornTime(System.currentTimeMillis());
|
||||
// msg.setBody(new String("测试哦" + new Random().nextLong()).getBytes());
|
||||
// System.out.println("init msg id " + msg.getMsgId());
|
||||
// q.add(msg);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void destory() {
|
||||
// mstore2.close();
|
||||
// }
|
||||
//
|
||||
// public void testSub() {
|
||||
// // Offset sb1 = new Offset();
|
||||
// // sb1.setTopicId("MOM-RACE-21486");
|
||||
// // sb1.setGroupId("CID_21486");
|
||||
// // sb1.setQueueId("0");
|
||||
// // sb1.setCurrentoffset(1);
|
||||
// // sb1.setCacheOffset(1);
|
||||
// // sb1.setMaxOffset(1);
|
||||
// // Offset sb2 = new Offset();
|
||||
// // sb2.setTopicId("ccc");
|
||||
// // sb2.setGroupId("dfadfasdlfasdfas");
|
||||
// // System.out.println("size(sb1):" + (JSON.toJSONBytes(sb1)).length);
|
||||
// // System.out.println("size(sb2):" + (JSON.toJSONBytes(sb2)).length);
|
||||
// SubscribeStoreImp_MappedBuffer2 ssi = new SubscribeStoreImp_MappedBuffer2();
|
||||
// ArrayList<Offset> ls = new ArrayList<Offset>();
|
||||
//
|
||||
// // ls.add(sb1);
|
||||
// // ls.add(sb2);
|
||||
// // ssi.write(ls);
|
||||
// System.out.println("开始读出订阅关系");
|
||||
// ls = ssi.read();
|
||||
// for (Offset sb : ls) {
|
||||
// System.out.println(sb.toString());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public void testArrayList() {
|
||||
// OffsetNum sb1 = new OffsetNum();
|
||||
// sb1.setI(1111);
|
||||
// OffsetNum sb2 = new OffsetNum();
|
||||
// sb2.setI(22222);
|
||||
// System.out.println("size(sb1):" + (JSON.toJSONBytes(sb1)).length);
|
||||
// System.out.println("size(sb2):" + (JSON.toJSONBytes(sb2)).length);
|
||||
// ArrayStoreImp ssi = new ArrayStoreImp();
|
||||
// ArrayList<OffsetNum> ls = new ArrayList<OffsetNum>();
|
||||
//
|
||||
// ls.add(sb1);
|
||||
// ls.add(sb2);
|
||||
// ssi.store(ls, "topicAndFilter", "0");
|
||||
// System.out.println("开始读出订阅关系");
|
||||
// ls = ssi.recover("topicAndFilter", "0");
|
||||
// for (OffsetNum sb : ls) {
|
||||
// System.out.println(sb.i);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.alibaba.middleware.race.mom.store;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel.MapMode;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class redotest {
|
||||
String redoFile = System.getProperty("user.home") + File.separator
|
||||
+ "store";
|
||||
public MappedByteBuffer mbb;
|
||||
public File file;
|
||||
public AtomicInteger fileName = new AtomicInteger(0);
|
||||
public int filesize = 1024*1024;
|
||||
BlockingQueue<MappedByteBuffer> mmbStoreQue = new ArrayBlockingQueue<MappedByteBuffer>(
|
||||
10);
|
||||
|
||||
BlockingQueue<MappedByteBuffer> mmbRecoverQue = new ArrayBlockingQueue<MappedByteBuffer>(
|
||||
10);
|
||||
|
||||
public void writeLog2(ByteBuffer smByte) {
|
||||
|
||||
if (mbb.position() + smByte.limit() > filesize) {
|
||||
try {
|
||||
mmbRecoverQue.put(this.mbb);
|
||||
this.mbb = this.mmbStoreQue.poll(3, TimeUnit.MILLISECONDS);
|
||||
if (this.mbb == null) {
|
||||
this.createMbb();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// ByteBuffer by = ByteBuffer.allocate(smByte.length);
|
||||
mbb.putInt(smByte.limit());
|
||||
mbb.put(smByte);
|
||||
}
|
||||
|
||||
public void readRecoverLog2() {
|
||||
|
||||
MappedByteBuffer readMbb = null;
|
||||
while (mmbRecoverQue.size() != 0) {
|
||||
try {
|
||||
readMbb = this.mmbRecoverQue.take();
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 恢复数据
|
||||
int size = readMbb.getInt();
|
||||
while (size != 0) {
|
||||
byte[] msg = new byte[size];
|
||||
readMbb.get(msg);
|
||||
{
|
||||
// 开始写indexdata文件
|
||||
}
|
||||
size = readMbb.getInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void flushLog() {
|
||||
mbb.force();
|
||||
}
|
||||
|
||||
public redotest() {
|
||||
this.createMbb();
|
||||
}
|
||||
|
||||
private void createMbb() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
try {
|
||||
mmbStoreQue.put(getFileMappedByteBuffer(redoFile
|
||||
+ File.separator + fileName.getAndIncrement(), 0,
|
||||
filesize));
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.mbb = mmbStoreQue.take();
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private MappedByteBuffer getFileMappedByteBuffer(String filePathAndName,
|
||||
int startPos, int fileSize) {
|
||||
File f = new File(filePathAndName);
|
||||
try {
|
||||
if (!f.exists()) {
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
f.createNewFile();
|
||||
}
|
||||
return mbb = new RandomAccessFile(f, "rw").getChannel().map(
|
||||
MapMode.READ_WRITE, startPos, fileSize);
|
||||
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* public MappedByteBuffer getNewFile() throws FileNotFoundException,
|
||||
* IOException { String newfilename = new String("" +
|
||||
* (Long.parseLong(file.getName()) + filesize)); file = new
|
||||
* File(System.getProperty("user.home") + File.separator + "store" +
|
||||
* File.separator + newfilename); mbb = new RandomAccessFile(file,
|
||||
* "rw").getChannel().map( FileChannel.MapMode.READ_WRITE, 0, filesize);
|
||||
* return mbb; }
|
||||
*/
|
||||
public static void main(String args[]) throws IOException {
|
||||
final redotest redo = new redotest();
|
||||
char FLAG=0;
|
||||
|
||||
for(int j=0;j<500;j++)
|
||||
{
|
||||
final long begin = System.currentTimeMillis();
|
||||
for (int i = 0; i < 200; i++) {
|
||||
String data="topic"+i+FLAG+"fitler"+FLAG+"hello mom Im baby";
|
||||
ByteBuffer smByte=ByteBuffer.allocate(64);
|
||||
smByte.put(data.getBytes());
|
||||
redo.writeLog2(smByte);
|
||||
}
|
||||
long flushtime=System.currentTimeMillis();
|
||||
redo.flushLog();
|
||||
System.out.println("writetime:"+(flushtime-begin)+" flushtime:"
|
||||
+ (System.currentTimeMillis() - flushtime));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.alibaba.middleware.race.mom.util;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
public class ConsumerInfo {
|
||||
private Channel channel;
|
||||
private SocketAddress consumerAddress;
|
||||
|
||||
|
||||
public ConsumerInfo(Channel channel){
|
||||
this.channel=channel;
|
||||
this.consumerAddress=channel.remoteAddress();
|
||||
}
|
||||
public Channel channel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public SocketAddress consumerAddress() {
|
||||
return consumerAddress;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(this==obj){
|
||||
return true;
|
||||
}else if(obj instanceof ConsumerInfo){
|
||||
ConsumerInfo another=(ConsumerInfo)obj;
|
||||
return this.consumerAddress.equals(another.consumerAddress);
|
||||
}else return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
37
src/main/java/com/alibaba/middleware/race/mom/util/Info.java
Normal file
37
src/main/java/com/alibaba/middleware/race/mom/util/Info.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.alibaba.middleware.race.mom.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/*
|
||||
* 封装所有的请求和响应信息
|
||||
* type 请求或者响应类型
|
||||
* body 对应的数据
|
||||
* describle 类型描述信息
|
||||
* Author ilyy510
|
||||
* */
|
||||
public class Info implements Serializable{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -2529148421210651297L;
|
||||
InfoType type;
|
||||
byte[] body;
|
||||
public InfoType getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(InfoType type) {
|
||||
this.type = type;
|
||||
}
|
||||
public byte[] getBody() {
|
||||
return body;
|
||||
}
|
||||
public void setBody(byte[] body) {
|
||||
this.body = body;
|
||||
}
|
||||
public static long getSerialversionuid() {
|
||||
return serialVersionUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.alibaba.middleware.race.mom.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
public class InfoBodyConsumer implements Serializable{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 4500208867228323993L;
|
||||
private String groupId;
|
||||
private String topic;
|
||||
private Map<String,String> filterMap;
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
public Map<String, String> getFilterMap() {
|
||||
return filterMap;
|
||||
}
|
||||
public void setFilterMap(Map<String, String> filterMap) {
|
||||
this.filterMap = filterMap;
|
||||
}
|
||||
public String toString(){
|
||||
return "group:"+this.groupId+" topic:"+this.topic+" filter:"+this.filterMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.alibaba.middleware.race.mom.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.alibaba.middleware.race.mom.broker.function.MessageInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Administrator
|
||||
* 封装producer的请求信息
|
||||
*/
|
||||
public class InfoBodyProduceList implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 9075042672715185764L;
|
||||
ArrayList<MessageInfo> msglist=new ArrayList<>();
|
||||
public ArrayList<MessageInfo> getMsglist() {
|
||||
return msglist;
|
||||
}
|
||||
public void setMsglist(ArrayList<MessageInfo> msglist) {
|
||||
this.msglist = msglist;
|
||||
}
|
||||
public InfoBodyProduceList() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.alibaba.middleware.race.mom.util;
|
||||
/**
|
||||
* Info消息的类型说明
|
||||
* Author ilyy510
|
||||
* */
|
||||
public enum InfoType {
|
||||
PRODUCER_TO_BROKER_SENG_MESSAGE,
|
||||
BROKER_TO_PRODUCER_CONFIRM_MESSAGE,
|
||||
BROKER_TO_CONSUMER_PUSH_MESSAGE,
|
||||
CONSUMER_TO_BROKER_CONFIRM_MESSAGE,
|
||||
CONSUMER_TO_BROKER_CONNECT
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.alibaba.middleware.race.mom.util;
|
||||
|
||||
public class InfoUtil {
|
||||
public static byte[] arraycat(byte[] buf1,byte[] buf2)
|
||||
{
|
||||
byte[] bufret=null;
|
||||
int len1=0;
|
||||
int len2=0;
|
||||
if(buf1!=null)
|
||||
len1=buf1.length;
|
||||
if(buf2!=null)
|
||||
len2=buf2.length;
|
||||
if(len1+len2>0)
|
||||
bufret=new byte[len1+len2];
|
||||
if(len1>0)
|
||||
System.arraycopy(buf1,0,bufret,0,len1);
|
||||
if(len2>0)
|
||||
System.arraycopy(buf2,0,bufret,len1,len2);
|
||||
return bufret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
package com.alibaba.middleware.race.mom.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* topic and filter的封装,一种TopicAndFilter代表一种消息,来自同一生产者集群组
|
||||
* 订阅者集群组 订阅某一TopicAndFilter(注意,订阅者filter为null,则订阅topic下所有filter消息)
|
||||
* 使用注意:构建订阅者TopicAndFilter须根据订阅者信息; 构建生产者或消息的TopicAndFilter须根据生产者的信息(系统大量使用TopicAndFilter为map键,构建绝对不能出错)
|
||||
* @author youngforever
|
||||
*
|
||||
*/
|
||||
public class TopicAndFilter implements Serializable{
|
||||
private static final long serialVersionUID = -7983983900930009841L;
|
||||
private String topic;
|
||||
private Map<String,String> filter;
|
||||
|
||||
public TopicAndFilter(){};
|
||||
|
||||
public TopicAndFilter(String topic, Map<String,String> filter) {
|
||||
if (topic == null) {
|
||||
throw new IllegalArgumentException("Left value is not effective.");
|
||||
}
|
||||
//允许filter为null
|
||||
// if (filter == null) {
|
||||
// throw new IllegalArgumentException("Right value is not effective.");
|
||||
// }
|
||||
this.topic = topic;
|
||||
this.filter = filter;
|
||||
}
|
||||
// public TopicAndFilter(String topicAndFilter) {
|
||||
//
|
||||
// }
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public Map<String,String> getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((topic == null) ? 0 : topic.hashCode());
|
||||
result = prime * result + ((filter == null) ? 0 : filter.hashCode());
|
||||
return result;
|
||||
}
|
||||
// public int hashCode() {
|
||||
// if(right!=null){
|
||||
// return (left.toString()+right.toString()).hashCode();
|
||||
// }else return left.toString().hashCode();
|
||||
// }
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (!(obj instanceof TopicAndFilter))
|
||||
return false;
|
||||
TopicAndFilter other = (TopicAndFilter) obj;
|
||||
if (topic == null) {
|
||||
if (other.topic != null)
|
||||
return false;
|
||||
} else if (!topic.equals(other.topic))
|
||||
return false;
|
||||
if (filter == null) {
|
||||
if (other.filter != null)
|
||||
return false;
|
||||
} else if (!filter.equals(other.filter))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if(filter!=null)
|
||||
return topic + filter.toString() ;
|
||||
else return topic +"null";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
package com.alibaba.middleware.race.mom.util;
|
||||
|
||||
public class Triple<L, M, R> {
|
||||
|
||||
private final L left;
|
||||
private final M middle;
|
||||
private final R right;
|
||||
|
||||
public Triple(L left, M middle, R right) {
|
||||
if (left == null) {
|
||||
throw new IllegalArgumentException("Left value is not effective.");
|
||||
}
|
||||
if (middle == null) {
|
||||
throw new IllegalArgumentException("Middle value is not effective.");
|
||||
}
|
||||
if (right == null) {
|
||||
throw new IllegalArgumentException("Right value is not effective.");
|
||||
}
|
||||
this.left = left;
|
||||
this.middle = middle;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
public L getLeft() {
|
||||
return this.left;
|
||||
}
|
||||
|
||||
public M getMiddle() {
|
||||
return this.middle;
|
||||
}
|
||||
|
||||
public R getRight() {
|
||||
return this.right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((left == null) ? 0 : left.hashCode());
|
||||
result = prime * result + ((middle == null) ? 0 : middle.hashCode());
|
||||
result = prime * result + ((right == null) ? 0 : right.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Triple<Object, Object, Object> other = (Triple<Object, Object, Object>) obj;
|
||||
if (left == null) {
|
||||
if (other.left != null)
|
||||
return false;
|
||||
} else if (!left.equals(other.left))
|
||||
return false;
|
||||
if (middle == null) {
|
||||
if (other.middle != null)
|
||||
return false;
|
||||
} else if (!middle.equals(other.middle))
|
||||
return false;
|
||||
if (right == null) {
|
||||
if (other.right != null)
|
||||
return false;
|
||||
} else if (!right.equals(other.right))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<" + left + "," + middle + "," + right + ">";
|
||||
}
|
||||
|
||||
}
|
||||
93
src/main/java/log4j.properties
Normal file
93
src/main/java/log4j.properties
Normal file
@@ -0,0 +1,93 @@
|
||||
log4j.rootCategory=error,stdout
|
||||
log4j.addivity.org.apache=true
|
||||
|
||||
#stout 控制台打印
|
||||
log4j.appender.stdout.Encoding=UTF-8
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
#log4j.appender.stdout.layout.ConversionPattern=[QC] %-d{yyyy-MM-dd HH:mm:ss} %p [%t] %C.%M(%L) | %m%n
|
||||
log4j.appender.stdout.layout.ConversionPattern= %p %C.%M(%L) | %m%n
|
||||
|
||||
#自定义打印到文件
|
||||
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.R.File=mom.log
|
||||
log4j.appender.R.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
|
||||
|
||||
## Disable other log 不打印的包日记
|
||||
log4j.logger.io.netty=OFF
|
||||
log4j.logger.org.springframework=OFF
|
||||
log4j.logger.org.apache.struts2=OFF
|
||||
log4j.logger.com.opensymphony.xwork2=OFF
|
||||
log4j.logger.com.ibatis=OFF
|
||||
log4j.logger.org.hibernate=OFF
|
||||
|
||||
|
||||
#暂时没有使用下面的logger
|
||||
# 应用于控制台
|
||||
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.CONSOLE.Threshold=INFO
|
||||
log4j.appender.CONSOLE.Target=System.out
|
||||
log4j.appender.CONSOLE.Encoding=GBK
|
||||
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
|
||||
|
||||
# 每天新建日志
|
||||
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.A1.File=C\:/log4j/log
|
||||
log4j.appender.A1.Encoding=GBK
|
||||
log4j.appender.A1.Threshold=DEBUG
|
||||
log4j.appender.A1.DatePattern='.'yyyy-MM-dd
|
||||
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.A1.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}\:%L \: %m%n
|
||||
|
||||
#应用于文件
|
||||
log4j.appender.FILE=org.apache.log4j.FileAppender
|
||||
log4j.appender.FILE.File=C\:/log4j/file.log
|
||||
log4j.appender.FILE.Append=false
|
||||
log4j.appender.FILE.Encoding=GBK
|
||||
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
|
||||
|
||||
# 应用于文件回滚
|
||||
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.ROLLING_FILE.Threshold=ERROR
|
||||
log4j.appender.ROLLING_FILE.File=rolling.log
|
||||
log4j.appender.ROLLING_FILE.Append=true
|
||||
log4j.appender.CONSOLE_FILE.Encoding=GBK
|
||||
log4j.appender.ROLLING_FILE.MaxFileSize=10KB
|
||||
log4j.appender.ROLLING_FILE.MaxBackupIndex=1
|
||||
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
|
||||
|
||||
#自定义Appender
|
||||
log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender
|
||||
log4j.appender.im.host = mail.cybercorlin.net
|
||||
log4j.appender.im.username = username
|
||||
log4j.appender.im.password = password
|
||||
log4j.appender.im.recipient = yyflyons@163.com
|
||||
log4j.appender.im.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
|
||||
|
||||
#应用于socket
|
||||
log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.SOCKET.RemoteHost=localhost
|
||||
log4j.appender.SOCKET.Port=5001
|
||||
log4j.appender.SOCKET.LocationInfo=true
|
||||
# Set up for Log Facter 5
|
||||
log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n
|
||||
# Log Factor 5 Appender
|
||||
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
|
||||
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000
|
||||
|
||||
# 发送日志给邮件
|
||||
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
|
||||
log4j.appender.MAIL.Threshold=FATAL
|
||||
log4j.appender.MAIL.BufferSize=10
|
||||
log4j.appender.MAIL.From=yyflyons@163.com
|
||||
log4j.appender.MAIL.SMTPHost=www.wusetu.com
|
||||
log4j.appender.MAIL.Subject=Log4J Message
|
||||
log4j.appender.MAIL.To=yyflyons@126.com
|
||||
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
|
||||
40
src/test/java/com/alibaba/middleware/race/mom/AppTest.java
Normal file
40
src/test/java/com/alibaba/middleware/race/mom/AppTest.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest {
|
||||
private static ExecutorService executorService=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
public static void main(String args[]) throws InterruptedException{
|
||||
long start=System.currentTimeMillis();
|
||||
for (int i = 0; i <Runtime.getRuntime().availableProcessors(); i++) {
|
||||
executorService.execute(new Runnable() {
|
||||
int i=0;
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
System.out.println(i++);
|
||||
Thread.sleep(3000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Thread.sleep(3000);
|
||||
if (true) {
|
||||
System.out.println("over");
|
||||
return ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
104
src/test/java/com/alibaba/middleware/race/mom/fstjson.java
Normal file
104
src/test/java/com/alibaba/middleware/race/mom/fstjson.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
class Group {
|
||||
long id;
|
||||
String name;
|
||||
HashMap<String, String> properties = new HashMap<String, String>();
|
||||
public String getProperty(String key) {
|
||||
return properties.get(key);
|
||||
}
|
||||
/**
|
||||
* 设置消息属性
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setProperty(String key, String value) {
|
||||
properties.put(key, value);
|
||||
}
|
||||
/**
|
||||
* 删除消息属性
|
||||
* @param key
|
||||
*/
|
||||
public void removeProperty(String key) {
|
||||
properties.remove(key);
|
||||
}
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
List<User> user = new ArrayList<User>();
|
||||
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<User> getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(List<User> user) {
|
||||
this.user = user;
|
||||
}
|
||||
}
|
||||
|
||||
class User {
|
||||
long id;
|
||||
String name;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public class fstjson {
|
||||
public static void main(String[] args) {
|
||||
Group group = new Group();
|
||||
group.setId(0L);
|
||||
group.setName("admin");
|
||||
group.setProperty("1", "1");
|
||||
User guestUser = new User();
|
||||
guestUser.setId(2L);
|
||||
guestUser.setName("guest");
|
||||
|
||||
User rootUser = new User();
|
||||
rootUser.setId(3L);
|
||||
rootUser.setName("root");
|
||||
|
||||
group.getUser().add(guestUser);
|
||||
group.getUser().add(rootUser);
|
||||
String jsonString = JSON.toJSONString(group);
|
||||
System.out.println(jsonString);
|
||||
}
|
||||
}
|
||||
47
src/test/java/com/alibaba/middleware/race/mom/test.java
Normal file
47
src/test/java/com/alibaba/middleware/race/mom/test.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package com.alibaba.middleware.race.mom;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class test extends Thread{
|
||||
public static BlockingQueue<String> queue=new LinkedBlockingQueue<String>(3);
|
||||
private int index;
|
||||
public test(int i){
|
||||
this.index=i;
|
||||
}
|
||||
|
||||
public void run(){
|
||||
try{
|
||||
queue.put(String.valueOf(this.index));
|
||||
System.out.println("put {"+this.index+"} into queue!");
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]){
|
||||
ExecutorService service=Executors.newCachedThreadPool();
|
||||
for( int i=0; i<10; i++){
|
||||
service.submit(new test(i));
|
||||
}
|
||||
Thread thread = new Thread(){
|
||||
public void run(){
|
||||
try{
|
||||
while(true){
|
||||
Thread.sleep((int)(Math.random()*1000));
|
||||
// if(test.queue.isEmpty()) break;
|
||||
String str=test.queue.take();
|
||||
System.out.println("take {" + str+"} out of queue!");
|
||||
}
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
service.submit(thread);
|
||||
service.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
30
target/classes/assembly.xml
Normal file
30
target/classes/assembly.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<assembly>
|
||||
<id>bin</id>
|
||||
<baseDirectory>middleware-mom</baseDirectory>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<formats>
|
||||
<format>dir</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
<include>LICENSE*</include>
|
||||
<include>NOTICE*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/lib</directory>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/lib</outputDirectory>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<scope>runtime</scope>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$1$1.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$1$1.class
Normal file
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$1.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$1.class
Normal file
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$2.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$2.class
Normal file
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$3.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$3.class
Normal file
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$4.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Broker$4.class
Normal file
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/Broker.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Broker.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/Consumer.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Consumer.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/Message.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Message.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/Producer.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/Producer.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/SendResult.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/SendResult.class
Normal file
Binary file not shown.
BIN
target/classes/com/alibaba/middleware/race/mom/SendStatus.class
Normal file
BIN
target/classes/com/alibaba/middleware/race/mom/SendStatus.class
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user