1. 程式人生 > >Spring Cloud下使用Feign Form實現微服務之間的檔案上傳

Spring Cloud下使用Feign Form實現微服務之間的檔案上傳

背景

​ Spring Cloud現在已經被越來越多的公司採用了,微服務架構比傳統意義上的單服務架構從複雜度上多了很多,出現了很多複雜的場景。比如,我們的產品是個app,支援第三方登入功能,在手機端呼叫第三方授權介面之後,返回了使用者的相關資訊,比如open_id,性別,頭像等。這些資訊我們需要儲存在我們伺服器上,當時針對頭像是應該儲存圖片的url還是圖片本身發生了歧義,在一番討論之後,得出的結果是,我們需要通過url將圖片下載到我們本地,然後呼叫我們自己的檔案微服務中上傳功能儲存起來。

​ 跨服務之間呼叫,我們採用的是Feign元件,原生的Feign元件並不支援檔案上傳,但是如果添加了Feign-Form模組,那麼就能上傳檔案,下面我通過一篇文章來講述如何通過Feign上傳檔案,程式碼已經上傳github地址。

說明

個人部落格首發: https://Shiyajian.github.io

github專案地址:https://github.com/Shiyajian/examples ,請找spring-cloud/chapter1

本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!

工具

  • IDE :IntelliJ IDEA
  • JDK : jdk 8
  • 構建工具:Gradle 4.10.2
  • Spring Cloud 版本:Finchley.SR2 (截止2018-11-25最新的GA版本,基於boot 2.0.6)
  • Spring Boot 版本:2.0.6.RELEASE (截止2018-11-25最新為2.1.0.RELEASE)

此處採用Gradle而沒有使用Maven作為依賴構建和管理的工具,主要原因是我們公司目前使用的是Gradle,而且從編譯速度,程式碼可讀性和清晰度上都遠遠優於Maven。

專案結構

​ 本專案分為三個角色,分別如下:

  • eureka-server : 註冊中心
  • provider-server: 服務提供者,此處模擬一個檔案伺服器,提供檔案上傳功能
  • consumer-server: 服務消費者,此處模擬一個業務服務,需要呼叫檔案上傳服務

大致的依賴圖如下:

配置並執行

​ 我們首先通過執行感受一下通過Feign上傳檔案的流程,在整個專案可以完整執行後,我們再參考文章和程式碼一起分析其中設定,並將其應用到自己的應用中

  • 首先clone專案到本地

    git clone https://github.com/Shiyajian/examples.git
  • 安裝並配置Gradle
  • 將專案匯入到IDEA中
  • 確認IDEA支援Lombok外掛,預設IDEA都支援的,此步驟可忽略
  • 更改IDEA設定,Project Settings(Mac中為Preferences)-> Compiler -> Annoatation Processors -> [√] Enable annotation processing
  • 重新整理Gradle,下載依賴並編譯
  • 啟動註冊中心
    • 找到 examples/spring-cloud/eureka-server中的EurekaApplication,執行main方法
    • 開啟瀏覽器,執行:http://localhost:8761/,能開啟證明成功
  • 啟動Provider專案
    • 找到 examples/spring-cloud/chapter1/provider/provider-service中的ProviderApplication,執行main方法
    • 重新整理註冊中心頁面,找到服務證明成功
  • 執行Consumer專案中的測試
    • 開啟examples/spring-cloud/chapter1/consumer/consumer-server/src/test目錄
    • 修改com.shiyajian.examples.consumer.service.impl.ConsumerServiceImplTest類中檔案的路徑為本機電腦上存在的檔案
    • 執行測試方法
    • 方法綠燈結束,在控制檯能找到輸出為成功

Provider 服務配置說明

