1. 程式人生 > >從零搭建Spring Cloud Gateway閘道器(一)

從零搭建Spring Cloud Gateway閘道器(一)

# 新建Spring Boot專案 怎麼新建Spring Boot專案這裡不再具體贅述,不會的可以翻看下之前的部落格或者直接百度。這裡直接貼出對應的pom檔案。 pom依賴如下: ```xml 4.0.0 org.springframework.boot spring-boot-starter-parent 2.2.5.RELEASE com.lifengdi gateway 0.0.1-SNAPSHOT gateway Demo project for Spring Boot 1.8 Hoxton.SR3 org.springframework.cloud
spring-cloud-starter-gateway
org.projectlombok lombok 1.18.12 true org.springframework.boot spring-boot-starter-test test org.junit.vintage
junit-vintage-engine
org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.boot spring-boot-starter-actuator org.apache.commons commons-pool2 commons-io commons-io 2.5 compile com.alibaba fastjson 1.2.58 org.springframework.boot spring-boot-starter-mail
org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin
``` 由於是閘道器專案,所以不需要`spring-boot-starter-web`相關的依賴。 配置檔案如下: ```yaml server: port: 8080 spring: application: name: spring-cloud-gateway-demo cloud: gateway: discovery: locator: enabled: true #啟用路由訪問 routes: - id: path_route # 指定域名 uri: http://localhost:8081 predicates: - Path=/jar/** filters: # 熔斷配置 - name: Hystrix args: name: default fallbackUri: forward:/fallback - id: path_route2 # 指定域名 uri: http://localhost:8082 predicates: - Path=/war/** filters: # 熔斷配置 - name: Hystrix args: name: hystrix1 fallbackUri: forward:/fallback mvc: throw-exception-if-no-handler-found: true # 預設熔斷超時時間30s hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 hystrix1: execution: isolation: thread: timeoutInMilliseconds: 1000 ``` # 熔斷(介面或者專案) 熔斷相關jar包如下: ```xml org.springframework.cloud spring-cloud-starter-netflix-hystrix ``` 預設的熔斷回撥介面: ```java package com.lifengdi.gateway.hystrix; import com.lifengdi.gateway.exception.BaseException; import com.lifengdi.gateway.response.ResponseResult; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author: Li Fengdi * @date: 2020-03-18 16:35 */ @RestController @Slf4j public class DefaultHystrixController { @RequestMapping("/fallback") public ResponseResult fallback(){ log.error("觸發熔斷......"); return ResponseResult.fail(BaseException.DEFAULT_HYSTRIX.build()); } } ``` 具體配置檔案說明如下: ```yaml routes: - id: path_route # 指定域名 uri: http://localhost:8081 predicates: - Path=/jar/** filters: # 熔斷配置 - name: Hystrix args: name: default fallbackUri: forward:/fallback - id: path_route2 # 指定域名 uri: http://localhost:8082 predicates: - Path=/war/** filters: # 熔斷配置 - name: Hystrix args: name: hystrix1 fallbackUri: forward:/fallback mvc: throw-exception-if-no-handler-found: true # 預設熔斷超時時間30s hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 hystrix1: execution: isolation: thread: timeoutInMilliseconds: 1000 ``` `default`、`hystrix1`為自定義的引數,可以配置多個熔斷策略,不同的介面、服務可以單獨配置對應的超時時間,不需要額外的進行開發,不過需要增加額外的配置檔案。 # 全域性session共享 依賴jar包: ```xml org.springframework.boot spring-boot-starter-data-redis-reactive org.springframework.session spring-session-data-redis org.apache.commons commons-pool2 org.springframework.boot spring-boot-starter-security ``` 相關yml配置: ```yaml spring: redis: database: 0 host: localhost port: 6379 password: 123456 lettuce: pool: max-active: 300 max-idle: 8 max-wait: -1ms min-idle: 0 session: store-type: redis ``` `spring.session.store-type`Spring預設就是redis實現的,也有其他的,配置不同罷了。 增加程式碼如下: 許可權相關,這裡預設全部放行: ```java package com.lifengdi.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; @Configuration @EnableWebFluxSecurity public class GatewaySecurityConfig { @Bean SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity serverHttpSecurity) throws Exception { serverHttpSecurity .csrf().disable() .authorizeExchange().pathMatchers("/**").permitAll() .anyExchange() .authenticated(); return serverHttpSecurity.build(); } } ``` session相關: ```java package com.lifengdi.gateway.config; import com.lifengdi.gateway.resolver.MyCookieWebSessionIdResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.ResponseCookie; import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession; import org.springframework.web.server.session.CookieWebSessionIdResolver; import org.springframework.web.server.session.WebSessionIdResolver; import java.util.function.Consumer; @Configuration @EnableRedisWebSession(maxInactiveIntervalInSeconds = 10*60*60, redisNamespace = "my:spring:session") public class WebSessionConfig { @Bean public WebSessionIdResolver webSessionIdResolver() { CookieWebSessionIdResolver resolver = new MyCookieWebSessionIdResolver(); resolver.setCookieName("SESSIONID"); Consumer consumer = responseCookieBuilder -> { responseCookieBuilder.path("/"); }; resolver.addCookieInitializer(consumer); return resolver; } } ``` 注意這裡使用的是`@EnableRedisWebSession`註解,而不是`@EnableRedisHttpSession`,這個是和zuul不一樣的地方。 用zuul做閘道器的時候,直接使用`@EnableRedisHttpSession`在配置裡面就可以通過redis共享session資訊 Spring同時提供了`@EnableRedisWebSession`來對WebFlux的支援。 值得一提的是這兩個註解內部實現並不相同,需要自定義的配置也不一樣。 這裡自定義cookieName、path等是自定義了`webSessionIdResolver`來實現的,而不是`cookieSerializer`。如果使用`cookieSerializer`的話,對`@EnableRedisWebSession`來說是不起作用的。這個坑之前坑了好半天! MyCookieWebSessionIdResolver程式碼如下: ```java package com.lifengdi.gateway.resolver; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpCookie; import org.springframework.session.web.http.DefaultCookieSerializer; import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.session.CookieWebSessionIdResolver; import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; /** * 自定義WebSessionId解析器,以相容{@link org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession} *

