一、網關
網絡的關口,負責請求的路由、轉發、身份驗證
server:port: 8080
spring:cloud:nacos:discovery:server-addr: 192.168.96.129:8848gateway:routes:- id: item-serviceuri: lb://item-servicepredicates:- Path=/items/**,/search/**- id: user-serviceuri: lb://user-servicepredicates:- Path=/addresses/**,/users/**- id: cart-serviceuri: lb://cart-servicepredicates:- Path=/carts/**- id: trade-serviceuri: lb://trade-servicepredicates:- Path=/orders/**application:name: hm-gateway
二、網關登錄校驗
自定義過濾器:
package com.hmall.gateway.filters;import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();System.out.println("GlobalFilter pre階段 執行了");return chain.filter(exchange);}@Overridepublic int getOrder() {return 0;}
}
微服務項目網關:
package com.hmall.gateway.filters;import com.hmall.common.exception.UnauthorizedException;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.utils.JwtTool;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {//不需要處理的請求路徑public final AuthProperties authProperties;public final JwtTool jwtTool;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//獲得請求頭ServerHttpRequest request = exchange.getRequest();//放行不需要攔截的請求//路徑合法,需要放行if (isUnique(request.getPath().toString())){//合法,放行return chain.filter(exchange);}//判斷令牌是否合法String token=null;Long userId=null;List<String> authorization = request.getHeaders().get("authorization");if (authorization != null && authorization.size() > 0) {token = authorization.get(0);}try {userId = jwtTool.parseToken(token);}catch (UnauthorizedException e) {//401 未登錄、未授權ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}//TODO 保存用戶id到請求頭,實現多個微服務間用戶id的共享String userInfo = userId.toString();ServerWebExchange swe=exchange.mutate().request(builder -> builder.header("user-info", userInfo)).build();//System.out.println(userId);//放行return chain.filter(swe);}@Overridepublic int getOrder() {return 0;}private boolean isUnique(String path) {for (String excludePath : authProperties.getExcludePaths()) {if (antPathMatcher.match(excludePath, path)) {return true;}}return false;}
}
server:port: 8080
spring:application:name: hm-gatewaycloud:nacos:discovery:server-addr: 192.168.96.129:8848gateway:routes:- id: item-serviceuri: lb://item-servicepredicates:- Path=/items/**,/search/**- id: user-serviceuri: lb://user-servicepredicates:- Path=/addresses/**,/users/**- id: cart-serviceuri: lb://cart-servicepredicates:- Path=/carts/**- id: trade-serviceuri: lb://trade-servicepredicates:- Path=/orders/**- id: pay-serviceuri: lb://pay-servicepredicates:- Path=/pay-orders/**
hm:jwt:location: classpath:hmall.jksalias: hmallpassword: hmall123tokenTTL: 30mauth:excludePaths:- /search/**- /users/login- /items/**- /hi
網關傳遞用戶:將用戶的id保存在請求頭當中,通過統一攔截處理,獲取用戶的id,放入ThreadLocal當中;請求完成,清理ThreadLocal,實現用戶id從網關到各個項目模塊的傳遞
OpenFeign傳遞用戶:OpenFeign中提供了一個攔截器接口,所有由OpenFeign發起的請求都會先調用攔截器處理請求,在攔截處理過程中,我們將ThreadLocal中的用戶id放入OpenFeign的請求頭當中,其他微服務攔截處理的過程中獲得用戶id并放入線程當中
三、配置管理
1.拉取共享配置
2.加入相關依賴
<!--nacos配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--讀取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>
3.配置熱更新
(1)nacos中要有一個與微服務名有關的配置文件
(2)微服務中要以特定方式讀取需要熱更新的配置屬性
package com.hmall.cart.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@Data
@ConfigurationProperties(prefix = "hm.cart")
public class MaxCommodityConfig {private Integer maxCommodity;
}
4.動態路由
package com.hmall.gateway.routes;
import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Executor;@Component
@RequiredArgsConstructor
public class DynamicRounterLoader {private final NacosConfigManager nacosConfigManager;private final RouteDefinitionWriter writer;private final String dataId="gateway-routes.json";private final String group="DEFAULT_GROUP";//記錄路由的idprivate HashSet<String> set=new HashSet<String>();//在Bean初始化之后執行@PostConstructpublic void initRoutesConfigListener() throws NacosException {//拉取配置并更新配置String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {@Overridepublic Executor getExecutor() {return null;}@Overridepublic void receiveConfigInfo(String configInfo) {//路由表更新,更新監聽器System.out.println(configInfo+"監聽器更新執行了");updateRouters(configInfo);}});System.out.println(configInfo+"監聽器更新了");//第一次啟動,更新監聽器updateRouters(configInfo);}private void updateRouters(String configInfo) {//將json數據轉換為實體類List<RouteDefinition> routeDefinitionList = JSONUtil.toList(configInfo, RouteDefinition.class);//刪除原來的路由表for (String id : set) {writer.delete(Mono.just(id)).subscribe();}set.clear();//添加新的路由表并記錄idfor (RouteDefinition routeDefinition : routeDefinitionList) {writer.save(Mono.just(routeDefinition)).subscribe();set.add(routeDefinition.getId());}}}
將yaml配置轉換為json配置:
[{"id": "item","predicates": [{"name": "Path","args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}}],"filters": [],"uri": "lb://item-service"},{"id": "cart","predicates": [{"name": "Path","args": {"_genkey_0":"/carts/**"}}],"filters": [],"uri": "lb://cart-service"},{"id": "user","predicates": [{"name": "Path","args": {"_genkey_0":"/users/**", "_genkey_1":"/addresses/**"}}],"filters": [],"uri": "lb://user-service"},{"id": "trade","predicates": [{"name": "Path","args": {"_genkey_0":"/orders/**"}}],"filters": [],"uri": "lb://trade-service"},{"id": "pay","predicates": [{"name": "Path","args": {"_genkey_0":"/pay-orders/**"}}],"filters": [],"uri": "lb://pay-service"}
]
三、服務保護和分布式事務
1.雪崩問題
微服務調用鏈路中的某個服務故障,引起整個鏈路中的所有微服務都不可用
解決方案:保證代碼的健壯性、保證網絡的暢通、能應對高并發請求
2.服務保護
請求限流:限制訪問服務器的并發量,避免服務因流量激增出現故障
線程隔離:模擬船艙隔板的防水原理。通過限定每個業務能使用的線程數量而將故障業務隔離,避免故障擴散
服務熔斷:由斷路器統計請求的異常比例或慢調用比例,如果超出閾值則會熔斷業務,則攔截該接口請求
3.分布式事務
事務協調者(TC):維護全局和分支事務的狀態,協調全局事務提交和回滾
事務管理器(TM):定義全局事務范圍、開始全局事務、提交或回滾全局事務
資源管理器(RM):管理分支事務,與TC交談以注冊分支事務和報告分支事務狀態
XA模式:
優點:事務的強一致性,滿足ACID原則?,常用數據庫都支持,實現簡單,并且沒有代碼侵入
缺點:因為一階段需要鎖定數據庫資源,等待二階段結束才釋放,性能較差?,依賴關系型數據庫實現事務?
AT模式:
優點:滿足ACID原則?,常用數據庫都支持,實現簡單,并且沒有代碼侵入,單個RM完成之后進行事務的提交,不占用資源,提高了性能
缺點:難以實現復的事務控制,如特定隔離級別;當事務的隔離級別過低時會出現臟讀、不可重復讀、幻讀問題
?