一、网关
网络的关口,负责请求的路由、转发、身份验证
XML
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


二、网关登录校验
自定义过滤器:
TypeScript
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阶段 执行了");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
微服务项目网关:
java
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 未登录、未授权
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);
}
@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;
}
}
XML
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
**网关传递用户:**将用户的id保存在请求头当中,通过统一拦截处理,获取用户的id,放入ThreadLocal当中;请求完成,清理ThreadLocal,实现用户id从网关到各个项目模块的传递
**OpenFeign传递用户:**OpenFeign中提供了一个拦截器接口,所有由OpenFeign发起的请求都会先调用拦截器处理请求,在拦截处理过程中,我们将ThreadLocal中的用户id放入OpenFeign的请求头当中,其他微服务拦截处理的过程中获得用户id并放入线程当中

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

2.加入相关依赖

java
<!--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)微服务中要以特定方式读取需要热更新的配置属性
java
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.动态路由
java
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初始化之后执行
@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) {
//路由表更新,更新监听器
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();
//添加新的路由表并记录id
for (RouteDefinition routeDefinition : routeDefinitionList) {
writer.save(Mono.just(routeDefinition)).subscribe();
set.add(routeDefinition.getId());
}
}
}
将yaml配置转换为json配置:
html
[
{
"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完成之后进行事务的提交,不占用资源,提高了性能
缺点:难以实现复的事务控制,如特定隔离级别;当事务的隔离级别过低时会出现脏读、不可重复读、幻读问题