* 使用EnableRedisHttpSession時{@link DefaultCookieSerializer}中useBase64Encoding預設為true,將cookie中的sessionId使用base64 * 加密,但是如果使用{@link org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession},預設 * 的解析器沒有將sessionId解密,導致獲取不到正確的session *

* * @author: Li Fengdi * @date: 2020/3/16 15:41 */ @Slf4j public class MyCookieWebSessionIdResolver extends CookieWebSessionIdResolver { @Override public List resolveSessionIds(ServerWebExchange exchange) { MultiValueMap cookieMap = exchange.getRequest().getCookies(); List cookies = cookieMap.get(getCookieName()); if (cookies == null) { return Collections.emptyList(); } return cookies.stream().map(HttpCookie::getValue).map(this::base64Decode).collect(Collectors.toList()); } /** * base64解碼 * * @param base64Value base64Value * @return 解碼後的字串 */ private String base64Decode(String base64Value) { try { byte[] decodedCookieBytes = Base64.getDecoder().decode(base64Value); return new String(decodedCookieBytes); } catch (Exception ex) { log.debug("Unable to Base64 decode value: " + base64Value); return null; } } } ``` 其實這段程式碼本就是參考了`cookieSerializer`中的程式碼來實現的。 > 如果指定了useBase64Encoding為false,即不加密sessionId,那麼就不需要這一段程式碼了。 程式碼已上傳到git上,需要的可以去看看。 git程式碼地址:[https://github.com/lifengdi/spring-cloud-gateway-demo](https://github.com/lifengdi/spring-cloud-gateway-demo) 原文地址:[https://www.lifengdi.com/archives/article/1776](https://www.lifengdi.com/archives/artic