sbc(二)高可用Eureka+聲明式服務調用

前言

上一篇簡單入門了SpringBoot+SpringCloud 構建微服務。但只能算是一個demo級別的應用。
此次會按照實際生產要求來搭建這套服務。html

Swagger應用

上次提到咱們調用本身的http接口的時候採用的是PostMan來模擬請求,這個在平時調試時天然沒有什麼問題,但當咱們須要和前端聯調開發的時候效率就比較低了。前端

一般來講如今先後端分離的項目通常都是後端接口先行。java

後端大大們先把接口定義好(入參和出參),前端大大們來肯定是否知足要求,能夠了以後後端纔開始着手寫實現,這樣總體效率要高上許多。node

但也會帶來一個問題:在接口定義階段頻繁變動接口定義而沒有一個文檔或相似的東西來記錄,那麼雙方的溝通加上前端的調試都是比較困難的。git

基於這個需求網上有各類解決方案,好比阿里的rap就是一個不錯的例子。github

可是springCould爲咱們在提供了一種在開發springCloud項目下更方便的工具swaggerspring

實際效果以下:後端

01.png
01.png

配置swagger

sbc-order爲例我將項目分爲了三個模塊:api

├── order                                    // Order服務實現 
│   ├── src/main
├── order-api                                // 對內API
│   ├── src/main
├── order-client                             // 對外的clientAPI
│   ├── src/main
├── .gitignore                               
├── LICENSE                
├── README.md複製代碼

由於實現都寫在order模塊中,因此只須要在該模塊中配置便可。springboot

首先須要加入依賴,因爲我在order模塊中依賴了:

<dependency>
    <groupId>com.crossoverJie</groupId>
    <artifactId>order-api</artifactId>
    <version>${target.version}</version>
</dependency>複製代碼

order-api又依賴了:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <scope>compile</scope>
</dependency>複製代碼

接着須要配置一個SwaggerConfig

@Configuration
@EnableSwagger2
/** 是否打開swagger **/
@ConditionalOnExpression("'${swagger.enable}' == 'true'")
public class SwaggerConfig {


    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.crossoverJie.sbcorder.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("sbc order api")
                .description("sbc order api")
                .termsOfServiceUrl("http://crossoverJie.top")
                .contact("crossoverJie")
                .version("1.0.0")
                .build();
    }

}複製代碼

其實就是配置swagger的一些基本信息。
以後啓動項目,在地址欄輸入http://ip:port/swagger-ui.html#/便可進入。
能夠看到如上圖所示的接口列表,點擊以下圖所示的參數例子便可進行接口調用。

02.jpg
02.jpg

自定義開關Swagger

swagger的便利能給咱們帶來不少好處,但稍有不慎也可能出現問題。

好比若是在生產環境還能經過IP訪問swagger的話那後果但是不堪設想的。
因此咱們須要靈活控制swagger的開關。

這點能夠利用spring的條件化配置(條件化配置能夠配置存在於應用中,一旦知足一些特定的條件時就取消這些配置)來實現這一功能:

@ConditionalOnExpression("'${swagger.enable}' == 'true'")複製代碼

該註解的意思是給定的SpEL表達式計算結果爲true時纔會建立swaggerbean

swagger.enable這個配置則是配置在application.properties中:

# 是否打開swagger
swagger.enable = true複製代碼

這樣當咱們在生產環境時只須要將該配置改成false便可。

ps:更多spring條件化配置:

@ConditionalOnBean                 //配置了某個特定Bean
@ConditionalOnMissingBean          //沒有配置特定的Bean
@ConditionalOnClass                //Classpath裏有指定的類
@ConditionalOnMissingClass         //Classpath裏缺乏指定的類
@ConditionalOnExpression           //給定的Spring Expression Language(SpEL)表達式計算結果爲true
@ConditionalOnJava                 //Java的版本匹配特定值或者一個範圍值
@ConditionalOnJndi                 //參數中給定的JNDI位置必須存在一個,若是沒有給參數,則要有JNDI InitialContext
@ConditionalOnProperty             //指定的配置屬性要有一個明確的值
@ConditionalOnResource             //Classpath裏有指定的資源
@ConditionalOnWebApplication       //這是一個Web應用程序
@ConditionalOnNotWebApplication    //這不是一個Web應用程序
(參考SpringBoot實戰)複製代碼

高可用Eureka

在上一篇中是用Eureka來作了服務註冊中心,全部的生產者都往它註冊服務,消費者又經過它來獲取服務。

可是以前講到的都是單節點,這在生產環境風險巨大,咱們必須作到註冊中心的高可用,搭建Eureka集羣。

這裏簡單起見就搭建兩個Eureka,思路則是這兩個Eureka都把本身當成應用向對方註冊,這樣就能夠構成一個高可用的服務註冊中心。

在實際生產環節中會是每一個註冊中心一臺服務器,爲了演示起見,我就在本地啓動兩個註冊中心,可是端口不同。

首先須要在本地配置一個host:

127.0.0.1 node1 node2複製代碼

這樣不管是訪問node1仍是node2均可以在本機調用的到(固然不配置host也能夠,只是須要經過IP來訪問,這樣看起來不是那麼明顯)。

