请教一个关于Rocketmq报错DESC: service not available now, maybe disk full

白白了白呀白 2019-04-19 11:51:40
按教程都在windows上下载好rocketmq,环境变量也配置了,复制官网的生产者消费者的demo,生产者demo是同步的,main方法中先启动消费者,然后启动生产者,他这个报错好像会在生产者运行之后某个时间范围内才报错,比如十几秒
Exception in thread "main" org.apache.rocketmq.client.exception.MQClientException: Send [3] times, still failed, cost [475]ms, Topic: TopicTest, BrokersSent: [MS-20160803HVCK, MS-20160803HVCK, MS-20160803HVCK]
See http://rocketmq.apache.org/docs/faq/ for further details.
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:586)
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1223)
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1173)
at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:214)
at com.example.demo.test2.SyncProducer.main(SyncProducer.java:29)
Caused by: org.apache.rocketmq.client.exception.MQBrokerException: CODE: 14 DESC: service not available now, maybe disk full, CL: 0.97 CQ: 0.97 INDEX: 0.97, maybe your broker machine memory too small.
For more information, please visit the url, http://rocketmq.apache.org/docs/faq/
at org.apache.rocketmq.client.impl.MQClientAPIImpl.processSendResponse(MQClientAPIImpl.java:556)
at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessageSync(MQClientAPIImpl.java:358)
at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:340)
at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:294)
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendKernelImpl(DefaultMQProducerImpl.java:761)
at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:505)
... 4 more

网上说磁盘不足可是我还有20多G,也有资料说broker启动方式不对,需要在后面加autoxxxxxx来着我也加了,还是一样的问题。每次好像启动生产者和消费者只能在某个小段时间范围内可以生产消费成功,之后就报错,我测试了一下rocketmq的nameserver和broker启动的20秒内我请求几次都可以成功,之后就报这个错,如果这20秒内不运行生产者的main方法,第21秒运行的时候就马上报错了
...全文
5227 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
qybao 2019-04-23
  • 打赏
  • 举报
回复
flagBits 初始值就是0

在DefaultMessageStore的isSpaceToDelete(判断硬盘是否该清理)方法里,会调用RunningFlags的getAndMakeDiskFull或getAndMakeDiskOK方法来给flagBits赋值
所以你直接改this.runningFlags.isWriteable()返回true,这些判断硬盘空间的处理就没意义了
所以还是修改磁盘的占用比例比较好

上面也说了,你启动broker的时候可以加-c your_properties.properties的方式来修改个别的property,这些property按源码的逻辑有则赋值没有则忽略(调用相应config的setXXX方法),所以你定义一个properties文件,里面设定diskMaxUsedSpaceRatio=95,然后启动broker的时候用-c 指定你的properties文件,相应的property就应该能覆盖了
白白了白呀白 2019-04-23
  • 打赏
  • 举报
回复
引用 14 楼 LCL_data 的回复:
调试源码把.isWriteable()的返回值都设为true后 rocketmq生产消费多少次都没问题了,在多少秒内消费也都不会报错了 ---> 这样相当于你把他的判断条件给屏蔽了。 flagBits 找下他使用的地方,就知道在哪里设置成16了
就是没找到他是在哪个地方值被改变的,找了好半天也没找到,我再找找看
十八道胡同 2019-04-23
  • 打赏
  • 举报
回复
调试源码把.isWriteable()的返回值都设为true后 rocketmq生产消费多少次都没问题了,在多少秒内消费也都不会报错了


--->
这样相当于你把他的判断条件给屏蔽了。

flagBits 找下他使用的地方,就知道在哪里设置成16了


白白了白呀白 2019-04-23
  • 打赏
  • 举报
回复
谢谢!我一会试下。 我自己去github上forkrocketmq源码去调试,根据之前启动的报错信息,是找到了报错的位置,不过里面逻辑有的不太理解。 我报错的核心代码应该是在DefaultMessageStore.putMessage(MessageExtBrokerInner msg)方法中的一个逻辑里他的代码是这样

