Kaynağa Gözat

初始化文件存储的磁盘相关信息

reghao 3 yıl önce
ebeveyn
işleme
99d0cf243f

+ 4 - 0
src/main/java/cn/reghao/tnb/file/app/config/ConfigMap.java

@@ -1,5 +1,8 @@
 package cn.reghao.tnb.file.app.config;
 
+import cn.reghao.jutil.jdk.machine.data.detail.DiskDetail;
+import cn.reghao.tnb.file.app.util.LocalStore;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -9,6 +12,7 @@ import java.util.Map;
  * @author reghao
  * @date 2022-03-22 16:49:08
  */
+@Deprecated
 public class ConfigMap {
     public static String baseUrl;
     private static final Map<String, DfsConfig> map = new HashMap<>();

+ 35 - 0
src/main/java/cn/reghao/tnb/file/app/controller/ImageFileController.java

@@ -0,0 +1,35 @@
+package cn.reghao.tnb.file.app.controller;
+
+import cn.reghao.tnb.file.app.db.mapper.FileUrlMapper;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.FileInputStream;
+
+/**
+ * @author reghao
+ * @date 2022-05-23 15:32:49
+ */
+@RestController
+@RequestMapping("/image")
+public class ImageFileController {
+    private FileUrlMapper fileUrlMapper;
+
+    @GetMapping("/{uploadId}")
+    public ResponseEntity<InputStreamResource> videoPlayer(@PathVariable("uploadId") String uploadId) throws Exception {
+        //fileUrlMapper.findByUploadId(uploadId);
+        String filePath = "/home/reghao/Downloads/pic/10393c62.jpg";
+        FileInputStream fis = new FileInputStream(filePath);
+        InputStreamResource inputStreamResource = new InputStreamResource(fis);
+
+        HttpHeaders httpHeaders = new HttpHeaders();
+        httpHeaders.set("Pragma", "No-cache");
+        httpHeaders.set("Cache-Control", "no-cache");
+        return ResponseEntity.status(HttpStatus.OK).headers(httpHeaders).contentType(MediaType.IMAGE_JPEG)
+                .body(inputStreamResource);
+    }
+}

+ 1 - 0
src/main/java/cn/reghao/tnb/file/app/util/HertubePrefix.java

@@ -6,6 +6,7 @@ package cn.reghao.tnb.file.app.util;
  * @author reghao
  * @date 2021-08-05 15:41:45
  */
+@Deprecated
 public class HertubePrefix {
     public final static String MP4_VIDEO = "hertube/vid/mp4";
     public final static String HLS_VIDEO = "hertube/vid/hls";

+ 16 - 0
src/main/java/cn/reghao/tnb/file/app/util/LoadBalancer.java

@@ -18,19 +18,30 @@ import java.util.*;
  */
 @Component
 public class LoadBalancer {
+    @Deprecated
     private final List<String> nodes = new ArrayList<>();
     private final HashFunction hashFunction = Hashing.murmur3_32_fixed();
 
+    @Deprecated
     public void init() {
         nodes.addAll(ConfigMap.baseDirs());
     }
 
+    @Deprecated
     public String getBaseDir(Object object) throws IOException, NoSuchAlgorithmException {
         int hash = hash(object);
         int nodeIdx = hash % nodes.size();
         return nodes.get(nodeIdx);
     }
 
+    public String getStoreDir(long fileSize, String sha256sum) throws IOException, NoSuchAlgorithmException {
+        String store = LocalStores.getMaxStore(fileSize);
+        List<String> subDirs = LocalStores.getSubDirs(store);
+        int hash = hash(sha256sum);
+        int nodeIdx = hash % subDirs.size();
+        return subDirs.get(nodeIdx);
+    }
+
     private int hash(Object object) throws IOException, NoSuchAlgorithmException {
         int ret = 0;
         if (object instanceof Integer) {
@@ -47,4 +58,9 @@ public class LoadBalancer {
         }
         return Math.abs(ret);
     }
+
+    // TODO 实现一致性 hash 以处理添加新磁盘时出现的问题
+    private int consistentHash() {
+        return 0;
+    }
 }

+ 66 - 0
src/main/java/cn/reghao/tnb/file/app/util/LocalStore.java

@@ -0,0 +1,66 @@
+package cn.reghao.tnb.file.app.util;
+
+import lombok.Getter;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.nio.file.FileStore;
+
+/**
+ * @author reghao
+ * @date 2022-05-23 15:00:59
+ */
+@Getter
+public class LocalStore {
+    private final String baseDir;
+    private final FileStore fileStore;
+    private final String blockId;
+    private final String fs;
+    private final String fsType;
+    private final String mountedOn;
+    private final long total;
+    private final long max;
+
+    public LocalStore(String baseDir, FileStore fileStore, String blockId, double maxPercent) throws IOException {
+        this.baseDir = baseDir;
+        this.fileStore = fileStore;
+        this.blockId = blockId;
+        this.fs = fileStore.name();
+        this.fsType = fileStore.type();
+        this.mountedOn = fileStore.toString().replace(fs, "").replace(" ()", "");
+        this.total = fileStore.getTotalSpace();
+
+        BigDecimal bigDecimal1 = new BigDecimal(total*10);
+        BigDecimal bigDecimal2 = new BigDecimal(maxPercent*10);
+        BigDecimal result = bigDecimal1.divide(bigDecimal2, RoundingMode.DOWN);
+        this.max = result.longValue();
+    }
+
+    public boolean available(long len) throws IOException {
+        return total - fileStore.getUsableSpace() + len > max;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = result * 31 + fs.hashCode();
+        result = result * 31 + fsType.hashCode();
+        result = result * 31 + mountedOn.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+
+        if (other instanceof LocalStore) {
+            LocalStore o = (LocalStore) other;
+            return o.fs.equals(fs) && o.fsType.equals(fsType) && o.mountedOn.equals(mountedOn);
+        } else {
+            return false;
+        }
+    }
+}

+ 82 - 0
src/main/java/cn/reghao/tnb/file/app/util/LocalStores.java

@@ -0,0 +1,82 @@
+package cn.reghao.tnb.file.app.util;
+
+import cn.reghao.tnb.file.app.config.DfsConfig;
+import cn.reghao.tnb.file.app.config.DfsProperties;
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileStore;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+
+/**
+ * @author reghao
+ * @date 2022-05-23 18:21:22
+ */
+public class LocalStores {
+    private static final Map<String, LocalStore> storeMap = new HashMap<>();
+    private static final Map<String, List<String>> subDirs = new HashMap<>();
+
+    public static void init(DfsProperties dfsProperties) throws IOException {
+        Map<String, String> blockIdMap = getBlockIdMap();
+        Map<String, String> checker = new HashMap<>();
+        for (DfsConfig dfsConfig : dfsProperties.getDfsConfigs()) {
+            String baseDir = dfsConfig.getBaseDir();
+            FileStore fileStore = Files.getFileStore(Path.of(baseDir));
+            String fs = fileStore.name();
+            String previous = checker.putIfAbsent(fs, baseDir);
+            if (previous != null) {
+                String msg = String.format("%s 和 %s 同属于分区 %s, 每个 baseDir 应该分属于不同的分区", baseDir, previous, fs);
+                throw new IOException(msg);
+            }
+
+            String blockId = blockIdMap.get(fs);
+            if (blockId == null) {
+                throw new IOException(String.format("%s 没有对应的 blockId, 请检查 /dev/disk/by-uuid", fs));
+            }
+
+            LocalStore localStore = new LocalStore(baseDir, fileStore, blockId, 0.9);
+            storeMap.put(baseDir, localStore);
+            subDirs.computeIfAbsent(baseDir, k -> new ArrayList<>());
+            createSubDirs(baseDir);
+        }
+    }
+
+    private static Map<String, String> getBlockIdMap() throws IOException {
+        Map<String, String> map = new HashMap<>();
+        File dir = new File("/dev/disk/by-uuid");
+        for (File symbolicFile : Objects.requireNonNull(dir.listFiles())) {
+            String blkId = symbolicFile.getName();
+            String str = Files.readSymbolicLink(Paths.get(symbolicFile.getPath())).toString();
+            String fs = "/dev/" + str.replace("../../", "");
+            map.putIfAbsent(fs, blkId);
+        }
+        return map;
+    }
+
+    private static void createSubDirs(String baseDir) throws IOException {
+        int total = 128;
+        for (int i = 0; i < total; i++) {
+            for (int j = 0; j < total; j++) {
+                String dirPath = String.format("%s/%s/%s", baseDir, i, j);
+                File dir = new File(dirPath);
+                if (!dir.exists()) {
+                    FileUtils.forceMkdir(dir);
+                }
+                subDirs.get(baseDir).add(dirPath);
+            }
+        }
+    }
+
+    public static String getMaxStore(long size) {
+        String storePath = storeMap.keySet().iterator().next();
+        return storePath;
+    }
+
+    public static List<String> getSubDirs(String store) {
+        return subDirs.get(store);
+    }
+}

+ 10 - 10
src/main/resources/application-dev.yml

@@ -1,25 +1,25 @@
 spring:
   redis:
     database: 0
-    host: 192.168.0.125
+    host: localhost
     port: 6379
-    password: Test@123456
+    password: Dev@123456
   datasource:
-    url: jdbc:mysql://192.168.0.50:3306/reghao_tnb_tdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
-    username: test
-    password: Test@123456
+    url: jdbc:mysql://localhost:3306/reghao_tnb_rdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
+    username: dev
+    password: Dev@123456
 dubbo:
   registry:
-    address: zookeeper://192.168.0.50:2181
+    address: zookeeper://localhost:2181
 dfs:
   domain: file.reghao.cn
   dfsConfigs:
     - baseDir: /home/reghao/opt/file/group0/
       group: group0
       node: node0
-    - baseDir: /home/reghao/opt/file/group1/
+    - baseDir: /home/reghao/mnt/file/
       group: group1
       node: node0
-    - baseDir: /home/reghao/opt/file/group2/
-      group: group2
-      node: node0
+#    - baseDir: /home/reghao/opt/file/group2/
+#      group: group2
+#      node: node0

+ 30 - 4
src/test/java/FileTest.java

@@ -1,10 +1,12 @@
+import cn.reghao.jutil.jdk.security.DigestUtil;
 import cn.reghao.jutil.tool.id.IdGenerator;
 import cn.reghao.tnb.file.app.FileApplication;
+import cn.reghao.tnb.file.app.config.DfsProperties;
 import cn.reghao.tnb.file.app.db.mapper.*;
 import cn.reghao.tnb.file.app.model.po.FileInfo;
 import cn.reghao.tnb.file.app.model.po.FileUrl;
-import cn.reghao.tnb.file.app.model.po.FileUser;
-import org.apache.commons.io.FileUtils;
+import cn.reghao.tnb.file.app.util.LoadBalancer;
+import cn.reghao.tnb.file.app.util.LocalStores;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -13,9 +15,10 @@ import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
-import java.util.List;
-import java.util.stream.Collectors;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
 
 /**
  * @author reghao
@@ -59,4 +62,27 @@ public class FileTest {
             System.out.println();
         });
     }
+
+    @Autowired
+    DfsProperties dfsProperties;
+    @Autowired
+    LoadBalancer loadBalancer;
+    @Test
+    public void test1() throws IOException, NoSuchAlgorithmException {
+        LocalStores.init(dfsProperties);
+
+        String dirPath = "/home/reghao/Downloads";
+        File dir = new File(dirPath);
+        for (File file : Objects.requireNonNull(dir.listFiles())) {
+            if (file.isDirectory()) {
+                continue;
+            }
+
+            long len = file.length();
+            FileInputStream fis = new FileInputStream(file);
+            String sha256sum = DigestUtil.sha256sum(fis);
+            String storeDir = loadBalancer.getStoreDir(len, sha256sum);
+            System.out.println();
+        }
+    }
 }

+ 42 - 12
src/test/java/FileTest1.java

@@ -1,8 +1,14 @@
-import org.apache.commons.io.FileUtils;
 import org.junit.Test;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.EnumSet;
+
+import static java.nio.file.StandardOpenOption.*;
 
 /**
  * @author reghao
@@ -10,17 +16,41 @@ import java.io.IOException;
  */
 public class FileTest1 {
     @Test
-    public void test1() throws IOException {
-        String baseDir = "/home/reghao/Downloads/dfs";
-        int total = 128;
-        for (int i = 0; i < total; i++) {
-            for (int j = 0; j < total; j++) {
-                String dirPath = String.format("%s/%s/%s", baseDir, i, j);
-                File dir = new File(dirPath);
-                if (!dir.exists()) {
-                    FileUtils.forceMkdir(dir);
-                }
-            }
+    public void writeTest() throws IOException {
+        FileInputStream fis = new FileInputStream("/home/reghao/Downloads/pic/818241ae.jpeg");
+        String filePath = "/home/reghao/Downloads/dfs/test.dat";
+        RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
+        raf.seek(0);
+        raf.write(fis.readAllBytes());
+        raf.close();
+    }
+
+    @Test
+    public void createTest() throws IOException {
+        String filePath = "/home/reghao/Downloads/dfs/test2.dat";
+        createSparseFile(filePath);
+    }
+
+    void createFile(String filePath) throws IOException {
+        File file = new File(filePath);
+        if (file.exists()) {
+            throw new IOException(String.format("%s 已存在", filePath));
         }
+
+        boolean created = file.createNewFile();
+        if (!created) {
+            throw new IOException(String.format("创建 %s 失败", filePath));
+        }
+
+        RandomAccessFile raf = new RandomAccessFile(file, "rw");
+        raf.setLength(1024*1024*128);
+        raf.close();
+    }
+
+    void createSparseFile(String filePath) throws IOException {
+        Files.newByteChannel(Paths.get(filePath), EnumSet.of(CREATE_NEW, WRITE, SPARSE));
+        RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
+        raf.setLength(1024*1024*128);
+        raf.close();
     }
 }