並給sbc-service新增了兩個配置文件:

application-node1.properties:

spring.application.name=sbc-service
server.port=8888
eureka.instance.hostname=node1

## 不向註冊中心註冊本身
#eureka.client.register-with-eureka=false
#
## 不須要檢索服務
#eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://node2:9999/eureka/複製代碼

application-node2.properties:

spring.application.name=sbc-service
server.port=9999
eureka.instance.hostname=node2

## 不向註冊中心註冊本身
#eureka.client.register-with-eureka=false
#
## 不須要檢索服務
#eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://node1:8888/eureka/複製代碼

其中最重要的就是:

eureka.client.serviceUrl.defaultZone=http://node2:9999/eureka/
eureka.client.serviceUrl.defaultZone=http://node1:8888/eureka/複製代碼

兩個應用互相註冊。

啓動的時候咱們按照:
java -jar sbc-service-1.0.0-SNAPSHOT.jar --spring.profiles.active=node1啓動,就會按照傳入的node1或者是node2去讀取application-node1.properties,application-node2.properties這兩個配置文件(配置文件必須按照application-{name}.properties的方式命名)。

分別啓動兩個註冊中心能夠看到如下:

03.jpg
03.jpg


04.jpg
04.jpg

能夠看到兩個註冊中心以及互相註冊了。
在服務註冊的時候只須要將兩個地址都加上便可:
eureka.client.serviceUrl.defaultZone=http://node1:8888/eureka/,http://node2:9999/eureka/

在服務調用的時候能夠嘗試關閉其中一個,正常狀況下依然是能夠調用到服務的。

Feign聲明式調用

接下來談談服務調用,上次提到能夠用ribbon來進行服務調用,可是明顯很不方便,不如像以前rpc調用那樣簡單直接。

爲此此次使用Feign來進行聲明式調用,就像調用一個普通方法那樣簡單。

order-client

片頭說到我將應用分紅了三個模塊order、order-api、order-client,其中的client模塊就是關鍵。

來看看其中的內容,只有一個接口:

@RequestMapping(value="/orderService")
@FeignClient(name="sbc-order")
@RibbonClient
public interface OrderServiceClient extends OrderService{


    @ApiOperation("獲取訂單號")
    @RequestMapping(value = "/getOrderNo", method = RequestMethod.POST)
    BaseResponse<OrderNoResVO> getOrderNo(@RequestBody OrderNoReqVO orderNoReq) ;
}複製代碼

@FeignClient這個註解要注意下,其中的name的是本身應用的應用名稱,在
application.properties中的spring.application.name配置

其中繼承了一個OrderServiceorder-api模塊中,來看看order-api中的內容。

order-api

其中也只有一個接口:

@RestController
@Api("訂單服務API")
@RequestMapping(value = "/orderService")
@Validated
public interface OrderService {

    @ApiOperation("獲取訂單號")
    @RequestMapping(value = "/getOrderNo", method = RequestMethod.POST)
    BaseResponse<OrderNoResVO> getOrderNo(@RequestBody OrderNoReqVO orderNoReq) ;
}複製代碼

這個接口有兩個目的。

  1. 給真正的controller來進行實現。
  2. client接口進行繼承。

類關係以下:

05.jpg
05.jpg

註解這些都沒什麼好說的,一看就懂。

order

order則是具體接口實現的模塊,就和平時寫controller同樣。
來看看如何使用client進行聲明式調用:

此次看看sbc-user這個項目,在裏邊調用了sbc-order的服務。
其中的user模塊依賴了order-client:

<dependency>
    <groupId>com.crossoverJie</groupId>
    <artifactId>order-client</artifactId>
</dependency>複製代碼

具體調用:

@Autowired
    private OrderServiceClient orderServiceClient ;

    @Override
    public BaseResponse<UserResVO> getUserByFeign(@RequestBody UserReqVO userReq) {
        //調用遠程服務
        OrderNoReqVO vo = new OrderNoReqVO() ;
        vo.setReqNo(userReq.getReqNo());
        BaseResponse<OrderNoResVO> orderNo = orderServiceClient.getOrderNo(vo);

        logger.info("遠程返回:"+JSON.toJSONString(orderNo));

        UserRes userRes = new UserRes() ;
        userRes.setUserId(123);
        userRes.setUserName("張三");

        userRes.setReqNo(userReq.getReqNo());
        userRes.setCode(StatusEnum.SUCCESS.getCode());
        userRes.setMessage("成功");

        return userRes ;
    }複製代碼

能夠看到只須要將order-client包中的Order服務注入進來便可。

sbc-clientswagger中進行調用:

06.jpg
06.jpg


07.jpg
07.jpg

因爲我並沒傳appId因此order服務返回的錯誤。

總結

當一個應用須要對外暴露接口時着須要按照以上方式提供一個client包更消費者使用。

其實應用自己也是須要作高可用的,和Eureka高可用同樣,再不一樣的服務器上再啓一個或多個服務並註冊到Eureka集羣中便可。

後續還會繼續談到zuul網關,容錯,斷路器等內容,歡迎拍磚討論。

項目:github.com/crossoverJi…

博客:crossoverjie.top

weixinchat.jpg
weixinchat.jpg
相關文章
相關標籤/搜索