上一篇簡單入門了SpringBoot+SpringCloud 構建微服務。但只能算是一個
demo
級別的應用。
此次會按照實際生產要求來搭建這套服務。html
上次提到咱們調用本身的http
接口的時候採用的是PostMan
來模擬請求,這個在平時調試時天然沒有什麼問題,但當咱們須要和前端聯調開發的時候效率就比較低了。前端
一般來講如今先後端分離的項目通常都是後端接口先行。java
後端大大們先把接口定義好(入參和出參),前端大大們來肯定是否知足要求,能夠了以後後端纔開始着手寫實現,這樣總體效率要高上許多。node
但也會帶來一個問題:在接口定義階段頻繁變動接口定義而沒有一個文檔或相似的東西來記錄,那麼雙方的溝通加上前端的調試都是比較困難的。git
基於這個需求網上有各類解決方案,好比阿里的rap就是一個不錯的例子。github
可是springCould
爲咱們在提供了一種在開發springCloud
項目下更方便的工具swagger
。spring
實際效果以下:後端
以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#/
便可進入。
能夠看到如上圖所示的接口列表,點擊以下圖所示的參數例子便可進行接口調用。
swagger
的便利能給咱們帶來不少好處,但稍有不慎也可能出現問題。
好比若是在生產環境還能經過IP訪問swagger
的話那後果但是不堪設想的。
因此咱們須要靈活控制swagger
的開關。
這點能夠利用spring的條件化配置(條件化配置能夠配置存在於應用中,一旦知足一些特定的條件時就取消這些配置)
來實現這一功能:
@ConditionalOnExpression("'${swagger.enable}' == 'true'")複製代碼
該註解的意思是給定的SpEL表達式計算結果爲true
時纔會建立swagger
的bean
。
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都把本身當成應用向對方註冊,這樣就能夠構成一個高可用的服務註冊中心。
在實際生產環節中會是每一個註冊中心一臺服務器,爲了演示起見,我就在本地啓動兩個註冊中心,可是端口不同。
首先須要在本地配置一個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的方式命名
)。
分別啓動兩個註冊中心能夠看到如下:
能夠看到兩個註冊中心以及互相註冊了。
在服務註冊的時候只須要將兩個地址都加上便可:eureka.client.serviceUrl.defaultZone=http://node1:8888/eureka/,http://node2:9999/eureka/
在服務調用的時候能夠嘗試關閉其中一個,正常狀況下依然是能夠調用到服務的。
接下來談談服務調用,上次提到能夠用ribbon
來進行服務調用,可是明顯很不方便,不如像以前rpc
調用那樣簡單直接。
爲此此次使用Feign
來進行聲明式調用,就像調用一個普通方法那樣簡單。
片頭說到我將應用分紅了三個模塊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配置
。
其中繼承了一個OrderService
在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) ;
}複製代碼
這個接口有兩個目的。
controller
來進行實現。client
接口進行繼承。類關係以下:
註解這些都沒什麼好說的,一看就懂。
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-client
的swagger
中進行調用:
因爲我並沒傳appId
因此order
服務返回的錯誤。
當一個應用須要對外暴露接口時着須要按照以上方式提供一個
client
包更消費者使用。
其實應用自己也是須要作高可用的,和Eureka
高可用同樣,再不一樣的服務器上再啓一個或多個服務並註冊到Eureka
集羣中便可。
後續還會繼續談到zuul網關,容錯,斷路器
等內容,歡迎拍磚討論。
博客:crossoverjie.top。