序
最近在忙工作的事,本来每天中午都要午睡的,我媳妇 都让我冷静点,但我冷静不了,知道没什么用,但是我尽力就好,让更多人看到
从前几天就看到的 《僧丫 》的实名举报助学金名额被挤占,这几天一直在关注,直到11月2号凌晨, 《四川观察》 第一个官媒帮忙发视频。
我也是一个单亲家庭,我听他本人的视频的时候 认真的听,不争气的说(我哭了),能感觉到他的无奈,尤其那句 奶奶和他说 家里真的没钱了,我们对这种事可能早就见过 或者 听过,但是一般都是不管不顾,因为没有必要,但能感觉到 他是遇到了特别的难处
我当时家里亲戚和我孤儿寡母 的打官司,要我家房子,打了4年,从12岁打到16岁,本来能赢的,前面还老赢,后面我们没钱请律师了,终审 判给了他们,4家,一家8w块,(16年前),这个社会就这样,我早看透了,希望大家去b站,或者抖音 帮顶下
僧丫 18岁的户主
建档立卡都评不上,操
如果占用了掘金的资源,抱歉
生气的点
b站在《僧丫》视频出来几个小时 就删除了
我看到了,然后后面就看不了了
《僧丫》给出了证据,还有人说风凉话
我相信这绝不是唯一的一件,我当时的大学导员就是 奖学金,助学金都是内定的,只是人家准备了一堆理由,让你找不到把柄,明眼人谁不知道怎么回事,我相信
序
今天再看一篇文章的时候,看到一句话
确实我也感觉有点 纯理论了,我给补充点代码
问题列表
- rocketmq 怎么做的存储模块
- 操作系统怎么做到的"按照空间占用比例"、"时间周期扫描"
本节细节
- 怎么判断 操作系统PageCache繁忙
- 做一个sdk,让我们能直接使用
背景
我是一枚大专非计算机专业的 java程序员,读写文件 我可能第一印象是File的工具类,比如
ini
long startTime = System.currentTimeMillis();
MultipartFile multipartFile = req.getFile();
PDDocument document;
String doc = "";
String originalFilename = "";
String prefixStr;
File file;
try {
originalFilename = multipartFile.getOriginalFilename();
String[] filename = originalFilename.split("\.");
prefixStr = String.join(".", Arrays.copyOfRange(filename, 0, filename.length - 1));
file = File.createTempFile(VerificationCodeGenerator.generateCode(6), "." + filename[filename.length-1]);
multipartFile.transferTo(file);
long startTime1 = System.currentTimeMillis();
document = PDDocument.load(file);
long endTime1 = System.currentTimeMillis();
System.out.println("读取pdf 花费的时间:" + (endTime1 - startTime1));
PDFTextStripper pdfStripper = new PDFTextStripper();
doc = pdfStripper.getText(document);
long endTime2 = System.currentTimeMillis();
System.out.println("读取doc内容 花费的时间:" + (endTime2 - startTime1));
// file.deleteOnExit();
document.close();
} catch (IOException e) {
log.error(e.getMessage());
return null;
}
大概读一个100页的pdf 耗时
读取pdf 花费的时间:104
读取doc内容 花费的时间:264
读取pdf 花费的时间:127
读取doc内容 花费的时间:486
读取pdf 花费的时间:64
读取doc内容 花费的时间:364
读取pdf 花费的时间:98
读取doc内容 花费的时间:287
有没有办法优化下呢
分两种,因为pdf 是特殊的文件,FileChannal 读取不了pdf内容,一般都是操作的txt。
- 基础中间件的高性能读写
- 业务中 读取pdf / docx 等文件的读写
今天这篇 我们先说 基础中间的高性能读写
- 内存读写的效率高于硬盘读写
- 批量读写的效率高于单条读写
- 顺序读写的效率高于随机读写
- 数据复制次数越多,效率越低
具体说说
提升写入操作的性能
写入性能的提高主要有缓存写、批量写、顺序写三个思路,这里对比来讲。
写入内存 就返回肯定是最快的,但是还要保证高性能的同时,不丢失数据
缓存写 与批量写
在计算机理论基础中,计算机多级存储模型的层级越高,代表速度越快(同时容量也越小,价格也越贵),也就是说写入速度从快到慢分别是:寄存器 > 缓存 > 主存 > 本地存储 > 远程存储。
写入优化的主要思路之一是:将数据写入到速度更快的内存中,等积攒了一批数据,再批量刷到硬盘中。
平时我们在一些技术文章看到的"数据先写入 PageCache,再批量刷到硬盘",说的就是这个思路。PageCache 指操作系统的页缓存,简单理解就是内存,通过缓存读写数据可以避免直接对硬盘进行操作,从而提高性能。
把缓存数据刷回到硬盘,一般有"按照空间占用比例"、"时间周期扫描"和"手动强制刷新"三种策略。操作系统内核提供了前两种处理策略,不需要应用程序感知。我们具体了解一下。
按空间占用比例刷新是指当系统内存中的"脏"数据大于某个阈值时会将数据刷新到硬盘。
操作系统提供了两个配置项。
- "脏"数据在内存中的占比(dirty_background_ratio)
- "脏"数据的绝对的字节数(dirty_background_bytes)
当这两个配置超过阈值,就会触发刷新操作。如果两者同时设置,则以绝对字节数为更高优先级。
按时间周期刷新是指根据配置好的时间,周期性刷新数据到硬盘。主要通过脏页存活时间(dirty_expire_seconds) 和刷新周期(dirty_writeback_centisecs)两个参数来配置。两个配置默认都是 1/100,也就说时间间隔为每秒 100 次,根据刷新周期的配置周期性执行刷新,刷新会检查脏页的存活时间是否超过配置的最大存活时间,如果是则刷入硬盘。
同时,操作系统也提供了第三种方法程序手动强制刷新,你可以通过系统提供的 sync()/msync()/fsync() 调用来强制刷新缓存。通过操作系统的参数配置,在 Java 代码中,通过 Java.NIO 包中 FileChannel 提供的 write() 和 force() 方法,实现写缓存和强制刷新缓存。代码参考:
ini
FileChannel channel = fin.getChannel();
file.write(buf)
file.force(true)
通过 FileChannel 提供的 write() 方法写数据时,FileChannel 把数据写入到缓存就会返回成功,然后依赖操作系统的缓存更新策略,将数据刷新到硬盘。我们也可以在代码中调用 FileChannel 提供的 force() 方法,把数据立即刷入刷盘中以免丢失。
基本所有的消息队列在写入时用的都是这个方案,比如 Kafka、RocketMQ、Pulsar 就是先写入缓存,然后依赖操作系统的策略刷新数据到硬盘。
消息队列一般会同时提供:是否同步刷盘、刷盘的时间周期、刷盘的空间比例三个配置项,让业务根据需要调整自己的刷新策略。从性能的角度看,异步刷新肯定是性能最高的,同步刷新是可靠性最高的。
rocketmq 怎么做的存储模块
Broker 怎么进行的优化呢?
先把本地Rocketmq 跑起来,可以看 Rocketmq源码(一)手把手本地调试_本地测试rocketmq_Serena0814的博客-CSDN博客
主要代码在 broker中调用的store代码
kotlin
@Override
public CompletableFuture<PutMessageResult> asyncPutMessage(MessageExtBrokerInner msg) {
PutMessageStatus checkStoreStatus = this.checkStoreStatus();
if (checkStoreStatus != PutMessageStatus.PUT_OK) {
return CompletableFuture.completedFuture(new PutMessageResult(checkStoreStatus, null));
}
PutMessageStatus msgCheckStatus = this.checkMessage(msg);
if (msgCheckStatus == PutMessageStatus.MESSAGE_ILLEGAL) {
return CompletableFuture.completedFuture(new PutMessageResult(msgCheckStatus, null));
}
long beginTime = this.getSystemClock().now();
CompletableFuture<PutMessageResult> putResultFuture = this.commitLog.asyncPutMessage(msg);
putResultFuture.thenAccept((result) -> {
long elapsedTime = this.getSystemClock().now() - beginTime;
if (elapsedTime > 500) {
log.warn("putMessage not in lock elapsed time(ms)={}, bodyLength={}", elapsedTime, msg.getBody().length);
}
this.storeStatsService.setPutMessageEntireTimeMax(elapsedTime);
if (null == result || !result.isOk()) {
this.storeStatsService.getPutMessageFailedTimes().incrementAndGet();
}
});
return putResultFuture;
}
检查 Store 状态
kotlin
private PutMessageStatus checkStoreStatus() {
//本身broker 是否关闭
if (this.shutdown) {
log.warn("message store has shutdown, so putMessage is forbidden");
return PutMessageStatus.SERVICE_NOT_AVAILABLE;
}
//如果是broker的子节点
if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
long value = this.printTimes.getAndIncrement();
if ((value % 50000) == 0) {
log.warn("broke role is slave, so putMessage is forbidden");
}
return PutMessageStatus.SERVICE_NOT_AVAILABLE;
}
if (!this.runningFlags.isWriteable()) {
long value = this.printTimes.getAndIncrement();
//% 50000 只是为了打印日志 找问题
if ((value % 50000) == 0) {
log.warn("the message store is not writable. It may be caused by one of the following reasons: " +
"the broker's disk is full, write to logic queue error, write to index file error, etc");
}
return PutMessageStatus.SERVICE_NOT_AVAILABLE;
} else {
this.printTimes.set(0);
}
if (this.isOSPageCacheBusy()) {
return PutMessageStatus.OS_PAGECACHE_BUSY;
}
return PutMessageStatus.PUT_OK;
}
分析上述检查broker状态代码,broker拒绝写入消息有以下几种情况:
- broker处于停止状态。broker在停止时,会通知MessageStore将shutdown标识设置为true
- broker是slave角色
- broker不支持写入,可能是磁盘满了、写consumeQueue消息队列错误或是写IndexFile错误等原因
- 操作系统PageCache繁忙
怎么判断 操作系统PageCache繁忙
arduino
public boolean isOSPageCacheBusy() {
long begin = this.getCommitLog().getBeginTimeInLock();
long diff = this.systemClock.now() - begin;
//代码@2
return diff < 10000000
&& diff > this.messageStoreConfig.getOsPageCacheBusyTimeOutMills();
}
- begin 通俗的一点讲,就是将消息写入Commitlog文件所持有锁的时间,精确说是将消息体追加到内存映射文件(DirectByteBuffer)或pageCache(FileChannel#map)该过程中开始持有锁的时间戳,具体的代码请参考:CommitLog#putMessage。
- diff 一次消息追加过程中持有锁的总时长,即往内存映射文件或pageCache追加一条消息所耗时间。
代码@2:如果一次消息追加过程的时间超过了Broker配置文件osPageCacheBusyTimeOutMills,则认为pageCache繁忙,osPageCacheBusyTimeOutMills默认值为1000,表示1s。
存储到store
中间就是判断消息是否正确,然后就开始存储
ini
result = mappedFile.appendMessage(msg, this.appendMessageCallback);