成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Nacos + 適配器 動態實現 OSS 無感切換!

開發
在一個微服務項目里,倘若我們要修改具體使用的云存儲廠商,就會致使 controller 層和 service 層發生變動,這并不符合低耦合的理念。在這種情況下,我們完全可以采用適配器模式來開展項目開發!

在一個微服務項目里,我們的 OSS 云存儲服務常常需要配置諸如阿里云、騰訊云、minio 等多個云存儲廠商的業務代碼,而且后續無法確保是否會增添新的云存儲廠商。

此時,倘若我們要修改具體使用的云存儲廠商,就會致使 controller 層和 service 層發生變動,這并不符合低耦合的理念。

在這種情況下,我們完全可以采用適配器模式來開展項目開發!

之前也介紹過另外一種封裝,看陳某之前的文章:《企業級的OSS對象存儲服務,這樣封裝萬能好用!

一、適配器模式改造

MinioUtils和AliyunUtils被適配者類作為源接口執行原子性操作的具體邏輯各不相同,想要把多個OSS共用一個相同的接口返回,就需要使用到適配器模式。

1. 被適配器類

@Component
publicclass MinioUtil {
    @Resource
    private MinioClient minioClient;

    /**
     * 創建Bucket桶(文件夾目錄)
     */
    public void createBucket(String bucket) throws Exception {
        boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        if(!exists) { //不存在創建
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
    }

    /**
     * 上傳文件
     * inputStream:處理文件的輸入流
     * bucket:桶名稱
     * objectName:桶中的對象名稱,也就是上傳后的文件在存儲桶中的存儲路徑和文件名。
     * stream(inputStream:處理文件的輸入流,-1:指定緩沖區大小的參數[-1為默認大小], 5242889L:指定文件內容長度的上限)
     */
    public void uploadFile(InputStream inputStream, String bucket, String objectName) throws Exception {
        minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(objectName)
                .stream(inputStream, -1, 5242889L).build());
    }


}

這是目標接口 **(目標抽象類,即客戶需要的方法)**,我們想要的不同OSS都可通過該接口進行操作:

/**
 * 為了方便切換任何一個oss,我們將公共方法抽取為接口,由某個oss的實現類去編寫具體邏輯
 */
public interface StorageAdapter {
    /**
     * 創建bucket
     * @param bucket
     */
    void createBucket(String bucket);

    /**
     * 上傳文件
     * @param multipartFile
     * @param bucket
     * @param objectName
     */
    void uploadFile(MultipartFile multipartFile, String bucket, String objectName);

    /**
     * 獲取文件在oss中的url
     * @param bucket
     * @param objectName
     * @return
     */
    String getUrl(String bucket, String objectName);

}

2. Minio適配器類

通過繼承或者組合方式,將被適配者類(minioUtils)的接口與目標抽象類的接口轉換起來,使得客戶端可以按照目標抽象類的接口進行操作。

/**
 * Minio相關操作的具體邏輯
 */
@Log4j2
publicclass MinioStorageAdapter implements StorageAdapter {

    @Resource
    private MinioUtil minioUtil;

    @Value("${minio.url}")
    private String url;

    @Override
    @SneakyThrows//Lombok中的注解 會在編譯期補上異常處理
    public void createBucket(String bucket) {
        minioUtil.createBucket(bucket);
    }

    /**
     * 上傳文件
     * @param multipartFile
     * @param bucket
     * @param objectName 為空,文件路徑為根目錄;不為空,文件路徑為objectName目錄下
     */
    @Override
    @SneakyThrows
    public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
        minioUtil.createBucket(bucket);
        if(objectName != null) {
            minioUtil.uploadFile(multipartFile.getInputStream(), bucket, objectName + "/" + multipartFile.getOriginalFilename());
        } else {
            minioUtil.uploadFile(multipartFile.getInputStream(), bucket, multipartFile.getOriginalFilename());
        }
    }

    /**
     * 獲取文件在oss中的url
     * @param bucket
     * @param objectName
     * @return
     */
    @Override
    public String getUrl(String bucket, String objectName) {
        return url + "/" + bucket + "/" + objectName;
    }


}

3. Aliyun適配器類

/**
 * 阿里云oss 具體實現邏輯
 */
publicclass AliStorageAdapter implements StorageAdapter {

    @Override
    public void createBucket(String bucket) {
        System.out.println("aliyun");
    }

    @Override
    public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {

    }

    @Override
    public String getUrl(String bucket, String objectName) {
        return"aliyun";
    }

}

二、定義StorageConfig類來獲取指定的文件適配器

通過Nacos的動態配置讀取來得到當前的storageType。

