Enseñarle cómo usar bsdiff para implementar actualizaciones incrementales en el backend de Java

Escribí un blog antes: Te enseño cómo usar bsdiff en Android para implementar la actualización incremental de archivos .
Dado que Android Studio viene con un entorno NDK, es relativamente simple implementar JNI.
Pero como se menciona en el blog, la función de diferenciación de archivos debe realizarse en el lado del servidor. El entorno donde corre el servidor puede ser en windows, mac o linux. Por lo tanto, necesitamos compilar el código fuente de bsdiff en un archivo de biblioteca nativo correspondiente al entorno, para que pueda llamarse a Java.
Dado que la mayoría de los proyectos se ejecutan básicamente en Linux, aquí solo demostraré cómo compilar el código fuente bsdiff en un archivo so y luego llamarlo en el código java.

Sin más preámbulos, ¡comencemos!


Clion construye el entorno de compilación.

Aquí uso Clion en el cubo de la familia Jetbrains. No hace falta decir que el IDE de Jetbrains lo entiende todo.

En primer lugar, debe preparar un sistema Linux. Para Windows, se recomienda instalar ubuntu directamente con WSL para la depuración local. El método de instalación también es relativamente simple, solo siga la documentación oficial. Documentación de instalación de WSL:

Si es una MAC, se recomienda usar Docker para instalar Linux para la depuración local. Cuando use Docker como entorno de compilación de Clion, asegúrese de instalar la imagen que ya tiene configurado el entorno de compilación, como cmake y gcc, de lo contrario, Clion no lo reconocerá. Instalé ubuntu primero antes de instalar cmake, gdb y otros entornos. Como Como resultado, Clion no pudo encontrarlo y seguía diciendo que no se encontró el paquete de instalación. Estuve atrapado en este lugar durante casi un día y dudé de mi vida. Posteriormente, tuvo éxito según el documento oficial Documento oficial: Uso de Docker con CLion

Jetbrains también proporciona varios Dockerfiles escritos en github, puede elegir según sus necesidades. clion-control remoto

La otra es una forma más recomendada. Vaya a Alibaba Cloud para comprar un host en la nube, compílelo de forma remota y luego publique el código escrito en el extremo remoto para probarlo. Se pueden usar tanto Windows como Mac, lo cual es más conveniente.

Después de instalar el entorno Linux, debe instalar cmake, gdb, etc., y no hay muchos BB aquí. Después de la instalación, vaya a Clion para configurar la cadena de herramientas y configúrela según sus necesidades, de la siguiente manera.
inserte la descripción de la imagen aquí

Luego configura cmake
inserte la descripción de la imagen aquí

Hasta ahora el entorno está configurado


Compilar C a fin de Linux

En el blog anterior sobre Android usando bsdiff, ya procesamos bsdiff y también podemos escribirlo en Android.
Es el mismo paso en el proyecto web java, preparar el código nativo declarado por java, cambiar el código cpp y configurar cmake.

Cabe señalar que el NDK en el proyecto de Android puede importar directamente jni.h. En Clion, debemos copiar manualmente jni.h y jni_md.h en jdk al proyecto.
jni.h está en el directorio de inclusión en jdk, jni_md.h está en el directorio win32.

inserte la descripción de la imagen aquí

La estructura del código es la siguiente
inserte la descripción de la imagen aquí
y luego puede compilar el archivo so. Haga clic en compilar->Reconstruir proyecto.
inserte la descripción de la imagen aquí

Espere a que se construya el proyecto, como se muestra a continuación
inserte la descripción de la imagen aquí
, puede ver que está completo y luego vaya al directorio que figura en el cuadro rojo de arriba para ver el archivo.
Como puede ver, hay un código fuente en Linux, que es equivalente a que Clion cargue su código local en Linux y luego lo compile a través del entorno de Linux.
inserte la descripción de la imagen aquí
Corte al directorio final especificado para ver
inserte la descripción de la imagen aquí
el archivo so en el cuadro rojo de arriba es lo que necesitamos.

Hasta ahora, se completó la compilación del archivo so en Linux.


Implementación Java Web Jni

Entonces es simple, solo una implementación normal de JNi. Pegue primero el directorio del proyecto.

inserte la descripción de la imagen aquí
Tenga en cuenta que necesita agregar código Java para acceder a la configuración del archivo so en el directorio de recursos.
Los siguientes son algunos códigos clave. Aquí está solo el código de demostración, principalmente para realizar la función. En el entorno de producción, preste atención a la robustez del código.

package com.yzq.bsdiffserver.utils;

import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;


/**
 * @description: 用来load so文件,先将项目的so拷贝到linux中,然后加载
 * @author : yuzhiqiang ([email protected])
 * @date   : 2021/12/18
 * @time   : 14:33
 */
