前言
今天和大家聊一下Spring Cloud微服務下服務介面除錯及管理的話題!我們知道在微服務架構下,軟體系統會被拆分成很多個獨立執行的服務,而這些服務間需要互動通訊,就需要定義各種各樣的服務介面。具體來說,在基於Spring Cloud的微服務模式中,各個微服務會基於Spring MVC的Controller定義多個該微服務需要向外部發布的介面。
根據各個微服務功能邊界定義的不同,有些微服務會提供與具體業務相關的介面,如支付介面、賬戶介面等;而有些微服務則會提供一些公共性質的服務介面,如簡訊介面、統一認證介面之類。而這些微服務往往又是由多個不同的團隊在開發維護,傳統方式下服務介面的定義往往需要服務提供方提供良好的、可閱讀性比較高的介面文件才可以比較方便地對接和測試,而事實上,隨著時間的推移、人員的迭代更新,很多情況下這些早期的介面文件往往很快就會因為無人維護而過時,即便不過時,微服務模式下這樣的方式也會因為服務介面文件太多而讓開發人員顯得抓狂!
那麼有沒有一種更便捷地方式,可以讓開發介面的同時,自動就能生成與服務介面高度一致的文件來呢?答案是有的,接下來就和大家一起聊聊到底有什麼樣方式可以讓微服務的介面管理變得更加容易些!
介面管理方式介紹
事實上,市面上已經有多種開源專案提供了這樣的支援!如:Swagger、ApiDoc、RAP、DOCLever、CrapApi等,這些專案都提供了對於Api線上文件的管理功能,那麼在Spring Cloud體系中哪一種方式更加適合呢?下面,我們一起來對比下這些專案的優缺點。
Swagger
Swagger是一款基於YAML、JSON語言的文件線上生成和程式碼自動生成的工具。它的優點如下:
1)、它可以直接嵌入在Spring Boot專案中,通過開發時編寫註釋,從而自動生成介面文件,實現程式碼與文件的高度一致; 2)、可以分析介面的結構,並且還可以通過發起請求來驗證介面的正確性; 3)、它提供了多種程式語言的前後端分離解決方案,支援根據定義的介面匯出各種語言的服務端或客戶端程式碼 ; 4)、它還包括了Swagger Editor,這是使用yaml語言的Swagger API的編輯器,支援匯出yaml和json格式的介面檔案; 5)、包含了Swagger UI,它可以將Swagger Editor編輯好的介面文件以html的形式展示出來; 6)、免費開源,支援國際化,生態豐富、社群活躍;
它的缺點是:
1)、對程式碼有侵入性; 2)、不同專案的Swagger介面文件是分離的,需要到不同的地方去找; 3)、Swagger UI展現出來的介面文件缺乏分類,使用體驗比較差; 4)、不同專案的介面文件沒有許可權管理,缺少Mock;
ApiDoc
ApiDoc是一款輕量級的類似於Swagger的線上文件生成工具。其缺點也類似於Swagger,介面管理、自動測試等功能也比較弱,並且其社群、生態國際化方面都還不如Swagger。
RAP
RAP是一個視覺化介面管理工具,它可以通過分析介面結構,動態生成模擬資料,校驗真實介面正確性,圍繞介面定義,通過一系列自動化工具提升微服務模式下的協作效率。
它的優點如下:
1)、支援專案管理、團隊管理、文件版本管理; 2)、支援Mock測試資料; 3)、阿里大廠出品,在阿里巴巴內部得到實踐; 4)、支援介面檢索; 5)、可以分析介面結構,發起請求校驗介面的正確性; 6)、免費開源
缺點如下:
1)、文件和介面分離,很容易出現不一致的現象; 2)、每個介面都需要手工編輯; 3)、後端採用nodejs編寫,與基於Java的Spring Cloud技術棧不一致;
DOCLever
DOCLever也是一個免費開源的介面管理工具,它的優點如下:
1)、支援專案管理、團隊管理、文件工具豐富; 2)、支援豐富的Mock測試資料; 3)、使用者案例也比較豐富:滴滴、美團、58同城、同城旅遊等; 4)、支援介面檢索; 5)、可以分析介面的結構、發起請求校驗介面正確性、引數也很豐富; 6)、支援複雜場景的自動化測試,比如獲取驗證碼、登陸,獲取訂單列表,甚至獲取某個特定訂單詳情等上下文關聯的操作;
缺點如下:
1)、文件和介面分離,很容易出現不一致的現象; 2)、每個介面都需要手工編輯; 3)、後端也是採用nodejs編寫,與Spring Cloud的Java技術棧不符;
CrapApi
優點如下:
1)、支援專案管理、團隊管理、文件版本管理; 2)、支援Mock測試資料; 3)、支援介面檢索; 4)、可以分析介面結構、發起請求校驗介面的正確性; 5)、支援介面監控、設定報警規則,介面不可用時及時通知服務負責人; 6)、後端基於Java開發,與Spring Cloud技術棧Java匹配; 7)、免費開源;
缺點:
1)、文件和介面分離,很容易出現不一致的現象; 2)、每個介面都需要手工編輯; 3)、使用案例較少,功能不夠完善,可能會有很多坑;
Spring Cloud整合Swagger
通過對上述開源專案的分析,除Swagger外其餘專案採取的都是文件和程式碼分離的方式,雖然這樣會減少對程式碼的侵入,但是也會造成文件和程式碼的不一致現象,所以在基於Spring Cloud的微服務專案中,我們選擇Swagger作為微服務介面管理工具。
那麼基於Spring Boot的Spring Cloud微服務該如何整合Swagger呢?
1)、在Spring Boot微服務專案中引入Maven依賴:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
複製程式碼
2)、建立Swagger2配置類:
@Configuration
@EnableSwagger2
@Profile("!production")
public class SwaggerConfiguration {
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("Api")
.select()
.apis(withClassAnnotation(RestController.class))
.build()
.globalOperationParameters(commonParameters())
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Api")
.version("1.0.0-SNAPSHOT")
.build();
}
private List<Parameter> commonParameters() {
List<Parameter> parameters = Lists.newArrayList();
parameters.add(new ParameterBuilder()
.name("war")
.description("backdoor 在測試環境繞過鑑權")
.modelRef(new ModelRef("string"))
.parameterType("query")
.required(false)
.defaultValue("war123")
.build());
parameters.add(new ParameterBuilder()
.name("uid")
.description("backdoor 設定的使用者ID")
.modelRef(new ModelRef("string"))
.parameterType("query")
.required(false)
.defaultValue("1000053")
.build());
parameters.add(new ParameterBuilder()
.name("Authorization")
.description("生產環境中,需要傳遞的使用者當前 Token")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.defaultValue("Bearer XXXXX")
.build());
return parameters;
}
}
複製程式碼
以上配置類中,我們可以通過@Profile("!production")註解指定生效的環境,如這裡我們設定除在生產環境外,其他環境都生效。
3)、新增文件內容
在完成上述配置後,其實已經可以生產文件內容了,但是這樣的文件主要針對請求本身,描述的主要來源是函式的命名,多使用者並不友好,為了讓文件更加易於閱讀和理解,我們可以通過Swagger註解來增加一些說明。這些註解主要有:
@Api:用在類上,說明該類的作用。 @ApiOperation:註解來給API增加方法說明。 @ApiImplicitParams : 用在方法上包含一組引數說明。 @ApiImplicitParam:用來註解來給方法入參增加說明。 @ApiResponses:用於表示一組響應。 @ApiResponse:用在@ApiResponses中,一般用於表達一個錯誤的響應資訊。 @ApiModel:描述一個Model的資訊(一般用在請求引數無法使用@ApiImplicitParam註解進行描述的時候)。
接下來,我們通過一個實際的微服務介面定義案例來演示下:
@Api(value = "運維端系統使用者層外部介面", description = "用於組裝直接面向運維App端相關服務")
@RestController
@Slf4j
public class OperationUserController {
@Autowired
OperationUserService operationUserServiceImpl;
@ApiOperation(value = "運維端使用者註冊", httpMethod = "POST")
@RequestMapping(value = "/userRegister", method = RequestMethod.POST)
public APIResponse sysUserRegister(@RequestParam(value = "mobileNo") String mobileNo,
@RequestParam(value = "email") String email,
@RequestParam(value = "nickName", required = false) String nickName,
@RequestParam(value = "idName") String idName, @RequestParam(value = "idType") String idType,
@RequestParam(value = "idNo") String idNo, @RequestParam(value = "gender") String gender,
@RequestParam(value = "password") String password, @RequestParam(value = "verifyCode") String verifyCode,
@RequestParam(value = "creator") String creator) {
UserRegisterReqVo userRegisterReqVo = UserRegisterReqVo.builder().mobileNo(mobileNo).email(email)
.nickName(nickName).idName(idName).idType(idType).idNo(idNo).gender(gender).passwd(password)
.passcode(verifyCode).creator(creator).build();
UserRegisterResVo userRegisterResVo;
try {
userRegisterResVo = operationUserServiceImpl.userRegister(userRegisterReqVo);
} catch (BizException e) {
log.error(e.toString() + "_" + e.getMessage(), e);
return APIResponse.error(e.getCode(), e.getMessage());
} catch (Exception e) {
log.error(e.toString() + "_" + e.getMessage(), e);
return APIResponse
.error(ApiResultStatus.INTERNAL_SERVER_ERROR.getApiResultStatus(),
ApiResultStatus.INTERNAL_SERVER_ERROR.getMessageResourceName());
}
return APIResponse
.success(ApiResultStatus.SUCCESS.getApiResultStatus(), ApiResultStatus.SUCCESS.getMessageResourceName(),
userRegisterResVo);
}
}
複製程式碼
以上我們在微服務下定義了一個使用者註冊介面,此時啟動微服務,然後輸入微服務的IP+埠+/swagger-ui.html,就可以看到Swagger-UI了,如下:
通過Swagger-UI我們就可以校驗的方式測試介面了,同時因為介面欄位都在UI有說明和暫時,並且是與實際程式碼完全一致的,所以在對接時基於這些介面定義進行對接就可以了!