企業須要把本身的服務經過接口的形式對外提供,提供接口的平臺稱之爲開放平臺。好比支付寶開放平臺,淘寶開放平臺。java
調用接口那一方通常稱之爲ISV,獨立軟體開發商(independent software vendor),開放平臺這一方稱之爲ISP,網絡服務提供者(Internet Service Provider)。一般狀況下須要爲ISV分配一個appKey和appSecret,能夠簡單的理解爲用戶名密碼,有了這個才能正常調用開放平臺接口。react
爲了保證請求參數的合法性,客戶端須要生成一個簽名串,而後開放平臺須要校驗這個簽名串。ISV能夠經過appKey和AppSecret來生成簽名串,這樣就能保證客戶端請求是合法的,服務端須要校驗簽名串是否合法,appKey是否合法,這裏開放平臺會提供一套簽名算法,常見的有:支付寶開放平臺簽名算法git
開放平臺的一個重要部分就是鑑權,鑑權功能和具體的業務無關,能夠單獨拿出來作,若是是單體應用的話能夠把這部分操做寫在一個Controller中。若是是微服務體系的話把鑑權部分放在網關是一個不錯的選擇,由於網關是一個統一入口,在入口處作好鑑權,後續的微服務不須要再作鑑權處理了,只需實現本身的業務邏輯便可。github
在Spring Cloud微服務體系當中,充當網關的角色常見有兩個,一個是Zuul,另外一個是Spring Cloud Gateway。Zuul本質是一個Servlet,IO模型是BIO,阻塞式,而Spring Cloud Gateway基於Netty開發的,IO模型是AIO,也就是異步IO,在處理高併發請求場景下,Spring Cloud Gateway具備明顯優點。二者各有優缺點,Zuul優勢是架構簡單,擴展起來比較方便,缺點是在處理高併發請求下稍顯力不從心,Spring Cloud Gateway優勢是高性能,能夠處理高併發請求,缺點是架構複雜,須要瞭解異步編程、Netty、React等框架基本原理,調試起來比較困難。web
本篇拿Spring Cloud Gateway來演示如何設計一個簡單的開放平臺。算法
首先簡單介紹下Spring Cloud Gateway的基本功能,做爲網關,首要的功能是路由功能,簡單理解就是將一個A請求變成B請求,相似於Nginx的反向代理。另外一個功能請求過濾,Spring Cloud Gateway容許開發者實現自定義過濾器,用來處理當前請求。spring
Spring Cloud Gateway的路由配置有兩種,一種是寫在配置文件裏面,一種使用代碼實現(Java DSL)。編程
server: port: 8090 spring: cloud: gateway: routes: - id: host_route uri: https://www.baidu.com/ pedicates: - Path=/
上面這個配置,在瀏覽器訪問http://localhost:8090
,頁面會出現百度首頁。瀏覽器
另外一種使用Java代碼形式:網絡
@Bean public RouteLocator customRouteLocator(RouteLocatorBuilder routeBuilder) { return routeBuilder.routes() .route("host_route", r -> r.path("/") .uri("https://www.baidu.com/") ) .build(); }
其效果跟配置文件是同樣的,若是路由配置固定不變可寫在配置文件中,若是涉及到動態改變路由配置,就必須寫在Java代碼中了,由於代碼更靈活。
假設咱們咱們的開放平臺接口地址爲:http://open.xx.com
,該接口提供一個參數method
,表示接口名,經過接口名來決定具體請求哪一個微服務。好比訪問http://open.xx.com/?method=goods.get
,轉發到商品微服務http://192.168.1.1:8080/getGoods
,以下圖所示:
因而可知,在網關須要配置一套路由:
spring: cloud: gateway: routes: - id: getGoods uri: http://192.168.1.1:8080 predicates: - Path=/getGoods - id: getOrder uri: http://192.168.1.2:8080 predicates: - Path=/getOrder
接下來須要在網關中作幾件事情:
這些事情均可以在全局過濾器中執行,過濾器代碼以下:
package com.example.gateway.filter; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; import java.util.HashMap; import java.util.Map; /** 網關入口過濾器 * @author tanghc */ @Component public class IndexFilter implements WebFilter, Ordered { private static Map<String, String> methodPathMap = new HashMap<>(16); // 存放接口名對應的path static { methodPathMap.put("goods.get", "/getGoods"); methodPathMap.put("order.get", "/getOrder"); } @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { // 獲取請求參數 Map<String, String> query = exchange.getRequest().getQueryParams().toSingleValueMap(); // 鑑權 this.check(query); String method = query.get("method"); String path = methodPathMap.get(method); if (path != null) { // 複製一個新的request ServerHttpRequest newRequest = exchange.getRequest() .mutate() // == 關鍵在這裏,從新定義轉發的path .path(path) .build(); // 複製一個新的exchange,request用新的 ServerWebExchange newExchange = exchange .mutate() .request(newRequest) .build(); // 向後轉發新的exchange return chain.filter(newExchange); } return chain.filter(exchange); } /** * 鑑權 * @param query */ private void check(Map<String, String> query) { } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
這裏經過一個map來存放method和path的對應關係,而後經過重寫request中的path來實現轉發功能。在瀏覽器請求http://localhost:8090/?method=order.get
,轉發到http://192.168.1.1:8080/getGoods
至此開放平臺的一個基本功能就實現了,不過依然存在幾個問題:
針對第一個問題,解決辦法是使用Java代碼(Java DSL)來配置路由,具體的思路是讓微服務來維護路由關係,而後網關在啓動完畢後去各個微服務端拉取路由配置,保存到本地。
第二個問題,能夠使用動態配置,如Spring Cloud Config或阿波羅配置來動態改變。
以上涉及到的全部問題在SOP中已經所有實現。