if (!this.runningFlags.isWriteable()) {
            long value = this.printTimes.getAndIncrement();
            if ((value % 50000) == 0) {
                log.warn("message store is not writeable, so putMessage is forbidden " + this.runningFlags.getFlagBits());
            }

            return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);
        } else {
            this.printTimes.set(0);
        }
runningFlags是RunningFlags.java类中的一个属性初始值是0,isWriteable()是把这个flags做按位与按位或运算,判断结果是否为0,为0返回true,非0返回false 判断如下

public boolean isWriteable() {
        if ((this.flagBits & (NOT_WRITEABLE_BIT | WRITE_LOGICS_QUEUE_ERROR_BIT | DISK_FULL_BIT | WRITE_INDEX_FILE_ERROR_BIT)) == 0) {
            return true;
        }

        return false;
    }
这块判断的逻辑我不是很懂,是判断磁盘是否不足?因为在上面第一段的代码中!this.runningFlags.isWriteable()成立的话会执行 return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);,而等于SERVICE_NOT_AVAILABLE的话在外面的方法中会给response的code值复制为14,然后给remarks赋值报错的磁盘不足的信息。我对flagBits 这个字段作用不是很理解,比如第一次进入它的值是0,下一次进入值就变为16了,然后就返回false接着逻辑就往报错的方向走了,找了半天也没找着这个flagBits 的值是什么时候变化的,是怎么变化的。我调试源码把.isWriteable()的返回值都设为true后 rocketmq生产消费多少次都没问题了,在多少秒内消费也都不会报错了
引用 12 楼 qybao 的回复:
看了一下源码,貌似可以在mqbroker.cmd 后加-c 指定property文件 默认还是75%,可以通过指定property修改 MessageStoreConfig源码
@ImportantField
    private String deleteWhen = "04";
    private int diskMaxUsedSpaceRatio = 75;
BrokerStarup源码
if (commandLine.hasOption('c')) {
                String file = commandLine.getOptionValue('c');
                if (file != null) {
                    configFile = file;
                    InputStream in = new BufferedInputStream(new FileInputStream(file));
                    properties = new Properties();
                    properties.load(in);

                    properties2SystemEnv(properties);
                    MixAll.properties2Object(properties, brokerConfig);
                    MixAll.properties2Object(properties, nettyServerConfig);
                    MixAll.properties2Object(properties, nettyClientConfig);
                    MixAll.properties2Object(properties, messageStoreConfig);

                    BrokerPathConfigHelper.setBrokerConfigPath(file);
                    in.close();
                }
            }
