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

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

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

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

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

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

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

一、適配器模式改造

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

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:桶中的對(duì)象名稱,也就是上傳后的文件在存儲(chǔ)桶中的存儲(chǔ)路徑和文件名。
     * stream(inputStream:處理文件的輸入流,-1:指定緩沖區(qū)大小的參數(shù)[-1為默認(rèn)大小], 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都可通過該接口進(jìn)行操作:

/**
 * 為了方便切換任何一個(gè)oss,我們將公共方法抽取為接口,由某個(gè)oss的實(shí)現(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)抽象類的接口進(jìn)行操作。

/**
 * Minio相關(guān)操作的具體邏輯
 */
@Log4j2
publicclass MinioStorageAdapter implements StorageAdapter {

    @Resource
    private MinioUtil minioUtil;

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

    @Override
    @SneakyThrows//Lombok中的注解 會(huì)在編譯期補(bǔ)上異常處理
    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 具體實(shí)現(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的動(dòng)態(tài)配置讀取來得到當(dāng)前的storageType。

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

注意:這里直接使用new的方式創(chuàng)建實(shí)現(xiàn)類(實(shí)現(xiàn)類也不需要使用@Service注解),而不是先把所有的實(shí)現(xiàn)類通過注解定義出來,再直接返回對(duì)象,這樣如果新增一個(gè)OSS的話,不光要加else,還需再把實(shí)現(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("為找到對(duì)應(yīng)的文件存儲(chǔ)處理器");
        }
    }
}

三、新增FileService防腐

提高可維護(hù)性:

/**
 * FileService防腐層 
 使用fileService(相當(dāng)于domain防腐層)與adapter(相當(dāng)于service層只做原子性操作)進(jìn)行交互、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來進(jìn)行操作:

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

    /**
     * 上傳文件, 返回文件在oss中的地址
     * @param uploadFile:文件, getOriginalFilename獲取原始文件名
     * @param bucket:桶名稱
     * @param objectName:上傳后的文件在存儲(chǔ)桶中的存儲(chǔ)路徑(存儲(chǔ)目錄)
     * @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
# 鏡像拉完之后,啟動(dòng)腳本
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:賦予容器擴(kuò)展的特權(quán)

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

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

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

(4) env MODE=standalone:nacos運(yùn)行模式為單機(jī)模式

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

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

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

2. 引入nacos客戶依賴

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

SpringCloudAlibaba 版本為2.2.6.RELEASE時(shí),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文件中,項(xiàng)目啟動(dòng)后會(huì)優(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)容:

這時(shí)spring會(huì)根據(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ā)生變化后會(huì)重新讀取
  • 當(dāng)文件更新后,Bean已加入到了IOC容器,即使storageType屬性值變了,Bean也無法重新加載。
  • 所以在@Bean方法上也要加入@RefreshScop注解,當(dāng)文件更新后,帶有此注解的Bean能夠自動(dòng)重新初始化
@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("為找到對(duì)應(yīng)的文件存儲(chǔ)處理器");
        }
    }
}

6. 測(cè)試

(1) type為阿里云

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

(2) 修改屬性為minio

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

在配置文件更新時(shí),nacos也會(huì)打印出對(duì)應(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)推薦

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設(shè)計(jì)模式開發(fā)

2015-08-07 10:05:37

recyclervie超省寫法

2013-11-26 16:39:21

Android設(shè)計(jì)模式

2021-02-18 08:39:28

設(shè)計(jì)模式場(chǎng)景

2009-11-18 18:08:20

PHP適配器模式

2009-12-21 10:26:09

Oracle適配器

2012-05-16 17:22:11

Java設(shè)計(jì)模式

2018-10-11 10:38:31

前端JavaScript編程語言

2022-02-13 23:33:24

設(shè)計(jì)模式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#適配器設(shè)計(jì)模式

2014-12-17 09:57:01

AndroidAdapteViewHolder

2014-07-17 10:55:10

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

2024-02-22 12:13:49

適配器模式代碼

2012-08-02 10:46:34

JavaAdapter模式
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 国产高清精品一区二区三区 | 国产欧美日韩在线观看 | 精品一区二区三区免费视频 | 精品亚洲91| 日韩 国产 在线 | 在线视频a | 国产午夜视频 | 97成人在线 | www.成人.com| 九一在线 | 欧美激情久久久 | 成人精品国产一区二区4080 | 国产精品视频在线观看 | www.蜜桃av | 精品一区二区三区在线视频 | 国产精品福利一区二区三区 | 成人免费视频观看 | www天天操| 特级黄色毛片 | 操操操av | 久久国产精99精产国高潮 | www.99久久.com | 久久久www成人免费无遮挡大片 | 国产精品99久久久久久宅男 | 欧美一区二区三区 | 暖暖日本在线视频 | 波多野结衣先锋影音 | 国产99视频精品免费播放照片 | 日韩成人精品一区 | 日韩伦理一区二区 | 91久久久久 | 国产精品视频免费观看 | 成人在线小视频 | 亚洲成人综合社区 | 亚洲精品久久国产高清情趣图文 | 日韩电影在线一区 | 欧美一级免费 | 亚洲一区二区三区 | 国产高清视频在线观看 | 99热在这里只有精品 | 韩日一区二区 |