此時如果想再加入一個新的OSS對象(得到xxUtils jar包等,我們無法進行修改),只需新增一個xxadapter適配器類且在@Bean注解的方法中加一個else即可。

注意:這里直接使用new的方式創建實現類(實現類也不需要使用@Service注解),而不是先把所有的實現類通過注解定義出來,再直接返回對象,這樣如果新增一個OSS的話,不光要加else,還需再把實現類通過直接定義出來。

@Configuration
publicclass StorageConfig {
    @Value("${storage.service.type}")
    private String storageType;
    @Bean
    public StorageAdapter storageAdapter() {
        if("minio".equals(storageType)) {
            returnnew MinioStorageAdapter();
        } elseif("aliyun".equals(storageType)) {
            returnnew AliStorageAdapter();
        } else {
            thrownew IllegalArgumentException("為找到對應的文件存儲處理器");
        }
    }
}

三、新增FileService防腐

提高可維護性:

/**
 * FileService防腐層 
 使用fileService(相當于domain防腐層)與adapter(相當于service層只做原子性操作)進行交互、Utils相當于dao層
 */
@Component
publicclass FileService {
    /**
     * 通過構造函數注入
     */
    privatefinal StorageAdapter storageAdapter;

    public FileService(StorageAdapter storageAdapter) {
        this.storageAdapter = storageAdapter;
    }

    /**
     * 創建bucket
     * @param bucket
     */
    public void createBucket(String bucket) {
        storageAdapter.createBucket(bucket);
    }

    /**
     * 上傳圖片、返回圖片在minio的地址
     * @param multipartFile
     * @param bucket
     * @param objectName
     */
    public String uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
        storageAdapter.uploadFile(multipartFile, bucket, objectName);
        objectName = (StringUtils.isEmpty(objectName) ? "" : objectName + "/") + multipartFile.getOriginalFilename();
        return storageAdapter.getUrl(bucket, objectName);
    }
}

四、Controller層

Controller層通過注入FileService來進行操作:

@RestController
@Log4j2
publicclass FileController {
    @Resource//根據名稱注入
    private FileService fileService;

    /**
     * 上傳文件, 返回文件在oss中的地址
     * @param uploadFile:文件, getOriginalFilename獲取原始文件名
     * @param bucket:桶名稱
     * @param objectName:上傳后的文件在存儲桶中的存儲路徑(存儲目錄)
     * @return String: 返回文件在minio的鏈接地址
     */
    @PostMapping("/upload")
    public Result<String> upload(MultipartFile uploadFile, String bucket, String objectName) throws Exception {
        try {
            Preconditions.checkArgument(!ObjectUtils.isEmpty(uploadFile), "文件不能為空");
            Preconditions.checkArgument(!StringUtils.isEmpty(bucket), "bucket桶名稱不能為空");
            if(log.isInfoEnabled()) {
                log.info("FileController.upload.uploadFile:{}, bucket:{}, objectName:{}", uploadFile.getOriginalFilename(), bucket, objectName);
            }
            String url = fileService.uploadFile(uploadFile, bucket, objectName);
            return Result.ok(url);
        } catch (Exception e) {
            log.info("FileController.upload.error:{}", e.getMessage(), e);
            return Result.fail("上傳文件失敗");
        }
    }

}

五、Nacos搭建

1. Nacos部署

服務器需開啟8848、9848端口:

docker search nacos
docker pull nacos/nacos-server
# 鏡像拉完之后,啟動腳本
docker run -d \
  --name nacos \
  --privileged  \
  --cgroupns host \
  --env JVM_XMX=256m \
 --env MODE=standalone \
  --env JVM_XMS=256m \
  -p 8848:8848/tcp \
  -p 9848:9848/tcp \
  --restart=always \
  -w /home/nacos \
  nacos/nacos-server

(1) privileged:賦予容器擴展的特權

(2) cgroupns host:讓容器使用宿主機的 cgroup 命名空間(在資源限制方面容器會遵循宿主機規則)

(3) env:設置Nacos服務使用的jvm參數

  • JVM_XMX:最大堆內存為 256m
  • JVM_XMS:初始堆內存為 256 m

(4) env MODE=standalone:nacos運行模式為單機模式

(5) w /home/nacos:指定容器內的工作目錄為 “/home/nacos”,容器內執行的命令如果涉及到相對路徑的操作,就會以這個目錄作為當前工作目錄的基準。

(6) 8848:Nacos服務端端口

(7) 9848:客戶端gRPC請求服務端端口

2. 引入nacos客戶依賴

