Use el método de escritura de archivos provisto por logback para escribir rápidamente texto a aproximadamente 100,000 datos por segundo

El autor ha participado en un proyecto en línea. La capacidad de almacenamiento de datos de este proyecto es enorme. Aproximadamente 10 W de datos se envían por segundo. El proyecto inicialmente usó mysql como almacenamiento, pero con una base de datos tradicional, esta cantidad de datos admite En poco tiempo, el uso de sub-bases de datos y sub-tablas es solo una solución temporal. Al final, el líder decidió usar Hadoop + HBase para el almacenamiento de datos, pero lo anterior le preocupa que algún día la plataforma de Big Data se enfrentará a un colapso, por lo que debe escribir un archivo en txt al mismo tiempo. La garantía final es la base para la recuperación de problemas extremos en el futuro.

Este nivel de datos, escrito en un archivo, debe tener altos requisitos para IO. En ese momento, el desarrollo del código fue otro colega, que utilizó el archivo básico API FileOutputStream proporcionado por Java para escribir en el archivo. Este método no tiene ningún problema en la escritura normal, pero cuando la cantidad de datos es demasiado grande, la velocidad de escritura es extremadamente lenta, lo que hace que los datos en MQ se compriman y bloqueen continuamente.

En ese momento, el equipo del proyecto estaba discutiendo cómo escribir rápidamente archivos en txt. Después de pensar, descubrí que los registros de nuestro proyecto, sin importar con qué frecuencia se acceda y cuánto tráfico, siempre se pueden escribir en el registro en tiempo real. En el archivo, el marco de registro es el inicio de sesión que SpringBoot admite de manera predeterminada, por lo que el autor usará este método y lo propondrá para lo anterior. Después de la discusión, decidí usar la API proporcionada por el inicio de sesión para escribir los datos en txt. Permítanme presentarles primero el proceso de flujo de datos y el proceso de escritura.

datos del cliente -> netty -> rabbit mq -> logback -> txt

Es decir, el cliente envía los datos al servidor del servidor a través de una conexión larga. Después de que el servidor recibe los datos, se almacena en el MQ. Mi programa de inicio de sesión actúa como suscriptor. Después de leer los datos, se clasifica y escribe en txt. Eche un vistazo a la implementación del código (solo céntrese en el código escrito en txt, el autor ha hecho muchos recortes en muchos lugares, el lector puede conocer el significado)

@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();
    }

}

En ese momento, después de escribir así, vaya al servicio, abra el clúster ~ descubrió que no es posible ... ¿MQ todavía está apilado o no?
Por qué ... Después de pensarlo, descubrí que la razón es que cuando varios servicios escriben en un archivo al mismo tiempo, descubrirá que el archivo se ocupa, se espera el uno al otro y se interrumpe, lo que hace que los mensajes MQ se lean y escriban. El lector puede conocer el problema de manejo de archivos. La solución es no abrir el clúster ~ Jaja, solo abrir un servicio. En
este momento, la velocidad de lectura y escritura es comparable a la del cohete.

Este servicio ha estado funcionando durante mucho tiempo en línea, y no ha ocurrido ningún problema. Por supuesto, la operación y el mantenimiento deben prestar atención a los discos de su servidor, y no tienen varios problemas anormales que ocurren cuando el disco está casi lleno. Los grandes datos están estrechamente relacionados con el almacenamiento de hardware ~

No discutiré aquí qué tipo de optimización de registro ha hecho para la escritura de archivos. Si los lectores encuentran el mismo problema, pueden consultar este artículo y usar el registro para resolver este problema ~

Gracias por leer

Publicado 38 artículos originales · elogiado 17 · visitas 9020

Supongo que te gusta

Origin blog.csdn.net/cainiao1412/article/details/99174718
Recomendado
Clasificación