当前位置: 首页 > news >正文

Spring Cloud 网关及配置管理教学

一、课程目标

深入理解 Spring Cloud 中网关和配置管理的核心概念、原理及应用场景。熟练掌握 Spring Cloud Gateway 的配置与开发,能够实现请求路由、登录校验、用户信息传递等功能。学会使用 Nacos 进行配置管理,包括共享配置、配置热更新和动态路由的实现,提升微服务架构的可维护性和灵活性。

二、课程内容

(一)网关基础

  1. 网关概念:网关作为网络的关口,承担着请求路由、转发和身份校验等关键职责。在微服务架构里,它是外界访问微服务的统一入口,将客户端请求精准地路由到对应的微服务实例,同时对请求进行必要的身份验证和权限检查,保障系统安全。
  2. Spring Cloud 中的网关实现
    • Spring Cloud Gateway:由 Spring 官方推出,基于 WebFlux 响应式编程模型构建。具备出色的性能,无需复杂调优即可高效运行。其设计理念先进,能够很好地适应现代高并发、低延迟的业务场景需求。
    • Netflix Zuul:由 Netflix 开发,基于 Servlet 的阻塞式编程。在性能方面,需要进行适当调优才能达到与 Spring Cloud Gateway 相近的水平。随着技术的发展,Spring Cloud Gateway 因其优势逐渐成为更主流的选择。
比较维度Spring Cloud GatewayNetflix Zuul
开发框架基于 Spring WebFlux 响应式编程,使用 Netty 进行异步非阻塞通信基于 Servlet 的阻塞式编程
性能无需复杂调优即可获得优异性能,适合高并发场景需要调优才能获得与 Spring Cloud Gateway 类似的性能,在高并发场景下性能可能受限
功能特性提供丰富的内置断言工厂(如时间、请求头、路径等断言)和众多路由过滤器,支持动态路由和更细粒度的请求匹配与处理功能相对基础,可通过扩展实现类似功能,但配置和使用相对复杂
集成与易用性与 Spring Cloud 生态系统集成度高,配置和使用相对简洁,基于注解和配置文件的方式易于理解和上手与 Spring Cloud 集成时需要额外配置,配置方式相对繁琐,学习成本较高
社区支持与发展趋势Spring 官方支持,社区活跃,更新频繁,功能不断完善,是 Spring Cloud 微服务架构中网关的主流选择Netflix 对其更新和维护相对较少,在 Spring Cloud 生态中