除了引入nacos依賴,還要引入log4j2依賴,來輸出nacos日志信息。

SpringCloudAlibaba 版本為2.2.6.RELEASE時,springboot版本要為2.3.8.RELEASE:

<!--nacos依賴(配合日志,打印nacos信息)-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <version>2.4.2</version>
</dependency>

3. 編寫配置文件

把nacos相關配置寫入bootstrap.yml文件中,項目啟動后會優先讀取。

spring:
  application:
    name:jc-club-oss#微服務名稱
profiles:
    active:dev#指定環境為開發環境
cloud:
    nacos:
      server-addr:117.72.118.73:8848
      config:
        file-extension:yaml#文件后綴名

4. 新增配置管理

dataId:jc-club-oss-dev.yaml 服務名稱+開發環境.yaml。

配置內容:

這時spring會根據bootstrap.yml文件中的${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作為文件id,來讀取配置。

5. 添加@RefreshScope注解開啟熱更新

  • 在@Value注入的變量所在類上添加注解@RefreshScop,當配置文件內容發生變化后會重新讀取
  • 當文件更新后,Bean已加入到了IOC容器,即使storageType屬性值變了,Bean也無法重新加載。
  • 所以在@Bean方法上也要加入@RefreshScop注解,當文件更新后,帶有此注解的Bean能夠自動重新初始化
@Configuration
@RefreshScope
publicclass StorageConfig {

    @Value("${storage.service.type}")
    private String storageType;
    
    @Bean
    @RefreshScope
    public StorageAdapter storageAdapter() {
        if("minio".equals(storageType)) {
            returnnew MinioStorageAdapter();
        } elseif("aliyun".equals(storageType)) {
            returnnew AliStorageAdapter();
        } else {
            thrownew IllegalArgumentException("為找到對應的文件存儲處理器");
        }
    }
}

6. 測試

(1) type為阿里云

結果為:成功返回aliyun

(2) 修改屬性為minio

結果為:圖片成功上傳。

在配置文件更新時,nacos也會打印出對應的日志提示:

2024-12-03 17:05:50.719  INFO 35932 --- [.72.118.73_8848] o.s.c.e.e.RefreshEventListener           : Refresh keys changed: [storage.service.type]
責任編輯:趙寧寧 來源: 碼猿技術專欄
相關推薦

2012-09-19 15:29:26

Worklight適配器

2020-10-25 08:56:21

適配器模式

2022-02-18 17:21:29

適配器模式客戶端

2024-04-10 12:27:43

Python設計模式開發

2015-08-07 10:05:37

recyclervie超省寫法

2021-02-18 08:39:28

設計模式場景

2013-11-26 16:39:21

Android設計模式

2009-11-18 18:08:20

PHP適配器模式

2009-12-21 10:26:09

Oracle適配器

2012-05-16 17:22:11

Java設計模式

2018-10-11 10:38:31

前端JavaScript編程語言

2022-02-13 23:33:24

設計模式Java

2021-08-06 06:51:16

適配器配置Spring

2021-02-16 08:16:09

適配器模式MybatisJava

2011-04-28 09:54:50

jQuery

2013-02-26 10:55:47

C#適配器設計模式

2014-12-17 09:57:01

AndroidAdapteViewHolder

2014-07-17 10:55:10

Win8.1應用開發適配器模式

2024-02-22 12:13:49

適配器模式代碼

2012-08-02 10:46:34

JavaAdapter模式
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品国产乱码久久久久久影片 | 紧缚调教一区二区三区视频 | 亚洲国产二区 | www.日韩系列 | 四虎影视一区二区 | 国产一区高清 | 欧美福利 | 国内自拍偷拍一区 | 成人免费视频在线观看 | av午夜激情| 精品久久久久久久久久久久久久久久久 | 欧美日韩视频在线 | 日韩久久在线 | av毛片 | 一区二区视频在线 | 亚洲免费精品一区 | 91在线看片 | 亚洲国产欧美国产综合一区 | 精品网站999www| 国产精品福利网站 | 福利视频日韩 | 成人黄色a | 精品成人av| 亚洲精品一区二区三区四区高清 | 久久国产精品一区二区三区 | 国产一区免费 | 国产精品国产精品 | 中文字幕一区二区三区在线观看 | 欧美激情啪啪 | 国产成人免费在线 | 亚洲人成网站777色婷婷 | 久久精品国产一区 | 99精品视频免费观看 | 国产精品国产馆在线真实露脸 | 欧美综合久久久 | 国产一区欧美 | 91资源在线 | 91精品久久久久久久久久小网站 | 国产精品网址 | 日韩二 | 亚洲精品9999 |