MixAll源码
public static void properties2Object(final Properties p, final Object object) {
        Method[] methods = object.getClass().getMethods();
        for (Method method : methods) {
            String mn = method.getName();
            if (mn.startsWith("set")) {
                try {
                    String tmp = mn.substring(4);
                    String first = mn.substring(3, 4);

                    String key = first.toLowerCase() + tmp;
                    String property = p.getProperty(key);
                    if (property != null) {
                        Class<?>[] pt = method.getParameterTypes();
                        if (pt != null && pt.length > 0) {
                            String cn = pt[0].getSimpleName();
                            Object arg = null;
                            if (cn.equals("int") || cn.equals("Integer")) {
                                arg = Integer.parseInt(property);
                            } else if (cn.equals("long") || cn.equals("Long")) {
                                arg = Long.parseLong(property);
                            } else if (cn.equals("double") || cn.equals("Double")) {
                                arg = Double.parseDouble(property);
                            } else if (cn.equals("boolean") || cn.equals("Boolean")) {
                                arg = Boolean.parseBoolean(property);
                            } else if (cn.equals("float") || cn.equals("Float")) {
                                arg = Float.parseFloat(property);
                            } else if (cn.equals("String")) {
                                arg = property;
                            } else {
                                continue;
                            }
                            method.invoke(object, arg);
                        }
                    }
                } catch (Throwable ignored) {
                }
            }
        }
白白了白呀白 2019-04-23
  • 打赏
  • 举报
回复
引用 19 楼 qybao 的回复:
查了一下,ConsumeQueue的putMessagePositionInfoWrapper会调用makeLogicsQueueError 会不会是ConsumeQueue的配置导致的问题?debug跟踪一下吧
谢谢 我再研究下,水平有限看源码有点吃力
qybao 2019-04-23
  • 打赏
  • 举报
回复
查了一下,ConsumeQueue的putMessagePositionInfoWrapper会调用makeLogicsQueueError
会不会是ConsumeQueue的配置导致的问题?debug跟踪一下吧
qybao 2019-04-23
  • 打赏
  • 举报
回复
怀疑不是磁盘空间不足,出了磁盘空间,以下也会造成isWriteable()返回false
查找一下哪里调用了makeLogicsQueueError或makeIndexFileError或getAndMakeNotWriteable方法
白白了白呀白 2019-04-23
  • 打赏
  • 举报
回复
引用 16 楼 qybao 的回复:
flagBits 初始值就是0 在DefaultMessageStore的isSpaceToDelete(判断硬盘是否该清理)方法里,会调用RunningFlags的getAndMakeDiskFull或getAndMakeDiskOK方法来给flagBits赋值 所以你直接改this.runningFlags.isWriteable()返回true,这些判断硬盘空间的处理就没意义了 所以还是修改磁盘的占用比例比较好 上面也说了,你启动broker的时候可以加-c your_properties.properties的方式来修改个别的property,这些property按源码的逻辑有则赋值没有则忽略(调用相应config的setXXX方法),所以你定义一个properties文件,里面设定diskMaxUsedSpaceRatio=95,然后启动broker的时候用-c 指定你的properties文件,相应的property就应该能覆盖了
我broker启动是调用conf文件下2m-2s-sync/里的-a的属性文件 ,里面是有设置了diskMaxUsedSpaceRatio=95不过还是没效果,过了20秒左右后生产消费就报磁盘不足了
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=SYNC_MASTER
flushDiskType=ASYNC_FLUSH
diskMaxUsedSpaceRatio=95
启动指令是
mqbroker.cmd -n localhost:9876 autoCreateTopicEnable=true -c ../conf/2m-2s-sync/broker-a.properties
qybao 2019-04-22
  • 打赏
  • 举报
回复
看了一下源码,貌似可以在mqbroker.cmd 后加-c 指定property文件
默认还是75%,可以通过指定property修改
MessageStoreConfig源码
@ImportantField
private String deleteWhen = "04";
private int diskMaxUsedSpaceRatio = 75;


BrokerStarup源码
if (commandLine.hasOption('c')) {
String file = commandLine.getOptionValue('c');
if (file != null) {
configFile = file;
InputStream in = new BufferedInputStream(new FileInputStream(file));
properties = new Properties();
properties.load(in);

properties2SystemEnv(properties);
MixAll.properties2Object(properties, brokerConfig);
MixAll.properties2Object(properties, nettyServerConfig);
MixAll.properties2Object(properties, nettyClientConfig);
MixAll.properties2Object(properties, messageStoreConfig);

BrokerPathConfigHelper.setBrokerConfigPath(file);
in.close();
}
}


MixAll源码
public static void properties2Object(final Properties p, final Object object) {
Method[] methods = object.getClass().getMethods();
for (Method method : methods) {
String mn = method.getName();
if (mn.startsWith("set")) {
try {
String tmp = mn.substring(4);
String first = mn.substring(3, 4);

String key = first.toLowerCase() + tmp;
String property = p.getProperty(key);
if (property != null) {
Class<?>[] pt = method.getParameterTypes();
if (pt != null && pt.length > 0) {
String cn = pt[0].getSimpleName();
Object arg = null;
if (cn.equals("int") || cn.equals("Integer")) {
arg = Integer.parseInt(property);
} else if (cn.equals("long") || cn.equals("Long")) {
arg = Long.parseLong(property);
} else if (cn.equals("double") || cn.equals("Double")) {
arg = Double.parseDouble(property);
} else if (cn.equals("boolean") || cn.equals("Boolean")) {
arg = Boolean.parseBoolean(property);
} else if (cn.equals("float") || cn.equals("Float")) {
arg = Float.parseFloat(property);
} else if (cn.equals("String")) {
arg = property;
} else {
continue;
}
method.invoke(object, arg);
}
}
} catch (Throwable ignored) {
}
}
}


白白了白呀白 2019-04-20
  • 打赏
  • 举报
回复
引用 6 楼 qybao 的回复:
broker-x.properties //x为a,b...
给p文件该了好像没啥效果,还是超过那个时间生产者生产就报错
白白了白呀白 2019-04-20
  • 打赏
  • 举报
回复
引用 8 楼 qybao 的回复:
看了一下源码,磁盘应该是你存储message的磁盘
private boolean isSpaceToDelete() {
            double ratio = DefaultMessageStore.this.getMessageStoreConfig().getDiskMaxUsedSpaceRatio() / 100.0;

            cleanImmediately = false;

            {
                String storePathPhysic = DefaultMessageStore.this.getMessageStoreConfig().getStorePathCommitLog();
                double physicRatio = UtilAll.getDiskPartitionSpaceUsedPercent(storePathPhysic);
                if (physicRatio > diskSpaceWarningLevelRatio) {
                    boolean diskok = DefaultMessageStore.this.runningFlags.getAndMakeDiskFull();
                    if (diskok) {
                        DefaultMessageStore.log.error("physic disk maybe full soon " + physicRatio + ", so mark disk full");
                    }

                    cleanImmediately = true;
                } else if (physicRatio > diskSpaceCleanForciblyRatio) {
                    cleanImmediately = true;
                } else {
                    boolean diskok = DefaultMessageStore.this.runningFlags.getAndMakeDiskOK();
                    if (!diskok) {
                        DefaultMessageStore.log.info("physic disk space OK " + physicRatio + ", so mark disk ok");
                    }
                }

                if (physicRatio < 0 || physicRatio > ratio) {
                    DefaultMessageStore.log.info("physic disk maybe full soon, so reclaim space, " + physicRatio);
                    return true;
                }
            }
 public int getDiskMaxUsedSpaceRatio() {
        if (this.diskMaxUsedSpaceRatio < 10)
            return 10;

        if (this.diskMaxUsedSpaceRatio > 95)
            return 95;

        return diskMaxUsedSpaceRatio;
    }
看最新代码好像是默认85%
class CleanCommitLogService {

        private final static int MAX_MANUAL_DELETE_FILE_TIMES = 20;
        private final double diskSpaceWarningLevelRatio =
            Double.parseDouble(System.getProperty("rocketmq.broker.diskSpaceWarningLevelRatio", "0.90"));

        private final double diskSpaceCleanForciblyRatio =
            Double.parseDouble(System.getProperty("rocketmq.broker.diskSpaceCleanForciblyRatio", "0.85"));
        private long lastRedeleteTimestamp = 0;

        private volatile int manualDeleteFileSeveralTimes = 0;

        private volatile boolean cleanImmediately = false;

        public void excuteDeleteFilesManualy() {
            this.manualDeleteFileSeveralTimes = MAX_MANUAL_DELETE_FILE_TIMES;
            DefaultMessageStore.log.info("executeDeleteFilesManually was invoked");
        }
如果是85的话 那应该更不可能报磁盘不足的错误了才是
白白了白呀白 2019-04-20
  • 打赏
  • 举报
回复
引用 6 楼 qybao 的回复:
broker-x.properties //x为a,b...
我看conf文件夹下有多个子文件,有分多master 、多master 多slave同时还有异步同步的,每个字文件夹下都有他们对应的broker.p文件。我目前就一个broker 没弄啥集群就是单击测试的。子文件的外层文件夹有个broker.conf我只要改这个文件夹就行吗。还是那些子文件夹下的broker-a/-b.p文件都要改
qybao 2019-04-19
  • 打赏
  • 举报
回复
rocketmq源码的DefaultMessageStore类里,默认会把剩余磁盘的比率不足75%当做磁盘空间不足处理
可以在broker.p里增加一个配置 diskMaxUsedSpaceRatio=95,或者更高,调整一下磁盘的剩余空间比例试试
十八道胡同 2019-04-19
  • 打赏
  • 举报
回复
https://www.cnblogs.com/xxt19970908/p/6717212.html 一般磁盘剩余不足75%则报警,你的20G 不足75%?
qybao 2019-04-19
  • 打赏
  • 举报
回复
看了一下源码,磁盘应该是你存储message的磁盘
private boolean isSpaceToDelete() {
double ratio = DefaultMessageStore.this.getMessageStoreConfig().getDiskMaxUsedSpaceRatio() / 100.0;

cleanImmediately = false;

{
String storePathPhysic = DefaultMessageStore.this.getMessageStoreConfig().getStorePathCommitLog();
double physicRatio = UtilAll.getDiskPartitionSpaceUsedPercent(storePathPhysic);
if (physicRatio > diskSpaceWarningLevelRatio) {
boolean diskok = DefaultMessageStore.this.runningFlags.getAndMakeDiskFull();
if (diskok) {
DefaultMessageStore.log.error("physic disk maybe full soon " + physicRatio + ", so mark disk full");
}

cleanImmediately = true;
} else if (physicRatio > diskSpaceCleanForciblyRatio) {
cleanImmediately = true;
} else {
boolean diskok = DefaultMessageStore.this.runningFlags.getAndMakeDiskOK();
if (!diskok) {
DefaultMessageStore.log.info("physic disk space OK " + physicRatio + ", so mark disk ok");
}
}

if (physicRatio < 0 || physicRatio > ratio) {
DefaultMessageStore.log.info("physic disk maybe full soon, so reclaim space, " + physicRatio);
return true;
}
}


 public int getDiskMaxUsedSpaceRatio() {
if (this.diskMaxUsedSpaceRatio < 10)
return 10;

if (this.diskMaxUsedSpaceRatio > 95)
return 95;

return diskMaxUsedSpaceRatio;
}


看最新代码好像是默认85%
class CleanCommitLogService {

private final static int MAX_MANUAL_DELETE_FILE_TIMES = 20;
private final double diskSpaceWarningLevelRatio =
Double.parseDouble(System.getProperty("rocketmq.broker.diskSpaceWarningLevelRatio", "0.90"));

private final double diskSpaceCleanForciblyRatio =
Double.parseDouble(System.getProperty("rocketmq.broker.diskSpaceCleanForciblyRatio", "0.85"));
private long lastRedeleteTimestamp = 0;

private volatile int manualDeleteFileSeveralTimes = 0;

private volatile boolean cleanImmediately = false;

public void excuteDeleteFilesManualy() {
this.manualDeleteFileSeveralTimes = MAX_MANUAL_DELETE_FILE_TIMES;
DefaultMessageStore.log.info("executeDeleteFilesManually was invoked");
}
qybao 2019-04-19
  • 打赏
  • 举报
回复
参考一下github
https://github.com/apache/rocketmq/blob/master/distribution/conf/2m-noslave/broker-a.properties
qybao 2019-04-19
  • 打赏
  • 举报
回复
broker-x.properties //x为a,b...
白白了白呀白 2019-04-19
  • 打赏
  • 举报
回复
引用 2 楼 qybao 的回复:
rocketmq源码的DefaultMessageStore类里,默认会把剩余磁盘的比率不足75%当做磁盘空间不足处理 可以在broker.p里增加一个配置 diskMaxUsedSpaceRatio=95,或者更高,调整一下磁盘的剩余空间比例试试
不知道你.p后缀的文件是哪个, 我再rocketmq的目录下没看到
白白了白呀白 2019-04-19
  • 打赏
  • 举报
回复
引用 1 楼 LCL_data 的回复:
https://www.cnblogs.com/xxt19970908/p/6717212.html 一般磁盘剩余不足75%则报警,你的20G 不足75%?
还是要算所有磁盘的总和吗
白白了白呀白 2019-04-19
  • 打赏
  • 举报
回复
引用 1 楼 LCL_data 的回复:
https://www.cnblogs.com/xxt19970908/p/6717212.html 一般磁盘剩余不足75%则报警,你的20G 不足75%?
我是放在H盘的算了下比例才66左右

81,092

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