From 8de1c479710527c143825ab14133d24a1c764456 Mon Sep 17 00:00:00 2001
From: yw <2185022909@qq.com>
Date: Wed, 23 Aug 2023 09:37:14 +0800
Subject: [PATCH 1/2] =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=AD=98=E5=82=A8?=
=?UTF-8?q?=E6=89=A9=E5=B1=95-Minio?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
powerjob-server/pom.xml | 8 +-
.../powerjob-server-persistence/pom.xml | 4 +
.../storage/StorageConfiguration.java | 11 +-
.../storage/impl/MinioOssService.java | 234 ++++++++++++++++++
4 files changed, 252 insertions(+), 5 deletions(-)
create mode 100644 powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/storage/impl/MinioOssService.java
diff --git a/powerjob-server/pom.xml b/powerjob-server/pom.xml
index cc097c96..1815ec51 100644
--- a/powerjob-server/pom.xml
+++ b/powerjob-server/pom.xml
@@ -55,6 +55,7 @@
4.3.5
1.6.14
3.17.1
+ 8.5.2
4.4
@@ -113,7 +114,12 @@
aliyun-sdk-oss
${aliyun-sdk-oss.version}
-
+
+
+ io.minio
+ minio
+ ${minio.version}
+
org.apache.commons
diff --git a/powerjob-server/powerjob-server-persistence/pom.xml b/powerjob-server/powerjob-server-persistence/pom.xml
index e51b54a9..d467dc63 100644
--- a/powerjob-server/powerjob-server-persistence/pom.xml
+++ b/powerjob-server/powerjob-server-persistence/pom.xml
@@ -40,6 +40,10 @@
com.aliyun.oss
aliyun-sdk-oss
+
+ io.minio
+ minio
+
\ No newline at end of file
diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/storage/StorageConfiguration.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/storage/StorageConfiguration.java
index 6b5627fe..890fbeba 100644
--- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/storage/StorageConfiguration.java
+++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/storage/StorageConfiguration.java
@@ -4,10 +4,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import tech.powerjob.server.extension.dfs.DFsService;
-import tech.powerjob.server.persistence.storage.impl.AliOssService;
-import tech.powerjob.server.persistence.storage.impl.EmptyDFsService;
-import tech.powerjob.server.persistence.storage.impl.GridFsService;
-import tech.powerjob.server.persistence.storage.impl.MySqlSeriesDfsService;
+import tech.powerjob.server.persistence.storage.impl.*;
/**
* 初始化内置的存储服务
@@ -36,6 +33,12 @@ public class StorageConfiguration {
return new AliOssService();
}
+ @Bean
+ @Conditional(MinioOssService.MinioOssCondition.class)
+ public DFsService initMinioOssFs() {
+ return new MinioOssService();
+ }
+
@Bean
@Conditional(EmptyDFsService.EmptyCondition.class)
public DFsService initEmptyDfs() {
diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/storage/impl/MinioOssService.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/storage/impl/MinioOssService.java
new file mode 100644
index 00000000..fee449ca
--- /dev/null
+++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/storage/impl/MinioOssService.java
@@ -0,0 +1,234 @@
+package tech.powerjob.server.persistence.storage.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import io.minio.*;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.core.env.Environment;
+import tech.powerjob.server.common.spring.condition.PropertyAndOneBeanCondition;
+import tech.powerjob.server.extension.dfs.*;
+import tech.powerjob.server.persistence.storage.AbstractDFsService;
+
+import javax.annotation.Priority;
+import java.nio.file.Files;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * MINIO support
+ * 配置项:
+ * oms.storage.dfs.minio.endpoint
+ * oms.storage.dfs.minio.bucketName
+ * oms.storage.dfs.minio.accessKey
+ * oms.storage.dfs.minio.secretKey
+ *
+ * @author xinyi
+ * @since 2023/8/21
+ */
+@Slf4j
+@Priority(value = Integer.MAX_VALUE - 1)
+@Conditional(MinioOssService.MinioOssCondition.class)
+public class MinioOssService extends AbstractDFsService {
+
+ private static final String TYPE_MINIO = "minio";
+ private static final String KEY_ENDPOINT = "endpoint";
+ private static final String KEY_BUCKET_NAME = "bucketName";
+ private static final String ACCESS_KEY = "accessKey";
+ private static final String SECRET_KEY = "secretKey";
+ private MinioClient minioClient;
+ private String bucket;
+ private static final String NO_SUCH_KEY = "NoSuchKey";
+
+ @Override
+ public void store(StoreRequest storeRequest) {
+ try {
+ minioClient.uploadObject(UploadObjectArgs.builder()
+ .bucket(this.bucket)
+ .object(parseFileName(storeRequest.getFileLocation()))
+ .filename(storeRequest.getLocalFile().getPath())
+ .contentType(Files.probeContentType(storeRequest.getLocalFile().toPath()))
+ .build());
+ } catch (Throwable t) {
+ ExceptionUtils.rethrow(t);
+ }
+ }
+
+ @Override
+ public void download(DownloadRequest downloadRequest) {
+ try {
+ FileUtils.forceMkdirParent(downloadRequest.getTarget());
+ // 下载文件
+ minioClient.downloadObject(
+ DownloadObjectArgs.builder()
+ .bucket(this.bucket)
+ .object(parseFileName(downloadRequest.getFileLocation()))
+ .filename(downloadRequest.getTarget().getAbsolutePath())
+ .build());
+ } catch (Throwable t) {
+ ExceptionUtils.rethrow(t);
+ }
+ }
+
+ /**
+ * 获取文件元
+ *
+ * @param fileLocation 文件位置
+ */
+ @Override
+ public Optional fetchFileMeta(FileLocation fileLocation) {
+ try {
+ StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder()
+ .bucket(this.bucket)
+ .object(parseFileName(fileLocation))
+ .build());
+ return Optional.ofNullable(stat).map(minioStat -> {
+
+ Map metaInfo = Maps.newHashMap();
+ if (stat.userMetadata() != null) {
+ metaInfo.putAll(stat.userMetadata());
+ }
+ return new FileMeta()
+ .setLastModifiedTime(Date.from(stat.lastModified().toInstant()))
+ .setLength(stat.size())
+ .setMetaInfo(metaInfo);
+ });
+ } catch (Exception oe) {
+ String errorCode = oe.getMessage();
+ if (NO_SUCH_KEY.equalsIgnoreCase(errorCode)) {
+ return Optional.empty();
+ }
+ ExceptionUtils.rethrow(oe);
+ }
+ return Optional.empty();
+ }
+
+ private static String parseFileName(FileLocation fileLocation) {
+ return String.format("%s/%s", fileLocation.getBucket(), fileLocation.getName());
+ }
+
+ /**
+ * 清理过期文件
+ *
+ * @param bucket 桶名
+ * @param days 日期
+ */
+ @Override
+ public void cleanExpiredFiles(String bucket, int days) {
+ /*
+ 使用Minio的管理界面或Minio客户端命令行工具设置对象的生命周期规则。在生命周期规则中定义文件的过期时间。Minio将自动根据设置的规则删除过期的文件。
+ */
+ }
+
+ /**
+ * 释放连接
+ */
+ @Override
+ public void destroy() {
+ //minioClient.close();
+ }
+
+ /**
+ * 初始化minio
+ *
+ * @param applicationContext /
+ */
+ @Override
+ protected void init(ApplicationContext applicationContext) {
+ Environment environment = applicationContext.getEnvironment();
+
+ String endpoint = fetchProperty(environment, TYPE_MINIO, KEY_ENDPOINT);
+ String bucketName = fetchProperty(environment, TYPE_MINIO, KEY_BUCKET_NAME);
+ String accessKey = fetchProperty(environment, TYPE_MINIO, ACCESS_KEY);
+ String secretKey = fetchProperty(environment, TYPE_MINIO, SECRET_KEY);
+
+ try {
+ initOssClient(endpoint, bucketName, accessKey, secretKey);
+ } catch (Exception e) {
+ ExceptionUtils.rethrow(e);
+ }
+ }
+
+ /**
+ * 创建minio连接并且创建桶
+ *
+ * @param endpoint 端口
+ * @param bucketName 桶名
+ * @param accessKey 访问密钥
+ * @param secretKey 秘密密钥
+ */
+ public void initOssClient(String endpoint, String bucketName, String accessKey, String secretKey) {
+ log.info("[Minio] init OSS by config: endpoint={}, bucketName={}, accessKey={}, secretKey={}", endpoint, bucketName, accessKey, secretKey);
+ if (StringUtils.isEmpty(bucketName)) {
+ throw new IllegalArgumentException("'oms.storage.dfs.minio.bucketName' can't be empty, please creat a bucket in minio oss console then config it to powerjob");
+ }
+ this.bucket = bucketName;
+ minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
+ createBucket(bucketName);
+ log.info("[Minio] initialize OSS successfully!");
+ }
+
+ /**
+ * 创建 bucket
+ *
+ * @param bucketName 桶名
+ */
+ @SneakyThrows(Exception.class)
+ public void createBucket(String bucketName) {
+ if (!bucketExists(bucketName)) {
+ minioClient.makeBucket(MakeBucketArgs.builder()
+ .bucket(bucketName).build());
+ }
+ String policy = "{\n" +
+ " \"Version\": \"2012-10-17\",\n" +
+ " \"Statement\": [\n" +
+ " {\n" +
+ " \"Action\": [\n" +
+ " \"s3:GetObject\"\n" +
+ " ],\n" +
+ " \"Effect\": \"Allow\",\n" +
+ " \"Principal\": {\n" +
+ " \"AWS\": [\n" +
+ " \"*\"\n" +
+ " ]\n" +
+ " },\n" +
+ " \"Resource\": [\n" +
+ " \"arn:aws:s3:::" + bucketName + "/*\"\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+ minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build());
+ }
+
+ /**
+ * 判断 bucket是否存在
+ *
+ * @param bucketName: 桶名
+ * @return boolean
+ */
+ @SneakyThrows(Exception.class)
+ public boolean bucketExists(String bucketName) {
+ return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
+ }
+
+ public static class MinioOssCondition extends PropertyAndOneBeanCondition {
+
+ @Override
+ protected List anyConfigKey() {
+ return Lists.newArrayList("oms.storage.dfs.minio.endpoint");
+ }
+
+ @Override
+ protected Class> beanType() {
+ return DFsService.class;
+ }
+ }
+}
From 360f105c0146016d7def5e97b59d880148384285 Mon Sep 17 00:00:00 2001
From: tjq
Date: Thu, 31 Aug 2023 23:51:50 +0800
Subject: [PATCH 2/2] docs: add second qq group
---
README_zhCN.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/README_zhCN.md b/README_zhCN.md
index c49ed0ee..41820938 100644
--- a/README_zhCN.md
+++ b/README_zhCN.md
@@ -69,4 +69,6 @@ PowerJob 的设计目标为企业级的分布式任务调度平台,即成为
* 欢迎共同参与本项目的贡献,PR和Issue都大大滴欢迎(求求了)~
* 觉得还不错的话,可以点个Star支持一下哦~ = ̄ω ̄=
* 联系方式@KFCFans -> `tengjiqi@gmail.com`
-* 用户交流QQ群(因广告信息泛滥,加群需要验证,请认真填写申请原因):487453839
\ No newline at end of file
+* 用户交流QQ群(因广告信息泛滥,加群需要验证,请认真填写申请原因):
+ * 一群(已满):487453839
+ * 二群:834937813
\ No newline at end of file