Provider服務為上傳服務的提供者,這裡模擬的是一個檔案伺服器,通過上面圖,我們可以看到專案分為2部分,下面就進行詳細解讀:

  • provider-api

    這個專案最終打成一個可以被引用的jar包,consumer-server通過引用這個jar包可以通過注入方式引用其中的方法,provider-server也需要引用這個jar包,然後實現其中的邏輯,供consumer-server遠端呼叫。配置api的方法如下:

    • 新增org.springframework.cloud:spring-cloud-starter-openfeign依賴,只需要這一個依賴就夠了,裡面儲存Fegin-Form等依賴。

    • 編寫配置類FeignMultipartSupportConfig.java

      public class FeignMultipartSupportConfig {
      
          @Bean
          @Primary
          @Scope("prototype")
          public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
              return new FeignSpringFormEncoder(new SpringEncoder(messageConverters));
          }
      
      }
    • 編寫自定義的Encoder,因為這個有個設計得BUG,本身可以解析檔案陣列,但是程式碼缺少對應的判斷,此處參考文章:https://blog.csdn.net/tony_lu229/article/details/73823757,程式碼不貼了,詳細見工程

    • 定義自己的介面,這裡我定義的是ProviderClient,程式碼簡單如下:

      @FeignClient(value = "provider-server", configuration = FeignMultipartSupportConfig.class)
      public interface ProviderClient { 
      
          @PostMapping(value = "client/upload/{id}", consumes = MULTIPART_FORM_DATA_VALUE)
          String uploadFile(@RequestPart("file") MultipartFile file,
                            @PathVariable("id") String id,
                            @RequestParam("name") String name);
      
          @PostMapping(value = "client/uploads", consumes = MULTIPART_FORM_DATA_VALUE)
          List<ProviderResponse> uploadFiles(@RequestPart("files") MultipartFile[] files, @RequestParam("author") String author);
      
      }

      這個介面定義時候需要有以下注意的幾點:

      • @FeignClient中的value,對應的是服務實現類在eureka中註冊的名字,也就是spring.application.name的值
      • configuration必須配置,就是咱們上面新增的兩個類,用來編解碼使用
      • 方法可以使用類似Controller中的一些註解,比如方法上可以加@RequestMapping,@PostMapping等,類上面不可以加,我試的時候,在class上加了@RequestMapping之後報錯,專案啟動時候顯示Url報錯,其實,也完全不需要加
      • 接受檔案的時候,必須是@RequestPart註解,我曾經看有文章說,@RequestPart和@RequestParam通用,但是我自己測試並不是這樣
      • consumes對應請求的contentType,必須為:multipart/form-data,此處使用了靜態導包。
      • 在傳統Controller中,我本身會經常簡寫@RequestParam,忽略他的value欄位。但是Feign介面中不行,如果這些註解沒有括號中的value那麼就會報錯
      • 不支援@RequestBody註解
    • provider-server

      這個專案是最後實際提供服務的專案,所以必須實現provider-api介面中的方法,並且註冊到eureka服務中。

      • 新增對feign的依賴,新增api專案的依賴,其他依賴略

        compile project(":provider-api")
        "org.springframework.cloud:spring-cloud-starter-feign:$feignVersion"
      • 實現provider-api中ProviderClient介面,生成實現類,並編寫業務程式碼,需要注意兩點

        • 因為父級已經在方法上增加了@PostMapping,此處可以省略
        • 如果是通過IDEA快生成的實現類,那麼引數前面的@RequestPart、@RequestParam的註解需要加上,不然報錯
    • consumer-server

      這個專案是消費對方提供服務的專案,需要做的也比較簡單。

      • 新增provider-api的專案依賴,正式環境下,兩個專案可能是不同組開發的,所以需要引入jar包,而不是直接編譯此工程,這裡僅做展示使用

        compile project(":provider-api")
      • 在啟動類上增加註解,掃描新增Feign功能對應的包

        @SpringCloudApplication
        // 這個註解非常重要,不然引用不到client中的方法
        @EnableFeignClients("com.shiyajian.examples.provider")
        public class ConsumerApplication {
            public static void main(String[] args) {
                SpringApplication.run(ConsumerApplication.class);
            }
        }
      • 在需要的地方通過@Autowird方式注入,然後就可以進行呼叫了

        @Autowired
        ProviderClient providerClient;
        ……
        providerClient.dosomething();
        ……

    總結

    ​ 整個通過Feign-Form上傳檔案的案例就寫完了,第一次寫部落格,寫的不好還望見諒,如果文章解釋的不夠清楚,可以參考我的專案中的程式碼,程式碼上可能會更清晰點,程式碼我已經測試通過的,可以放心使用。文章中如果有寫錯誤的地方還望各位指正,當然,如果有什麼好的建議也可以給我評論和留言,如果你還其他關於java方面的教程和示例程式碼你也可以告訴我,我如果不忙的時候,我就會寫出來。

    意外

    ​ 在發文章之前又做了一次測試,這次測試沒有通過,通過調查發現,Eureka中專案的註冊地址變成了:MacBook-Pro.local:provider-server:8100,然後呼叫時候就發生url錯誤,請求fe80:0:0:0:***:8100這個地址,等重新聯網之後再次啟動,註冊地址就變成 192.168.1.101這種地址。

      文章釋出在github上沒有問題,在園子裡面出現了格式BUG,調了好長時間沒調好, 就先這樣將就著看吧。猜測原因是小標題後面帶個程式碼塊樣式就被頂跑了,但是不知道怎麼處理,剛開始用Markdown,以後再研究吧,見諒見諒。

    其他

    ​ QQ群:757696438是我的個人好友群,目前也就30來個人,主要就是吹牛侃大山,順便學習技術共同進步。歡迎各種浪的飛起、悶騷到爆的同志來玩,但是不歡迎裝逼的。