(二)Spring Cloud Gateway 快速入门

  1. 创建新模块:在开发工具(如 IntelliJ IDEA 或 Eclipse)中,利用 Spring Initializr 快速创建一个新的 Spring Boot 模块。在创建过程中,选择合适的 Spring Boot 和 Spring Cloud 版本,同时勾选 Spring Cloud Gateway 相关依赖,确保项目具备网关开发的基础环境。
  2. 引入网关依赖:如果创建项目时未自动添加,手动在项目的pom.xml文件中添加 Spring Cloud Gateway 依赖。
  1. 编写启动类:创建一个主启动类,使用@SpringBootApplication注解开启 Spring Boot 应用。确保启动类能够正常启动 Spring Cloud Gateway 服务,为后续配置和功能实现提供基础。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}
  1. 配置路由规则:在application.yml文件中配置路由规则,定义请求如何被路由到目标微服务。每个路由规则包含唯一的id、目标微服务的uri(使用lb://表示负载均衡)以及路由断言predicates
spring:cloud:gateway:routes:- id: itemuri: lb://item-servicepredicates:- Path=/items/**- id: useruri: lb://user-servicepredicates:- Path=/users/**

(三)路由属性详解

  1. RouteDefinition 属性
    • id:路由的唯一标识符,用于区分不同的路由规则,在整个网关配置中必须保持唯一。
    • uri:指定路由的目标地址,可以是微服务的名称(结合负载均衡)或具体的 URL 地址。
    • predicates:路由断言,是一组判断条件。只有当请求满足这些条件时,才会触发该路由规则。Spring 提供了丰富的内置断言工厂,如Path(根据请求路径判断)、Method(根据请求方法判断)等。
    • filters:路由过滤器,用于对请求或响应进行预处理和后处理。可以添加请求头、移除响应头、重写路径等,满足多样化的业务需求。
  2. 路由断言工厂:Spring 提供了 12 种基本的RoutePredicateFactory实现。
    • 时间相关断言After断言判断请求是否在指定时间点之后,Before断言判断是否在指定时间点之前,Between断言判断是否在两个指定时间点之间。例如:
- After=2024-01-01T00:00:00.000+08:00[Asia/Shanghai]
  • 请求头相关断言Cookie断言检查请求是否包含特定的 cookie,Header断言检查请求是否包含特定的 header。例如:
- Cookie=JSESSIONID, [a-zA-Z0-9]{32}
- Header=X-Request-Id, \d+
  • 其他断言Host断言验证请求的主机名,Method断言验证请求方法,Path断言匹配请求路径等。
  1. 路由过滤器:网关提供了 33 种路由过滤器。
    • 请求头过滤器AddRequestHeader用于添加请求头,RemoveRequestHeader用于移除请求头。例如:
- AddRequestHeader=X-Request-Timestamp, ${currentTimeMillis}
- RemoveRequestHeader=X-Private-Header
  • 响应头过滤器AddResponseHeader用于添加响应头,RemoveResponseHeader用于移除响应头。
  • 路径处理过滤器RewritePath用于重写请求路径,StripPrefix用于去除请求路径中的指定前缀。
属性名说明示例
id路由的唯一标识符,用于在网关配置中区分不同的路由规则。在整个网关的路由配置体系里,这个标识符必须是独一无二的,否则会导致配置冲突id: "user - route":定义了一个名为user - route的路由规则,在后续的配置和管理中,可通过该id来引用和操作这条路由
uri指定路由的目标地址,它决定了请求最终要被转发到的微服务实例或具体的 URL。可以使用lb://前缀结合微服务名称,实现基于负载均衡的请求转发;也可以直接填写具体的 URL 地址uri: lb://user - service:表示将请求负载均衡地转发到名为user - service的微服务上。
uri: http://192.168.1.100:8081:将请求直接转发到指定的 IP 地址和端口对应的服务上
predicates路由断言,是一组用于判断请求是否符合当前路由规则的条件。只有当请求满足所有断言条件时,才会触发该路由。Spring Cloud Gateway 提供了丰富的内置断言工厂,可基于请求的各种属性进行判断predicates: - Path=/users/**:表示当请求路径以/users/开头时,满足该路由的断言条件,请求会被路由到对应的目标地址。
predicates: - Method=GET,POST:意味着只有请求方法为 GET 或 POST 时,请求才符合该路由规则
filters路由过滤器,用于对请求或响应进行预处理和后处理操作。可以在请求被转发到目标微服务之前对请求进行修改,如添加请求头、修改请求路径;也可以在响应返回给客户端之前对响应进行处理,如添加响应头、修改响应内容filters: - AddRequestHeader=X - Request - Source, gateway:在请求被转发到目标微服务之前,为请求添加一个名为X - Request - Source,值为gateway的请求头。
filters: - RewritePath=/oldPath/(?<segment>.*), /newPath/$ {segment}:对请求路径进行重写,将以/oldPath/开头的路径重写为/newPath/开头的路径

断言(Predicates)

断言用于判断请求是否符合特定条件,只有满足所有断言条件的请求才会被路由到指定的目标。Spring Cloud Gateway 提供了丰富的内置断言工厂,每个断言工厂都基于请求的不同属性进行判断。

  1. 基于时间的断言
    • After:判断请求时间是否在指定时间点之后。例如After=2024-01-01T00:00:00.000+08:00[Asia/Shanghai],表示请求时间在 2024 年 1 月 1 日 0 点之后(上海时区)才符合该断言。常用于特定时间窗口开放的服务,如限时抢购活动。
    • Before:判断请求时间是否在指定时间点之前。例如Before=2024-02-01T23:59:59.999+08:00[Asia/Shanghai],表示请求时间在 2024 年 2 月 1 日 23 点 59 分 59 秒之前符合断言。适用于活动截止时间的控制。
    • Between:判断请求时间是否在两个指定时间点之间。例如Between=2024-01-01T00:00:00.000+08:00[Asia/Shanghai],2024-01-07T23:59:59.999+08:00[Asia/Shanghai],表示请求时间在 2024 年 1 月 1 日 0 点到 2024 年 1 月 7 日 23 点 59 分 59 秒之间符合断言。常用于周期性活动的时间范围限制。
  2. 基于请求头的断言
    • Cookie:检查请求是否包含特定的 cookie,并且 cookie 的值匹配指定的正则表达式。例如Cookie=JSESSIONID,[a-zA-Z0-9]{32},表示请求必须包含名为JSESSIONID,且值为 32 位字母和数字组合的 cookie。可用于基于用户登录状态的路由判断。
    • Header:检查请求是否包含特定的 header,并且 header 的值匹配指定的正则表达式。例如Header=X-Request-Id,\d+,表示请求必须包含名为X-Request-Id,且值为数字的 header。常用于请求标识或权限验证。
  3. 基于请求其他属性的断言
    • Host:验证请求的主机名是否匹配指定的模式。例如Host=*.example.com,表示请求的主机名必须是以example.com为后缀的域名。适用于多租户或基于域名的服务区分。
    • Method:验证请求方法是否为指定的方法。例如Method=GET,POST,表示请求方法必须是 GET 或 POST。用于限制特定接口的请求方式。
    • Path:匹配请求路径是否符合指定规则。例如Path=/users/{id},表示请求路径必须以/users/开头,并且后面跟着一个路径参数idPath=/users/**表示请求路径以/users/开头的所有路径都匹配。常用于根据不同业务路径进行路由。
    • Query:检查请求参数是否包含指定参数,并且参数值匹配指定的正则表达式(可省略)。例如Query=name表示请求必须包含名为name的参数;Query=name,Jack表示请求必须包含名为name且值为Jack的参数。用于根据请求参数进行路由判断。
    • RemoteAddr:验证请求者的 IP 必须是指定范围。例如RemoteAddr=192.168.1.1/24,表示请求的源 IP 地址必须在192.168.1.1192.168.1.254这个网段内。可用于限制特定 IP 段的访问。
    • Weight:用于权重处理,根据权重将请求分配到不同的路由。例如Weight=group1,2表示在一组路由中,当前路由的权重为 2,其他路由的权重根据配置而定,请求会按照权重比例被分配到不同路由。常用于流量分发和负载均衡的精细化控制。

过滤器(Filters)

过滤器用于对请求或响应进行预处理和后处理操作,可在请求到达目标微服务之前对请求进行修改,也可在响应返回给客户端之前对响应进行处理。Spring Cloud Gateway 提供了多种内置过滤器,同时也支持自定义过滤器。

  1. 请求头过滤器
    • AddRequestHeader:给当前请求添加一个请求头。例如AddRequestHeader=X-Request-Source,gateway,会在请求中添加一个名为X-Request-Source,值为gateway的请求头。可用于传递额外的请求信息。
    • RemoveRequestHeader:移除请求中的一个请求头。例如RemoveRequestHeader=X-Private-Header,会从请求中移除名为X-Private-Header的请求头。常用于敏感信息的处理。
  2. 响应头过滤器
    • AddResponseHeader:给响应结果中添加一个响应头。例如AddResponseHeader=X-Response-Time,${currentTimeMillis},会在响应中添加一个名为X-Response-Time,值为当前时间戳的响应头。可用于记录响应时间。
    • RemoveResponseHeader:从响应结果中移除一个响应头。例如RemoveResponseHeader=X-Server-Info,会从响应中移除名为X-Server-Info的响应头。常用于保护服务器信息。
  3. 路径处理过滤器
    • RewritePath:请求路径重写。例如RewritePath=/oldPath/(?<segment>.*),/newPath/${segment},会将以/oldPath/开头的请求路径重写为/newPath/开头的路径,并保留路径中的其他部分。适用于接口升级或路径调整时的兼容处理。
    • StripPrefix:去除请求路径中的 N 段前缀。例如StripPrefix=1,当请求路径为/a/b时,转发时只保留/b。常用于简化请求路径。
  4. 自定义过滤器
    • GatewayFilter:路由过滤器,作用于任意指定的路由,默认不生效,需配置到路由后生效。通过继承AbstractGatewayFilterFactory类来自定义过滤器,在apply方法中编写过滤逻辑。例如自定义一个记录请求日志的过滤器:
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class RequestLoggingGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestLoggingGatewayFilterFactory.Config> {public RequestLoggingGatewayFilterFactory() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println("Request URL: " + exchange.getRequest().getURI());return chain.filter(exchange);}};}@Datapublic static class Config {// 可定义配置属性,如日志级别等}
}
  • GlobalFilter:全局过滤器,作用范围是所有路由,声明后自动生效。实现GlobalFilter接口,重写filter方法来编写过滤逻辑。例如自定义一个全局的权限验证过滤器:
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class AuthenticationGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();// 权限验证逻辑if (isAuthenticated(request)) {return chain.filter(exchange);} else {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}}private boolean isAuthenticated(ServerHttpRequest request) {// 实际的权限验证代码return true;}@Overridepublic int getOrder() {// 过滤器执行顺序,值越小优先级越高return 0;}
}

(四)网关登录校验

  1. 网关请求处理流程
    • HandlerMapping:负责根据请求找到匹配的路由规则,将路由信息存入上下文,默认实现是RoutePredicateHandlerMapping
    • WebHandler:通常由FilteringWebHandler担任,它加载并排序网关配置的过滤器,形成过滤器链,按顺序执行过滤器逻辑。
    • 过滤器:分为prepost两个阶段。pre阶段在请求路由到微服务之前执行,post阶段在微服务返回结果之后执行。只有pre阶段所有过滤器都通过,请求才会被路由到微服务;若有过滤器拦截请求,后续过滤器不再执行。
  2. 自定义过滤器实现登录校验
    • GlobalFilter:全局过滤器,对所有路由生效。实现GlobalFilter接口,重写filter方法。在方法中获取请求,进行登录校验逻辑(如检查 JWT 令牌)。若校验通过,调用chain.filter(exchange)放行请求;若校验失败,返回错误响应。

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.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();// 登录校验逻辑,例如检查JWT令牌if (isValidToken(request)) {return chain.filter(exchange);} else {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}}private boolean isValidToken(ServerHttpRequest request) {// 实际的JWT校验逻辑return true;}@Overridepublic int getOrder() {// 过滤器执行顺序,值越小优先级越高return 0;}
}

  • GatewayFilter:路由过滤器,作用于指定路由。通过继承AbstractGatewayFilterFactory类,实现自定义路由过滤器。在apply方法中编写过滤器逻辑,可根据配置属性进行灵活处理。

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {public CustomGatewayFilterFactory() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 自定义过滤器逻辑return chain.filter(exchange);}};}@Datapublic static class Config {private String customParam;}
}

(五)网关传递用户信息

  1. 网关到微服务传递用户信息:在网关的登录校验过滤器中,使用ServerWebExchangemutate方法修改请求,将获取到的用户信息写入请求头。
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 UserInfoPassingFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();// 获取用户信息,假设从JWT令牌中解析String userInfo = getUserIdFromToken(request);ServerHttpRequest newRequest = request.mutate().header("user-info", userInfo).build();ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();return chain.filter(newExchange);}private String getUserIdFromToken(ServerHttpRequest request) {// 实际的JWT解析逻辑return "123456";}@Overridepublic int getOrder() {return 1;}
}
  1. 微服务中获取用户信息:在hm - common模块中编写 SpringMVC 拦截器,获取请求头中的用户信息,并保存到ThreadLocal中,方便后续业务逻辑使用。

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String userInfo = request.getHeader("user-info");if (userInfo != null) {// 保存用户信息到ThreadLocalUserContextHolder.setUserId(userInfo);}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 清理ThreadLocalUserContextHolder.clearUserId();}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清理ThreadLocalUserContextHolder.clearUserId();}
}

  1. OpenFeign 传递用户信息:OpenFeign 提供RequestInterceptor接口,实现该接口可在请求发送前修改请求头,传递用户信息。

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;@Component
public class OpenFeignUserInfoInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {String userInfo = UserContextHolder.getUserId();if (userInfo != null) {template.header("user-info", userInfo);}}
}

(六)配置管理

  1. 配置管理的必要性:在微服务架构中,配置管理至关重要。微服务数量众多,若每个微服务都有大量重复配置,会增加维护成本;业务配置频繁变动时,每次修改都需重启服务,影响系统可用性;网关路由配置写死,变更时同样需要重启网关,不利于系统的灵活调整。
  2. 使用 Nacos 进行配置管理
    • 添加配置到 Nacos:登录 Nacos 管理界面,在配置管理模块中添加共享配置。例如,创建shared - jdbc.yaml文件,配置数据库连接信息、MyBatis - Plus 相关配置等。

spring:datasource:url: jdbc:mysql://${hm.b.host}:3306/hm - item?useUnicode=true&characterEncoding=UTF - 8&autoReconnect=truedriver - class - name: com.mysql.cj.jdbc.Driverusername: rootpassword: ${hm.db.pw}mybatis - plus:configuration:default - enum - type - handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandlerglobal - config:db - config:update - strategy: not_nullid - type: auto

  • 拉取共享配置:在微服务项目中引入spring - cloud - starter - alibaba - nacos - configspring - cloud - starter - bootstrap依赖。在bootstrap.yaml文件中配置 Nacos 地址和共享配置信息。

spring:application:name: cart - serviceprofiles:active: devcloud:nacos:server - addr: 192.168.150.101:8848config:file - extension: yamlshared - configs:- dataId: shared - jdbc.yaml- dataId: shared - log.yaml- dataId: shared - swagger.yaml

  1. 配置热更新:实现配置热更新需满足两个条件。Nacos 中要有与微服务名相关的配置文件,格式为${spring.application.name}-${spring.active.profile}.${file-extension}(如cart - service - dev.yaml)。微服务以特定方式读取需要热更新的配置属性,使用@ConfigurationProperties注解结合@RefreshScope注解,或使用@Value注解结合@RefreshScope注解。

import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@ConfigurationProperties(prefix = "hm.cart")
@RefreshScope
public class CartProperties {private int maxItems;public int getMaxItems() {return maxItems;}public void setMaxItems(int maxItems) {this.maxItems = maxItems;}
}

  1. 动态路由:实现动态路由需完成两件事。监听 Nacos 配置变更消息,通过NacosConfigManager注册监听器,在监听器的receiveConfigInfo方法中处理配置变更。

import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.concurrent.Executor;@Component
public class GatewayRouteConfigListener {@Autowiredprivate NacosConfigManager nacosConfigManager;public void initRouteConfigListener() throws NacosException {String dataId = "gateway - routes.json";String group = "DEFAULT_GROUP";ConfigService configService = nacosConfigManager.getConfigService();configService.getConfigAndSignListener(dataId, group, 5000, new Listener() {@Overridepublic Executor getExecutor() {return null;}@Overridepublic void receiveConfigInfo(String configInfo) {// 处理配置变更,更新路由表updateGatewayRoutes(configInfo);}});// 首次启动时,拉取并更新配置String initialConfig = configService.getConfig(dataId, group, 5000);updateGatewayRoutes(initialConfig);}private void updateGatewayRoutes(String configInfo) {// 解析配置信息,更新网关路由表}
}

更新路由表,利用RouteDefinitionWriter接口的save方法将新的路由定义保存到网关路由表中。推荐使用 JSON 格式的路由配置,方便解析和管理。

{"id": "item","uri": "lb://item - service","predicates": [{"name": "Path","args": {"_genkey_0": "/items/**","_genkey_1": "/search/**"}}],"filters": []
}

三、课程总结

本课程系统学习了 Spring Cloud 网关及配置管理的核心知识。在网关部分,掌握了 Spring Cloud Gateway 的基本概念、快速入门配置、路由属性、登录校验以及用户信息传递的实现方法。配置管理方面,学会了使用 Nacos 进行共享配置、配置热更新和动态路由的操作。通过这些内容的学习,能够有效提升微服务架构的安全性、可维护性和灵活性,为开发高质量的微服务应用奠定坚实基础。在实际项目中,可根据业务需求灵活运用所学知识,不断优化和完善微服务架构。

网关在微服务架构中扮演着至关重要的角色,它是外界访问微服务的统一入口,承担着多种功能,确保系统的安全、稳定和高效运行。

  1. 路由转发:根据请求的 URL、请求方法、请求头等信息,将客户端的请求精准地路由到对应的后端微服务实例。通过配置路由规则,网关能够实现不同服务间的灵活调用。在电商系统中,用户对商品详情页的请求,网关可根据请求路径,将其转发到商品服务;用户的订单操作请求,则被转发到订单服务。这一功能是微服务架构中服务通信的基础,使得各个微服务专注于自身业务逻辑,提高了系统的可维护性和扩展性。
  2. 鉴权与认证:对客户端的请求进行身份验证和权限检查,只有通过验证的请求才会被转发到后端微服务。常见的鉴权方式包括基于令牌(如 JWT)的验证、OAuth 2.0 等。在企业级应用中,员工访问特定资源时,网关会验证其身份和权限,确保敏感信息不被未授权人员访问。通过这种方式,网关为系统提供了一层安全防护,有效防止非法访问和数据泄露。
  3. 限流:控制客户端对微服务的请求流量,防止因高并发请求导致系统资源耗尽而崩溃。可以基于请求频率、并发连接数等指标进行限流。在秒杀活动期间,网关可以限制每个用户每秒的请求次数,避免恶意刷单和过度请求,保证系统的稳定性和公平性。这有助于保护后端微服务,确保其在高负载情况下仍能正常运行。
  4. 负载均衡:将请求均匀地分配到多个后端微服务实例上,避免单个实例负载过高。常见的负载均衡算法有轮询、随机、加权轮询等。在大型互联网应用中,网关会根据各个微服务实例的负载情况,动态地将请求分配到不同实例,提高系统的整体性能和可用性。这使得系统能够更好地应对高并发场景,提升用户体验。
  5. 请求过滤与修改:在请求到达后端微服务之前,对请求进行过滤和修改。可以添加、删除或修改请求头信息,也可以对请求体进行处理。例如,网关可以为请求添加统一的请求 ID,方便在整个系统中进行日志追踪;或者对请求体中的敏感信息进行加密处理。这种灵活性使得网关能够满足不同业务场景下的需求,增强系统的功能。
  6. 响应处理:对后端微服务返回的响应进行处理,如添加响应头、修改响应体、缓存响应结果等。可以在响应中添加缓存控制头,提高系统的性能;对于频繁访问且数据变化不大的接口,网关可以缓存响应结果,减少后端服务的压力。这有助于优化系统的性能和用户体验,提高系统的整体效率。
  7. 服务聚合:将多个后端微服务的响应结果进行整合,返回给客户端。在电商系统的商品详情页,网关可以同时调用商品服务、评论服务、库存服务等,将各个服务的结果合并后返回给用户,减少客户端与多个微服务的直接交互,简化客户端的调用逻辑。这提高了系统的集成性和用户体验,使得客户端能够更方便地获取所需信息。
  8. 监控与日志记录:记录所有进出网关的请求和响应信息,为系统监控和故障排查提供依据。通过分析这些日志,运维人员可以了解系统的运行状况,及时发现潜在问题。例如,当系统出现异常时,可以通过网关日志快速定位问题的源头。同时,网关还可以提供一些监控指标,如请求处理时间、流量统计等,帮助运维人员更好地优化系统性能。

1. 如何在转发之前做登录校验

在 Spring Cloud Gateway 中,你可以通过自定义全局过滤器(GlobalFilter)或者路由过滤器(GatewayFilter)来在请求转发之前进行登录校验。通常推荐使用全局过滤器,因为它可以作用于所有的路由。

实现思路
  • 创建一个实现GlobalFilterOrdered接口的过滤器类。
  • filter方法中进行登录校验逻辑,例如检查请求头中的 JWT 令牌。
  • 如果校验通过,放行请求;如果校验失败,返回错误响应。
代码示例
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class LoginCheckFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();// 从请求头中获取JWT令牌String token = request.getHeaders().getFirst("Authorization");if (token == null || !validateToken(token)) {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}return chain.filter(exchange);}private boolean validateToken(String token) {// 实现JWT令牌的校验逻辑,例如使用JWT库进行验证// 这里只是示例,实际需要根据具体情况实现return token != null && token.startsWith("Bearer ");}@Overridepublic int getOrder() {return -1; // 过滤器执行顺序,值越小越先执行}
}

2. 网关校验 JWT 之后,如何将用户信息传递给微服务

在网关校验 JWT 通过后,可以将解析出的用户信息添加到请求头中,然后将请求转发给微服务。

实现思路
  • 在登录校验过滤器中,解析 JWT 令牌获取用户信息。
  • 使用ServerWebExchangemutate方法修改请求,将用户信息添加到请求头中。
  • 转发修改后的请求到微服务。
代码示例
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 UserInfoPassingFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String token = request.getHeaders().getFirst("Authorization");if (token != null && validateToken(token)) {String userInfo = parseUserInfoFromToken(token);ServerHttpRequest newRequest = request.mutate().header("X-User-Info", userInfo).build();ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();return chain.filter(newExchange);}return chain.filter(exchange);}private String parseUserInfoFromToken(String token) {// 解析JWT令牌获取用户信息,例如用户ID、用户名等// 这里只是示例,实际需要根据具体情况实现return "user123";}private boolean validateToken(String token) {// 实现JWT令牌的校验逻辑return token != null && token.startsWith("Bearer ");}@Overridepublic int getOrder() {return 0;}
}

3. 微服务之间也会相互调用,这种调用不经过网关,又该如何传递用户信息

当微服务之间使用 OpenFeign 等方式进行调用时,可以通过实现RequestInterceptor接口来在请求发送前添加用户信息。

实现思路
  • 创建一个实现RequestInterceptor接口的拦截器类。
  • apply方法中,从ThreadLocal或者其他上下文存储中获取用户信息。
  • 将用户信息添加到请求头中。
代码示例

同时,在微服务中需要确保用户信息在请求处理过程中被正确存储到ThreadLocal中,例如在 Controller 的拦截器中进行存储:

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String userInfo = request.getHeader("X-User-Info");if (userInfo != null) {// 将用户信息存储到ThreadLocal中UserContextHolder.setUserInfo(userInfo);}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 清理ThreadLocal中的用户信息UserContextHolder.clearUserInfo();}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清理ThreadLocal中的用户信息UserContextHolder.clearUserInfo();}
}

public class UserContextHolder {private static final ThreadLocal<String> userInfoHolder = new ThreadLocal<>();public static void setUserInfo(String userInfo) {userInfoHolder.set(userInfo);}public static String getUserInfo() {return userInfoHolder.get();}public static void clearUserInfo() {userInfoHolder.remove();}
}

相关文章:

Spring Cloud 网关及配置管理教学

一、课程目标 深入理解 Spring Cloud 中网关和配置管理的核心概念、原理及应用场景。熟练掌握 Spring Cloud Gateway 的配置与开发&#xff0c;能够实现请求路由、登录校验、用户信息传递等功能。学会使用 Nacos 进行配置管理&#xff0c;包括共享配置、配置热更新和动态路由的…...

202520 | 微服务

微服务 VS 单体架构 单体架构&#xff08;Monolithic Architecture&#xff09;和微服务架构&#xff08;Microservices Architecture&#xff09;是两种主流的软件设计模式&#xff0c;它们在开发、部署、扩展和维护等方面有显著差异。以下是两者的详细对比&#xff1a; 1. 定…...

32信号和槽_信号和槽存在的意义(2)

① 一个学生,可以选择多门课程来学习 一门课程,也可以被多个同学来选择 ② 张三这个同学,可以选 语文和数学 . 李四这个同学可以选 语文 和 英语 语文这门课程,既可以被张三选择,也可以被李四选择~~ ③ 引入第三张表作为关联表 ④一个信号,可以 connect 到多个槽函数上 一个槽…...

Mac 上使用 mysql -u root -p 命令,出现“zsh: command not found: mysql“?

一、确定 MySQL 安装路径&#xff1a; 如果你是使用 Homebrew 安装的 MySQL&#xff0c;通常安装路径是 /usr/local/mysql/bin 。 如果你是通过官方 DMG 安装包安装的 MySQL&#xff0c;默认安装路径可能是 /usr/local/mysql/bin 。你可以在终端中使用以下命令来查找 MySQL 的…...

Django REST Framework系列教程(9)——过滤与排序

目录 前情回顾 方法一: 重写get_queryset方法 方法二: 使用django-filter 安装django-filter 自定义FilterSet类 方法三: 使用SearchFilter类 自定义SearchFilter类 排序OrderingFilter类 在前面的DRF系列教程中&#xff0c;我们以博客为例介绍了序列化器(Serializer),…...

工业级主题系统程序设计

一、架构优化方案 #mermaid-svg-gB72o6CBc2UMgkrv {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-gB72o6CBc2UMgkrv .error-icon{fill:#552222;}#mermaid-svg-gB72o6CBc2UMgkrv .error-text{fill:#552222;stroke:#5…...

Java 逐梦力扣之旅_[204. 计数质数]

题目: 204. 计数质数 给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;4 解释&#xff1a;小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。示例 2&#xff1a; 输入&#xff1a;n 0 输出&…...

[3.3] 阻塞与非阻塞通信 理论+代码

文章目录 1. 阻塞通信的发生和解决2. 点对点通信应用程序案例 - 随机步行&#xff08;Random Walk&#xff09; 1. 阻塞通信的发生和解决 MPI的p2p中包括两种模式&#xff1a; a. 阻塞通信&#xff08;Blocking&#xff09; MPI_Send 和 MPI_Recv 在返回前&#xff0c;必须满…...

node_modules\deasync: Command failed.

运行&#xff1a;“yarn install” 时报错 PS D:\WebPro\hainan-mini-program> yarn install yarn install v1.22.19 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... warning " > babel-loader8.2.2" has un…...

二十种中药果实识别分类系统,Python/resnet18/pytorch

二十种中药果实识别分类系统,Python/resnet18/pytorch 基于pytorch训练, resnet18网络&#xff0c;可用于训练其他分类问题&#xff0c;也可自己重新训练 20类中药材具体包括&#xff1a;(1) 补骨脂&#xff0c;(2) 草豆蔻&#xff0c;(3) 川楝子&#xff0c;(4) 地肤子&…...

C++·异常

目录 概念 使用 异常的抛出和捕获 栈展开 catch匹配 继承体系例子 异常重新抛出 异常安全问题 例&#xff1a; 异常规范 小知识 概念 异常处理机制允许程序中独⽴开发的部分能够在运⾏时就出现的问题进⾏通信并做出相应的处理&#xff0c; 异常使得我们能够将问题的…...

C 语言高级编程指南:回调函数与设计模式

C 语言高级编程指南&#xff1a;回调函数与设计模式 目录 回调函数详解C语言中的设计模式高级回调函数应用内存管理与安全多线程环境下的设计模式 回调函数详解 1. 什么是回调函数&#xff1f; 回调函数&#xff08;Callback Function&#xff09;是一个通过函数指针调用的…...

SeaTunnel系列之:Apache SeaTunnel编译和安装

Apache SeaTunnel编译 Prepare编译克隆源代码本地安装子项目从源代码构建 SeaTunnel构建子模块安装 JetBrains IDEA Scala 插件安装 JetBrains IDEA Lombok 插件代码风格运行简单示例不仅如此 安装下载 SeaTunnel 发布包下载连接器插件从源代码构建 SeaTunnel 运行 SeaTunnel 在…...

zk基础—5.Curator的使用与剖析二

大纲 1.基于Curator进行基本的zk数据操作 2.基于Curator实现集群元数据管理 3.基于Curator实现HA主备自动切换 4.基于Curator实现Leader选举 5.基于Curator实现分布式Barrier 6.基于Curator实现分布式计数器 7.基于Curator实现zk的节点和子节点监听机制 8.基于Curator创…...

bge-m3+deepseek-v2-16b+离线语音能力实现离线文档向量化问答语音版

ollama run deepseek-v2:16b ollama pull bge-m3 1、离线听写效果的大幅度提升。50M 1.3G&#xff08;每次初始化都会很慢&#xff09;---优化到首次初始化使用0延迟响应。 2、文档问答历史问题处理与优化&#xff0c;文档问答离线策略讨论与参数暴露。 3、离线大模型答复中断…...

[leetcode]3123. 最短路径中的边(Dijkstra+反向搜索找边)

题目链接 题意 给定n个点的无向图 给定一个edges{u,v,w}数组 表示u到v有一条边权为w的无向边 返回一个bool数组ans&#xff0c;ans[i]1表示edges[i]在任意一条0到n-1的最短路中 思路 先Dijkstra找出最短路再从n-1出发 反向搜索 当前点i&#xff0c;邻接点j&#xff0c;边权…...

构建macOS命令速查手册:基于Flask的轻量级Web应用实践

构建macOS命令速查手册&#xff1a;基于Flask的轻量级Web应用实践 一、项目概述 本文介绍一个基于Flask框架开发的macOS命令速查Web应用。该应用通过结构化的命令数据存储和响应式前端设计&#xff0c;为用户提供便捷的命令查询体验&#xff0c;具备以下特点&#xff1a; 六…...

中国移动启动数字乡村“五新升级”:年底前,行政村5G覆盖达95%

大湾区经济网品牌观察报道&#xff0c;近日&#xff0c;在国家全面推进乡村振兴的战略背景下&#xff0c;中国移动近日发布数字乡村升级行动计划&#xff0c;以“AI大模型数智化平台”为核心引擎&#xff0c;围绕“五新升级”构建“两个新型”信息服务体系。 一、数字基建筑基&…...

借助mcpo在open-webui中使用mcp

open-webui前几天发布了0.6版本&#xff0c;我立即进行了升级。新版本中一个重要功能是通过mcpo方式支持了mcp server。本文将介绍mcpo是什么&#xff0c;以及如何在open-webui中使用它。同时&#xff0c;我也会分享几个在接入过程中遇到的问题及解决方案。 首先来介绍mcpo&…...

Mysql的备份还原

MySQL日志 日志类型 MySQL有几个不同的日志文件&#xff0c;可以帮助你找出mysqld内部发生的事情&#xff1a; 日志文件记入文件中的信息类型错误日志记录启动、运行或停止时出现的问题。查询日志记录建立的客户端连接和执行的语句。二进制日志记录所有更改数据的语句。主要用…...

测试:正交法设计测试用例

目录 一、什么是正交法 二、利用正交表设计测试用例 正交法设计测试用例的步骤 一、什么是正交法 正交法的目的是为了减少测试用例的数量&#xff0c;让尽可能少的用例覆盖两两组合。认识正交表。 最简单的正交表是L4(2^3)&#xff0c;含意如下&#xff1a; “L”代表正…...

zk基础—5.Curator的使用与剖析一

大纲 1.基于Curator进行基本的zk数据操作 2.基于Curator实现集群元数据管理 3.基于Curator实现HA主备自动切换 4.基于Curator实现Leader选举 5.基于Curator实现分布式Barrier 6.基于Curator实现分布式计数器 7.基于Curator实现zk的节点和子节点监听机制 8.基于Curator创…...

VSCode中结合DeepSeek使用Cline插件的感受

前言 听网上有传言说AI智能插件Cline非常的好用&#xff0c;而且相对Cursor而言还是免费的&#xff0c;捆绑的大模型选择也比较的广泛。所以&#xff0c;特意安装试用了一下。 我的采用IDE是VSCode&#xff0c;捆绑的大模型是最近比较火的DeepSeek。总体使用下来感觉非常的棒。…...

安卓开发工程师-Java 常用数据结构

1. Java 中的数组和集合有什么区别&#xff1f; 数组&#xff1a; 长度固定&#xff1a;一旦声明&#xff0c;长度不能改变。类型单一&#xff1a;只能存储相同类型的元素。存储效率高&#xff1a;底层是连续的内存空间&#xff0c;访问速度快。示例代码&#xff1a; int[] …...

thinkphp8.0上传图片到阿里云对象存储(oss)

1、开通oss,并获取accessKeyId、accessKeySecret <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><tit…...

Angular 2 模板语法详解

Angular 2 模板语法详解 引言 Angular 2 作为一款强大的前端框架,以其组件化的开发模式和高效的性能被众多开发者所青睐。模板语法是Angular 2中用于定义组件UI的关键部分。本文将详细介绍Angular 2的模板语法,帮助开发者更好地理解和运用这一功能。 模板语法概述 Angula…...

进行性核上性麻痹护理攻略:多维度守护健康

日常起居护理 保证患者居住环境安全&#xff0c;清除地面障碍物&#xff0c;避免患者跌倒。家具摆放固定且合理&#xff0c;方便患者活动。为患者准备宽松、舒适、易于穿脱的衣物&#xff0c;减轻穿衣时的困难。在饮食上&#xff0c;提供富含营养、易于吞咽的食物&#xff0c;…...

MessageQueue --- RabbitMQ WorkQueue

MessageQueue --- RabbitMQ WorkQueue 什么是WorkQueue如何分发RoundRobinFair dispatch (Prefetch) --- 能者多劳 什么是WorkQueue Work queues&#xff0c;任务模型。简单来说就是让多个消费者绑定到一个队列&#xff0c;共同消费队列中的消息。当消息处理比较耗时的时候&…...

Redis内存碎片详解!

目录 一、 什么是内存碎片&#xff1f;&#x1f914;二、 为什么 Redis 会有内存碎片呢&#xff1f;&#x1f937;‍♀️三、 如何查看 Redis 内存碎片的信息&#xff1f;&#x1f50d;四、 如何清理 Redis 内存碎片&#xff1f;&#x1f9f9;五、总结&#x1f4dd; &#x1f3…...

如何使用 Nginx 代理 Easysearch 服务

Nginx 是一个高性能的 HTTP 服务器和反向代理服务器&#xff0c;广泛用于负载均衡、缓存、SSL 终端和服务代理等场景。本篇将尝试使用 Nginx 代理 Easysearch 服务&#xff0c;方法同样适用于 Elasticsearch 和 Opensearch。 测试环境 Easysearch 集群版本为 1.10.0&#xff…...

用python输出OLED字模库的符号

提示&#xff1a;博主是小白&#xff0c;如有不足&#xff0c;望海涵和指出 在单片机上练习使用OLED显示屏时&#xff0c;可以看到有个OLED字模库 本文用python将这些字符打印出来&#xff0c;代码如下&#xff08;本文只适用与128*64的OLED&#xff0c;如果是其它OLED&#xf…...

【java】Class.newInstance()

在 Java 中&#xff0c;Class.newInstance()是一个用于创建类的新实例的方法。它调用类的无参构造函数来创建对象。然而&#xff0c;从 Java 9 开始&#xff0c;Class.newInstance()方法已经被标记为废弃&#xff0c;推荐使用其他替代方法。 Class.newInstance()的使用 Class.…...

Apache Arrow 使用

下述操作参考 Building Arrow C — Apache Arrow v20.0.0.dev267 安装依赖组件 sudo apt-get install \build-essential \ninja-build \cmake 下载源码 git clone --recursive --shallow-submodules gitgithub.com:apache/arrow.git 配置 创建build目录并且进入 mkdir a…...

第二届图像处理与人工智能国际学术会议(ICIPAI2025)

重要信息 时间&#xff1a;2025年4月18日-20日 地点&#xff1a;吉林-长春&#xff08;线上线下结合&#xff09; 官网&#xff1a;www.icipai.org 简介&#xff08;部分&#xff09; 主题 其他 图像处理与人工智能&#xff08;Image Processing & Artificial Intell…...

Kafka 消息堆积的原因有哪些?

Kafka 产生消息堆积的本质原因是&#xff1a; ⚠️ “消费速度 < 生产速度”&#xff0c;也就是&#xff1a;写入太快&#xff0c;处理太慢。 下面我从实际场景出发&#xff0c;帮你梳理出常见的几种堆积情况&#xff0c;结合原因和例子&#xff0c;便于你对号入座排查问题 …...

解决cline等免费使用deepseek模型的问题

OpenAI、OpenRouter、Claude等都无法在国内免费正常使用&#xff0c;cline作为在vscode中应对cursor比较好的替代方案&#xff0c;怎么使用免费Deepseek&#xff0c;最核心的是在点击模型名称打开配置以下几项&#xff1a; 1、打开VSCode左侧的Cline\Roo Cline插件面板 2、点…...

ROS多设备交互

ROS多设备连接同一个Master&#xff1a;ROS Master多设备连接-CSDN博客 在多个PC端连接同一个ROS Master后&#xff0c;接下来就可以实现不同设备之间的话题交流&#xff0c;Master主机端启动不同PC端的功能包等功能了 尽管多个PC端拥有不同的ROS工作空间&#xff0c;但是只要…...

浅谈 MVVM 模式

MVVM&#xff08;Model-View-ViewModel&#xff09; 是一种软件架构设计模式&#xff0c;旨在将用户界面&#xff08;UI&#xff09;与业务逻辑分离&#xff0c;从而提高代码的可维护性和可测试性。它在现代前端开发和桌面应用开发中得到了广泛应用&#xff0c;尤其是在构建复杂…...

flutter点击事件教程

在 Flutter 中&#xff0c;处理点击事件是非常常见的操作。Flutter 提供了多种方式来实现用户交互&#xff0c;比如按钮点击、手势检测等。下面是一个详细的教程&#xff0c;帮助你理解如何在 Flutter 中实现点击事件。 一、使用 onPressed 实现按钮点击事件 Flutter 提供了 E…...

[SAP SD] 常用事务码

在SAP系统中&#xff0c;事务码(Transaction Code)是一个具有特定功能的代码标识符&#xff0c;用于快速调用和执行SAP系统内的各种业务模块的功能 /NT-code: 关闭当前业务窗口&#xff0c;退回到SAP初始界面&#xff0c;进入对应的T-Code窗口 /OT-code: 新建SAP GUI窗口&…...

【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 的未来:从微服务到云原生的演进

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、引子&…...

保留格式地一键翻译英文ppt

我手头上有一个贝叶斯推断的英文ppt&#xff0c;假如我想翻译成中文&#xff0c;整合起来进行pre&#xff0c;你会怎么做&#xff1f; 1&#xff0c;复制粘贴型&#xff1a; 在翻译软件与源文件ppt之间不断流转&#xff0c;效率太低 2&#xff0c;office ppt自带翻译插入整合…...

晶晨S905L3S/S905L3SB_安卓9.0_10秒开机_通刷-线刷固件包

晶晨S905L3S&#xff0f;S905L3SB_安卓9.0_10秒开机_通刷-线刷固件包 线刷方法&#xff1a;&#xff08;新手参考借鉴一下&#xff09; 使用晶晨刷机工具USB_Burning_Tool进行刷机&#xff1b;请使用Amlogic USB Burning Tool v2.2.5或v2.2.7&#xff08;晶晨线刷烧录工具v2.2…...

Android Transition转场动效使用全解析

Transition的使用和原理 项目效果 1. 简述 Android 4.4.2 中引入了 Transition 过渡动画&#xff0c;不过功能比较简单。在 Android 5.0 的 Material Design 中引入更完整和强大的 Transition 框架。通过Transition可以实现&#xff1a; 同一个页面中的场景过渡动画Activit…...

第九章Python语言高阶加强-面向对象篇

目录 一.初始对象 二.成员方法 1.成员变量和成员方法 三.类和对象 四.构造方法 五.其他内置方法&#xff08;魔术方法&#xff09; 1.__str__字符串方法 2.__lt__小于符号比较方法 3.__le__小于等于比较符号方法 4.__eq__比较运算符实现方法 六.封装 七.继承 1.继承…...

AI重构SEO关键词智能布局

内容概要 随着人工智能技术在搜索引擎优化领域的深入发展&#xff0c;AI驱动的关键词智能布局正在重塑传统SEO策略的核心逻辑。通过整合自然语言处理、深度学习与语义分析技术&#xff0c;现代SEO系统已形成包含智能分词、意图解码、动态优化的三维技术框架&#xff0c;使关键…...

言同数字:法新社AFP海外新闻媒体发稿成功案例——出海品牌背书必备

作者&#xff1a;言同数字全球传播团队 一、品牌困境&#xff1a;当中国技术遇上海外认知壁垒 案例背景&#xff1a; 某中国光伏储能企业&#xff08;应保密要求匿名&#xff0c;代号"GreenTech"&#xff09;&#xff0c;其家用储能系统在欧洲市场遭遇&#xff1…...

第三章 react redux的学习之redux和react-redux,@reduxjs/toolkit依赖结合使用

redux系列文章目录 第一章 简单学习redux,单个reducer 第二章 简单学习redux,多个reducer 第四章 react-redux&#xff0c;reduxjs/toolkit依赖&#xff0c;学习 第五章 两张图告诉你redux常使用的api有哪些 前言 前面两章&#xff0c;我们是只使用的redux的依赖。 本章…...

【HTML】纯前端网页小游戏-戳破彩泡

分享一个简单有趣的网页小游戏 - 彩色泡泡爆破。玩家需要点击屏幕上随机出现的彩色泡泡来得分。 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…...

【Python使用】嘿马云课堂web完整实战项目第3篇:增加数据,修改数据【附代码文档】

教程总体简介&#xff1a;项目概述 项目背景 项目的功能构架 项目的技术架构 CMS 什么是CMS CMS需求分析与工程搭建 静态门户工程搭建 SSI服务端包含技术 页面预览开发 4 添加“页面预览”链接 页面发布 需求分析 技术方案 测试 环境搭建 数据字典 服务端 前端 数据模型 页面原…...