public class LibLoader {
    
    
    public static void loadLib(String libName, String resourcePath) {
    
    

        System.out.println("libName = " + libName);
        System.out.println("resourcePath = " + resourcePath);

        /*获取当前项目所在的linux路径 示例:/home/admin/webapp */
        final String projectPath = System.getProperty("user.dir");

        System.out.println("projectPath = " + projectPath);

        /*创建一个目录 用来放so*/
        String nativeLibPath = projectPath + File.separator + "tmp" + File.separator + "bsdiffdemo" + File.separator + "lib" + File.separator;

        File nativeLibFolder = new File(nativeLibPath);
        if (!nativeLibFolder.exists()) {
    
    
            nativeLibFolder.mkdirs();
        }
        /*用来存放临时文件的目录*/
        String filePath = projectPath + File.separator + "tmp" + File.separator + "bsdiffdemo" + File.separator + "file" + File.separator;
        File fileFolder = new File(filePath);
        if (!fileFolder.exists()) {
    
    
            fileFolder.mkdirs();
        }

        File libFile = new File(nativeLibFolder, libName);
        System.out.println("libFile.getAbsolutePath() = " + libFile.getAbsolutePath());
        if (libFile.exists()) {
    
    
            System.out.println("libFile 文件存在 libFile.getAbsolutePath() = " + libFile.getAbsolutePath());
            System.load(libFile.getAbsolutePath());
        } else {
    
    
            try {
    
    

                final InputStream inputStream = new ClassPathResource(resourcePath).getInputStream();
//                InputStream in = LibLoader.class.getResourceAsStream(resourcePath);
                final FileOutputStream fileOutputStream = new FileOutputStream(libFile);

                /*把文件存到临时目录里*/
                final int copy = FileCopyUtils.copy(inputStream, fileOutputStream);

                System.out.println("copy result= " + copy);
                inputStream.close();
                fileOutputStream.close();
                System.out.println("libFile.getAbsolutePath() = " + libFile.getAbsolutePath());
                System.load(libFile.getPath());


            } catch (Exception e) {
    
    
                e.printStackTrace();
                throw new RuntimeException("Failed to load required lib", e);
            }
        }
    }
}

El código de la clase de herramienta es similar al de Android.

package com.yzq.bsdiffserver.utils;

import java.io.File;


/**
 * @author : yuzhiqiang ([email protected])
 * @description: BsDiffUtil
 * @date : 2021/12/18
 * @time : 14:33
 */
public class BsDiffUtil {
    
    
    
    static {
    
    
        String systemType = System.getProperty("os.name");
        System.out.println("systemType = " + systemType);
        try {
    
    
            /*获取当前项目所在的linux路径 示例:/home/admin/webapp */
//            final String projectPath = System.getProperty("user.dir");
            String libPath = "lib" + File.separator + "libxeon_bsdiff.so";
            String libName = "libxeon_bsdiff.so";

            /*mac上使用dylib*/
//            String libPath = "lib" + File.separator + "libxeon_bsdiff.dylib";
//            String libName = "libxeon_bsdiff.dylib";
            LibLoader.loadLib(libName, libPath);
            System.out.println("load so success");

        } catch (Exception e) {
    
    
            System.out.println("e = " + e.getMessage());
            e.printStackTrace();
        }

    }


    /**
     * 生成补丁文件
     *
     * @param newFilePath
     * @param oldFilePath
     * @param patchFilePath
     * @return
     */
    public static native int fileDiff(String newFilePath, String oldFilePath, String patchFilePath);

    /**
     * 合并文件
     *
     * @param oldFilePath
     * @param patchFilePath
     * @param combineFilePath
     * @return
     */
    public static native int filePatch(String oldFilePath, String patchFilePath, String combineFilePath);
}

Métodos de prueba

package com.yzq.bsdiffserver.service;


import com.yzq.bsdiffserver.utils.BsDiffUtil;

import java.io.File;


/**
 * @description: 测试类
 * @author : yuzhiqiang ([email protected])
 * @date   : 2021/12/18
 * @time   : 14:55
 */
public class BsDiffService {
    
    

    public static void testBsDiff() {
    
    
        try {
    
    

            /*测试load so*/
//            BsDiffUtil.test();

            final String projectPath = System.getProperty("user.dir");
            String filePath = projectPath + File.separator + "tmp" + File.separator + "bsdiffdemo" + File.separator + "file" + File.separator;
            /*旧文件路径*/

            String oldFilePath = filePath + "old.txt";
            /*新文件*/
            String newFilePath = filePath + "new.txt";
            /*补丁文件*/
            String patchFilePath = filePath + "patch.txt";
            /*合并后的文件*/
            String combineFilePath = filePath + "combine.txt";

            System.out.println("oldFilePath = " + oldFilePath);
            System.out.println("newFilePath = " + newFilePath);
            System.out.println("patchFilePath = " + patchFilePath);
            System.out.println("combineFilePath = " + combineFilePath);

            /*生成补丁文件*/
            final int diffResult = BsDiffUtil.fileDiff(newFilePath, oldFilePath, patchFilePath);
            System.out.println("diffResult = " + diffResult);

            /*合并补丁文件*/
            final int patchResult = BsDiffUtil.filePatch(oldFilePath, patchFilePath, combineFilePath);
            System.out.println("patchResult = " + patchResult);


        } catch (Exception e) {
    
    
            e.printStackTrace();
        }


    }


}

Una vez que el código Java está listo, se puede implementar directamente en el servidor remoto para realizar pruebas.

Aquí están los resultados de la prueba directamente.

inserte la descripción de la imagen aquí
El directorio donde se almacenan los archivos so
inserte la descripción de la imagen aquí

Directorio para probar archivos diff combinados
inserte la descripción de la imagen aquí

Se puede ver que el tamaño del archivo fusionado es el mismo que el del archivo nuevo, y el contenido es exactamente el mismo después de leerlo.

En este punto, se ha abierto la tecnología de función de actualización incremental basada en bsdiff de Android al back-end.La aplicación específica depende de sus necesidades.

No es fácil codificar en invierno y mis manos están heladas. Si crees que es útil, haz clic en Me gusta para apoyarlo...
inserte la descripción de la imagen aquí


Si crees que este artículo es útil para ti, por favor dale me gusta. Puede ayudar a más desarrolladores. Si hay algún error en el artículo, corrígeme. Para volver a imprimir, indica que estás reenviando desde el blog de Yu Zhiqiang, ¡ gracias !

Supongo que te gusta

Origin blog.csdn.net/yuzhiqiang_1993/article/details/121923120
Recomendado
Clasificación