logbackが提供するファイル書き込み方法を使用して、1秒あたり約100,000データにテキストをすばやく書き込みます

著者はオンラインプロジェクトに参加しています。このプロジェクトのデータストレージ容量は巨大です。毎秒約10Wのデータがプッシュされます。プロジェクトは最初にmysqlをストレージとして使用しましたが、従来のデータベースでは、このデータ量でサポートされますやがて、サブデータベースとサブテーブルの使用は一時的な解決策に過ぎません。結局、リーダーはデータストレージにHadoop + HBaseを使用することを決定しましたが、上記の問題は、ある日ビッグデータプラットフォームがクラッシュすることを懸念しているため、同時にtxtにファイルを書き込む必要があります。最終的な保証は、将来の極度の問題の回復の基礎となります。

ファイルに書き込まれるこのレベルのデータには、IOに対する高い要件が必要です。当時、コード開発は別の同僚であり、Javaが提供する基本ファイルAPI FileOutputStreamを使用してファイルに書き込みました。この方法は通常の書き込みでは問題ありませんが、データ量が多すぎると書き込み速度が極端に遅くなり、MQのデータが継続的にスクイーズおよびブロックされます。

当時、プロジェクトチームはファイルをtxtにすばやく書き込む方法について話し合っていました。考えた結果、プロジェクトのログは、アクセス頻度やトラフィック量に関係なく、常にリアルタイムでログに書き込むことができることがわかりました。ファイルでは、ログフレームワークはSpringBootがデフォルトでサポートするログバックなので、作成者はこのメソッドを使用して上記に提案します。議論の後、logbackによって提供されるAPIを使用してデータをtxtに書き込むことにしました。まず、データフロープロセスと書き込みプロセスを紹介しましょう

クライアントデータ-> netty-> rabbit mq-> logback-> txt

つまり、クライアントは長い接続を介してサーバーのnettyにデータを送信します。nettyがデータを受信すると、MQに格納されます。私のログバックプログラムはサブスクライバーとして機能します。データを読み取った後、データは分類され、txtに書き込まれます。コードの実装を見てください(txtで書かれたコードにのみ焦点を当ててください、著者は多くの場所で多くのカットを行いました、読者は意味を知ることができます)

@Component
@RabbitListener
public class DataListener {

    private static final Logger logger = LoggerFactory.getLogger(DataListener.class);

    @RabbitHandler
    public void getMessage(@Payload Data data) {
        try {
            data.setUuid(UUID.randomUUID().toString().replace("-", ""));
            // 此处根据业务逻辑以数据的num后四位作为文件名称
            String str4id = "0000";
            if (data.getNum() != null && data.getNum().length() > 4) {
                str4id = data.getNum().substring(data.getNum().length() - 4, data.getNum().length());
            }
            data.setStr4id(10000 + Long.parseLong(str4id));
            String fileName = "data_" + data.getStr4id();
            // 文件名称可以看做是一个客户端对象,这个客户端对象会不停的发送数据过来, 对每个文件名称都做了一个DataLogWriter对象的缓存,有就使用已经创建的写入,没有则创建并保存到map缓存中后写入
            DataLogWriter dataSqlWriter = dataUtil.objMap.get(fileName);
            if (dataSqlWriter == null) {
                dataSqlWriter = new dataLogWriter(fileName);
                dataUtil.objMap.put(fileName, dataSqlWriter);
            }
            dataSqlWriter.outLog(dataUtil.getdataLog(data));
        } catch (Exception ex) {
            ex.printStackTrace();
            logger.error("gpswriter异常:", ex);
        }
    }

}

package cn.net.leadu.log;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.FileAppender;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class DataLogWriter {

    private final Logger logbackLogger;
    LoggerContext loggerContext = new LoggerContext();
    public FileAppender fileAppender = new FileAppender();
    private String dayDate ; //当前天
    private String loggerName; //日志文件名称

    public void outLog(String msg){
        SimpleDateFormat yMd = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        String nowDate = yMd.format(date);
        //每次写入的时候,查看当前时间和对象中的天数是否相同,如果不相同则重置日志路径
        if(!nowDate.equals(dayDate)) {
            dayDate = nowDate;
            fileAppender.setFile("/data/data_backup/" + dayDate + "/" + loggerName + ".txt");
            fileAppender.start();
            logbackLogger.debug(msg);
        }else{
            logbackLogger.debug(msg);
        }
    }


    public DataLogWriter(String logfileName) {
        SimpleDateFormat yMd = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        loggerName = logfileName;
        fileAppender.setContext(loggerContext);
        fileAppender.setName("logfile");
        //文件路径 按照天 / 小时 / num后四位分文件
        dayDate = yMd.format(date);
        fileAppender.setFile("/data/data_backup/" + dayDate +"/" + loggerName + ".txt");
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(loggerContext);
        encoder.setPattern("%msg");
        encoder.start();
        fileAppender.setEncoder(encoder);
        fileAppender.start();
        logbackLogger = loggerContext.getLogger("nelogger");
        logbackLogger.addAppender(fileAppender);
        logbackLogger.isAdditive();
    }

}

その時、このように書いた後、サービスに行き、クラスターを開いてください〜それが不可能であることがわかりました... MQはまだ積み上げられているのですか、それともダウンしていないのですか?
なぜ...それについて考えた後、その理由は、複数のサービスが同時にファイルに書き込みを行うと、ファイルが占有され、互いに待機し、デッドロックが発生するため、MQメッセージの読み取りと書き込みが行われるためです。これには、読者はファイルハンドルの問題を知ることができます。解決策は、クラスターを開かない〜Haha、1つのサービスのみを開くことです。現時点
では、読み書き速度はロケットの速度と同等です。

このサービスはオンラインで長時間実行されており、問題は発生していません。もちろん、運用と保守はサーバーディスクに注意を払う必要があり、ディスクがほぼ満杯のときに発生するさまざまな異常な問題は発生しません。ビッグデータはハードウェアストレージと密接に関連しています〜

ここでは、ファイルの書き込みに対してどのような最適化ログバックが行われたかについては説明しません。読者が同じ問題に遭遇した場合は、この記事を参照し、ログバックを使用してこの問題を解決できます〜

読んでくれてありがとう

元の38の記事を公開しました 賞賛されました17 9020を訪問します

おすすめ

転載: blog.csdn.net/cainiao1412/article/details/99174718