SpringCloud之網(wǎng)關(guān)、服務(wù)保護和分布式事務(wù)詳解
一、網(wǎng)關(guān)
網(wǎng)絡(luò)的關(guān)口,負責(zé)請求的路由、轉(zhuǎn)發(fā)、身份驗證
server:
port: 8080
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.96.129:8848
gateway:
routes:
- id: item-service
uri: lb://item-service
predicates:
- Path=/items/**,/search/**
- id: user-service
uri: lb://user-service
predicates:
- Path=/addresses/**,/users/**
- id: cart-service
uri: lb://cart-service
predicates:
- Path=/carts/**
- id: trade-service
uri: lb://trade-service
predicates:
- Path=/orders/**
application:
name: hm-gateway


二、網(wǎng)關(guān)登錄校驗
自定義過濾器:
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 {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
System.out.println("GlobalFilter pre階段 執(zhí)行了");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
微服務(wù)項目網(wǎng)關(guān):
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();
@Override
public 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 未登錄、未授權(quán)
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//TODO 保存用戶id到請求頭,實現(xiàn)多個微服務(wù)間用戶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);
}
@Override
public 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-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.96.129:8848
gateway:
routes:
- id: item-service
uri: lb://item-service
predicates:
- Path=/items/**,/search/**
- id: user-service
uri: lb://user-service
predicates:
- Path=/addresses/**,/users/**
- id: cart-service
uri: lb://cart-service
predicates:
- Path=/carts/**
- id: trade-service
uri: lb://trade-service
predicates:
- Path=/orders/**
- id: pay-service
uri: lb://pay-service
predicates:
- Path=/pay-orders/**
hm:
jwt:
location: classpath:hmall.jks
alias: hmall
password: hmall123
tokenTTL: 30m
auth:
excludePaths:
- /search/**
- /users/login
- /items/**
- /hi網(wǎng)關(guān)傳遞用戶:將用戶的id保存在請求頭當(dāng)中,通過統(tǒng)一攔截處理,獲取用戶的id,放入ThreadLocal當(dāng)中;請求完成,清理ThreadLocal,實現(xiàn)用戶id從網(wǎng)關(guān)到各個項目模塊的傳遞
OpenFeign傳遞用戶:OpenFeign中提供了一個攔截器接口,所有由OpenFeign發(fā)起的請求都會先調(diào)用攔截器處理請求,在攔截處理過程中,我們將ThreadLocal中的用戶id放入OpenFeign的請求頭當(dāng)中,其他微服務(wù)攔截處理的過程中獲得用戶id并放入線程當(dāng)中

三、配置管理
1.拉取共享配置

2.加入相關(guān)依賴

<!--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中要有一個與微服務(wù)名有關(guān)的配置文件

(2)微服務(wù)中要以特定方式讀取需要熱更新的配置屬性
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.動態(tài)路由
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";
//記錄路由的id
private HashSet<String> set=new HashSet<String>();
//在Bean初始化之后執(zhí)行
@PostConstruct
public void initRoutesConfigListener() throws NacosException {
//拉取配置并更新配置
String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
//路由表更新,更新監(jiān)聽器
System.out.println(configInfo+"監(jiān)聽器更新執(zhí)行了");
updateRouters(configInfo);
}
});
System.out.println(configInfo+"監(jiān)聽器更新了");
//第一次啟動,更新監(jiān)聽器
updateRouters(configInfo);
}
private void updateRouters(String configInfo) {
//將json數(shù)據(jù)轉(zhuǎn)換為實體類
List<RouteDefinition> routeDefinitionList = JSONUtil.toList(configInfo, RouteDefinition.class);
//刪除原來的路由表
for (String id : set) {
writer.delete(Mono.just(id)).subscribe();
}
set.clear();
//添加新的路由表并記錄id
for (RouteDefinition routeDefinition : routeDefinitionList) {
writer.save(Mono.just(routeDefinition)).subscribe();
set.add(routeDefinition.getId());
}
}
}
將yaml配置轉(zhuǎn)換為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"
}
]三、服務(wù)保護和分布式事務(wù)
1.雪崩問題
微服務(wù)調(diào)用鏈路中的某個服務(wù)故障,引起整個鏈路中的所有微服務(wù)都不可用
解決方案:保證代碼的健壯性、保證網(wǎng)絡(luò)的暢通、能應(yīng)對高并發(fā)請求
2.服務(wù)保護
請求限流:限制訪問服務(wù)器的并發(fā)量,避免服務(wù)因流量激增出現(xiàn)故障
線程隔離:模擬船艙隔板的防水原理。通過限定每個業(yè)務(wù)能使用的線程數(shù)量而將故障業(yè)務(wù)隔離,避免故障擴散
服務(wù)熔斷:由斷路器統(tǒng)計請求的異常比例或慢調(diào)用比例,如果超出閾值則會熔斷業(yè)務(wù),則攔截該接口請求
3.分布式事務(wù)
事務(wù)協(xié)調(diào)者(TC):維護全局和分支事務(wù)的狀態(tài),協(xié)調(diào)全局事務(wù)提交和回滾
事務(wù)管理器(TM):定義全局事務(wù)范圍、開始全局事務(wù)、提交或回滾全局事務(wù)
資源管理器(RM):管理分支事務(wù),與TC交談以注冊分支事務(wù)和報告分支事務(wù)狀態(tài)
- XA模式:

優(yōu)點:事務(wù)的強一致性,滿足ACID原則?,常用數(shù)據(jù)庫都支持,實現(xiàn)簡單,并且沒有代碼侵入
缺點:因為一階段需要鎖定數(shù)據(jù)庫資源,等待二階段結(jié)束才釋放,性能較差?,依賴關(guān)系型數(shù)據(jù)庫實現(xiàn)事務(wù)?
- AT模式:

優(yōu)點:滿足ACID原則?,常用數(shù)據(jù)庫都支持,實現(xiàn)簡單,并且沒有代碼侵入,單個RM完成之后進行事務(wù)的提交,不占用資源,提高了性能
缺點:難以實現(xiàn)復(fù)的事務(wù)控制,如特定隔離級別;當(dāng)事務(wù)的隔離級別過低時會出現(xiàn)臟讀、不可重復(fù)讀、幻讀問題
?總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- 解決SpringCloud gateway網(wǎng)關(guān)配置MVC攔截器報錯問題
- SpringCloud的網(wǎng)關(guān)Zuul和Gateway詳解
- SpringCloudGateway 網(wǎng)關(guān)登錄校驗實現(xiàn)思路
- Sentinel網(wǎng)關(guān)限流與SpringCloud Gateway整合過程
- SpringCloud?Sentinel服務(wù)保護詳解(請求限流、線程隔離、服務(wù)熔斷)
- SpringCloud分布式事務(wù)Seata部署和集成過程
- SpringCloud微服務(wù)開發(fā)基于RocketMQ實現(xiàn)分布式事務(wù)管理詳解
相關(guān)文章
Java向數(shù)據(jù)庫中插入數(shù)據(jù)后獲取自增ID的常用方法
有時候因為新增的需求需要獲取剛剛新增的數(shù)據(jù)的自增的主鍵ID,下面這篇文章主要給大家介紹了關(guān)于Java向數(shù)據(jù)庫中插入數(shù)據(jù)后獲取自增ID的常用方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-11-11
解析Java線程編程中的線程安全與synchronized的使用
這篇文章主要介紹了Java線程編程中的線程安全與synchronized的使用,synchronized多線程使用時一定要注意線程之間的沖突問題,需要的朋友可以參考下2015-12-12
springboot?注解方式批量插入數(shù)據(jù)的實現(xiàn)
一次請求需要往數(shù)據(jù)庫插入多條數(shù)據(jù)時,可以節(jié)省大量時間,本文主要介紹了springboot?注解方式批量插入數(shù)據(jù),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
SpringBoot+Quartz實現(xiàn)定時任務(wù)的代碼模版分享
quartz?是一款開源且豐富特性的Java?任務(wù)調(diào)度庫,用于實現(xiàn)任務(wù)調(diào)度和定時任務(wù),本文主要和大家分享一個SpringBoot整合Quartz實現(xiàn)定時任務(wù)的代碼模版,需要的可以參考一下2023-06-06
你知道怎么用Spring的三級緩存解決循環(huán)依賴嗎
這篇文章主要為大家詳細介紹了Spring的三級緩存解決循環(huán)依賴,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-02-02

