自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Nacos + 適配器 動態(tài)實現(xiàn) OSS 無感切換!

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

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

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

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

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

一、適配器模式改造

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

1. 被適配器類

@Component
publicclass MinioUtil {
    @Resource
    private MinioClient minioClient;

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

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


}

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

/**
 * 為了方便切換任何一個oss,我們將公共方法抽取為接口,由某個oss的實現(xiàn)類去編寫具體邏輯
 */
public interface StorageAdapter {
    /**
     * 創(chuàng)建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)的接口與目標(biāo)抽象類的接口轉(zhuǎn)換起來,使得客戶端可以按照目標(biāo)抽象類的接口進行操作。

/**
 * Minio相關(guān)操作的具體邏輯
 */
@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 具體實現(xiàn)邏輯
 */
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的動態(tài)配置讀取來得到當(dāng)前的storageType。

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

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

@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("為找到對應(yīng)的文件存儲處理器");
        }
    }
}

三、新增FileService防腐

提高可維護性:

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

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

    /**
     * 創(chuàng)建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//根據(jù)名稱注入
    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部署

服務(wù)器需開啟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:賦予容器擴展的特權(quán)

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

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

  • JVM_XMX:最大堆內(nèi)存為 256m
  • JVM_XMS:初始堆內(nèi)存為 256 m

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

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

(6) 8848:Nacos服務(wù)端端口

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

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相關(guān)配置寫入bootstrap.yml文件中,項目啟動后會優(yōu)先讀取。

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

4. 新增配置管理

dataId:jc-club-oss-dev.yaml 服務(wù)名稱+開發(fā)環(huán)境.yaml。

配置內(nèi)容:

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

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

  • 在@Value注入的變量所在類上添加注解@RefreshScop,當(dāng)配置文件內(nèi)容發(fā)生變化后會重新讀取
  • 當(dāng)文件更新后,Bean已加入到了IOC容器,即使storageType屬性值變了,Bean也無法重新加載。
  • 所以在@Bean方法上也要加入@RefreshScop注解,當(dāng)文件更新后,帶有此注解的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("為找到對應(yīng)的文件存儲處理器");
        }
    }
}

6. 測試

(1) type為阿里云

結(jié)果為:成功返回aliyun

(2) 修改屬性為minio

結(jié)果為:圖片成功上傳。

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

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]
責(zé)任編輯:趙寧寧 來源: 碼猿技術(shù)專欄
相關(guān)推薦

2022-02-18 17:21:29

適配器模式客戶端

2020-10-25 08:56:21

適配器模式

2012-09-19 15:29:26

Worklight適配器

2012-05-16 17:22:11

Java設(shè)計模式

2009-11-18 18:08:20

PHP適配器模式

2009-12-21 10:26:09

Oracle適配器

2018-10-11 10:38:31

前端JavaScript編程語言

2024-04-10 12:27:43

Python設(shè)計模式開發(fā)

2013-11-26 16:39:21

Android設(shè)計模式

2021-02-18 08:39:28

設(shè)計模式場景

2015-08-07 10:05:37

recyclervie超省寫法

2011-04-28 09:54:50

jQuery

2014-12-17 09:57:01

AndroidAdapteViewHolder

2013-02-26 10:55:47

C#適配器設(shè)計模式

2021-02-16 08:16:09

適配器模式MybatisJava

2022-02-13 23:33:24

設(shè)計模式Java

2021-08-06 06:51:16

適配器配置Spring

2014-07-17 10:55:10

Win8.1應(yīng)用開發(fā)適配器模式

2013-12-10 09:44:00

網(wǎng)絡(luò)適配器卸載

2010-07-09 12:53:30

HART協(xié)議
點贊
收藏

51CTO技術(shù)棧公眾號