【Spring Cloud 八】Spring Cloud Gateway网关

gateway网关

  • 系列博客
  • 背景
  • 一、什么是Spring Cloud Gateway
  • 二、为什么要使用Spring Cloud Gateway
  • 三、 Spring Cloud Gateway 三大核心概念
    • 4.1 Route(路由)
    • 4.2 Predicate(断言)
    • 4.3 Filter(过滤)
  • 五、Spring Cloud Gateway是如何工作的
  • 四、 如何使用Spring Cloud Gateway进行服务路由
    • 4.1搭建服务A
      • pom文件
      • yml配置文件
      • 启动类
      • controller类
    • 4.2 搭建服务B
      • pom文件
      • yml配置文件
      • 启动类
      • controller类
    • 4.3搭建Spring Cloud Gateway服务
      • pom文件
      • yml配置文件
      • 启动类
    • 4.4启动项目
    • 4.5动态路由
  • 五、gateway过滤器
    • 5.1自定义全局过滤器
  • 六、使用gateway做token校验
    • 6.1实现的大致流程示意图:![](https://img-blog.csdnimg.cn/3001fa8b45324a61a95e0c06263528a7.png)
    • 6.2实现代码
      • gateway服务
        • pom.xml文件
        • TokenCheckFilter类
      • login-service服务
        • pom.xml文件
        • LoginController类
      • 访问测试
  • 总结
  • 升华

系列博客

【Spring Cloud一】微服务基本知识
【Spring Cloud 三】Eureka服务注册与服务发现
【Spring Cloud 四】Ribbon负载均衡
【Spring Cloud 五】OpenFeign服务调用
【Spring Cloud 六】Hystrix熔断
【Spring Cloud 七】Sleuth+Zipkin 链路追踪

背景

在项目中是使用了Gateway做统一的请求的入口,以及统一的跨域处理以及统一的token校验。但是这些工作都是之前的同事来做的,正好在新项目中也需要使用其进行统一的token校验。本着对Gateway更精进一步所以博主就对Gateway进行了较为全面的学习了解,包括动态路由、自定义过滤器、token校验和续活。

一、什么是Spring Cloud Gateway

Spring Cloud Gateway提供一种简单有效的方法来路由到对应的API上,并可以提供一些额外的功能,安全性、监控、度量、负载等等。

我们可以这样理解,Spring Cloud Gateway将该项目中所有服务对外提供的API聚集起来,并向外提供唯一的入口,同时提供了一些额外的功能,安全性、监控、度量、负载等等。

没使用Spring Cloud Gateway 之前的示意图
在这里插入图片描述
使用Spring Cloud Gateway之后的示意图:
在这里插入图片描述

二、为什么要使用Spring Cloud Gateway

  1. 统一入口并解除客户端与服务端的耦合:在微服务架构中,每个微服务都有自己入口,在没有它的时候客户端需要大量微服务的地址,会照成复杂的客户端代码。并且会暴露服务端的细节,如果服务端发生变化,可能会影响到客户端代码,导致维护困难。
  2. 动态路由和负载均衡:Spring Cloud GateWay 提供动态路由来应对当;网关来能够在多个服务实例之间进行负载均衡,提供整个系统的高可用。
  3. 请求过滤:在微服务架构中,可能需要对请求进行鉴权、身份验证一个中心化的地方来处理这些请求过滤和处理逻辑可以减少重复的代码和逻辑。
  4. 反应式和高性能:由于微服务架构的复杂性,需要一个能够处理高并发请求的解决方案,以确保系统在高负载情况下保持稳定。Spring Cloud Gateway是基于webFlux框架实现的,而webFlux框架底层使用了高性能Reactor模式通信框架的Netty。

三、 Spring Cloud Gateway 三大核心概念

4.1 Route(路由)

路由是由一个ID、一个目的URI、一组断言、一组Filter组成。
如果路由断言为真,那么说明这个路由被匹配上了。

4.2 Predicate(断言)

是一个java8函数断言。输入类型是一个Spring Framewordk ServerWebExchange。可以让你匹配HTTP上的任何请求。比如请求头和参数。

4.3 Filter(过滤)

是Spring WebFilter的实例,Spring Cloud Gateway中的Filter分为两种,分贝是Gateway Filter和Global Filter(一个是针对某一个路由的filter,例如对某一个接口做限流;一个是针对全局的filter,例如token校验,ip黑名单)。过滤器Filter将会对请求和响应进行修改处理。

五、Spring Cloud Gateway是如何工作的

Spring 官网
在这里插入图片描述
客户端向Spring Cloud Gateway发出请求。如果网关处理器映射器确定请求与路由匹配,则会将其发送到网关web处理器。它通过特定的过滤器链来运行请求。过滤器被虚线分割的原因是过滤器可以在发送代理请求之前和之后运行对应的逻辑。

四、 如何使用Spring Cloud Gateway进行服务路由

示例项目示意图:
在这里插入图片描述
备注:Eureka的搭建可以参考这篇博客:【Spring Cloud 三】Eureka服务注册与服务发现

这里之所以使用Eureka是为了之后做动态路由和负载均衡。

4.1搭建服务A

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.12.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.wangwei</groupId><artifactId>login-service</artifactId><version>0.0.1-SNAPSHOT</version><name>login-service</name><description>login-service</description><properties><java.version>8</java.version><spring-cloud.version>Hoxton.SR12</spring-cloud.version></properties><dependencies><!--		<dependency>-->
<!--			<groupId>org.springframework.boot</groupId>-->
<!--			<artifactId>spring-boot-starter-data-redis</artifactId>-->
<!--		</dependency>--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

yml配置文件

server:port: 8081spring:application:name: login-serviceeureka:client:service-url:defaultZone: http://localhost:8761/eurekaregister-with-eureka: true #设置为fasle 不往eureka-server注册,默认为truefetch-registry: true #应用是否拉取服务列表到本地registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答instance: #实例的配置instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}hostname: localhost #主机名称或者服务ipprefer-ip-address: true #以ip的形式显示具体的服务信息lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔

启动类

@SpringBootApplication
@EnableEurekaClient
public class LoginServiceApplication {public static void main(String[] args) {SpringApplication.run(LoginServiceApplication.class, args);}}

controller类

@RestController
public class LoginController {@GetMapping("doLogin")public String doLogin(){return "登陆成功";}
}

4.2 搭建服务B

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.12.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.wangwei</groupId><artifactId>teacher-service</artifactId><version>0.0.1-SNAPSHOT</version><name>teacher-service</name><description>teacher-service</description><properties><java.version>8</java.version><spring-cloud.version>Hoxton.SR12</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

yml配置文件

server:port: 8082spring:application:name: teacher-serviceeureka:client:service-url:defaultZone: http://localhost:8761/eurekaregister-with-eureka: true #设置为fasle 不往eureka-server注册,默认为truefetch-registry: true #应用是否拉取服务列表到本地registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答instance: #实例的配置instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}hostname: localhost #主机名称或者服务ipprefer-ip-address: true #以ip的形式显示具体的服务信息lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔

启动类

@SpringBootApplication
@EnableEurekaClient
public class TeacherServiceApplication {public static void main(String[] args) {SpringApplication.run(TeacherServiceApplication.class, args);}}

controller类

@RestController
public class TeacherController {@GetMapping("teach")public String teach(){return "教书学习";}
}

4.3搭建Spring Cloud Gateway服务

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.12.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.wangwei</groupId><artifactId>gateway-server</artifactId><version>0.0.1-SNAPSHOT</version><name>gateway-server</name><description>gateway-server</description><properties><java.version>8</java.version><spring-cloud.version>Hoxton.SR12</spring-cloud.version></properties><dependencies>
<!--		<dependency>-->
<!--			<groupId>org.springframework.boot</groupId>-->
<!--			<artifactId>spring-boot-starter-data-redis</artifactId>-->
<!--		</dependency>-->
<!--		<dependency>-->
<!--			<groupId>org.springframework.boot</groupId>-->
<!--			<artifactId>spring-boot-starter-data-redis-reactive</artifactId>-->
<!--		</dependency>--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

yml配置文件

server:port: 81 #?????80spring:application:name: gateway-servercloud:gateway:enabled: trueroutes:- id: login-service-route # 路由的id  保持唯一uri: http://localhost:8081 #uri统一资源标识符  url 统一资源定位符#uri: lb://login-service #??lb负载均衡predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上- Path=/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上- id: teacher-service-routeurl:  http://localhost:8082predicates:- Path=/teacheureka:client:service-url:defaultZone: http://localhost:8761/eurekaregister-with-eureka: true #设置为fasle 不往eureka-server注册,默认为truefetch-registry: true #应用是否拉取服务列表到本地registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答instance: #实例的配置instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}hostname: localhost #主机名称或者服务ipprefer-ip-address: true #以ip的形式显示具体的服务信息lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔

启动类

@SpringBootApplication
@EnableEurekaClient
public class GatewayServerApplication {public static void main(String[] args) {SpringApplication.run(GatewayServerApplication.class, args);}}

4.4启动项目

依次启动Eureka服务,gateway服务和两个服务A和服务B。

进行调用服务A和服务B:
如下图所示通过gateway的ip+端口+路径调用到对应的服务A中和服务B中。

在这里插入图片描述

在这里插入图片描述

4.5动态路由

在微服务中通常一个服务的实例有多个,那我们网关如何做负载均衡呢?
gateway帮我们做了很多东西,只要我们集成注册中心(nacos、Eureka、zoomkeeper)并添加对应的配置gateway就可以自动帮我们进行负载均衡。

方式一:
添加对应的配置信息,来开启动态路由

    gateway:enabled: trueroutes:- id: login-service-route # 路由的id  保持唯一uri: http://localhost:8081 #uri统一资源标识符  url 统一资源定位符#uri: lb://login-service #??lb负载均衡predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上- Path=/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上- id: teacher-service-routeuri:  http://localhost:8082predicates:- Path=/teachdiscovery:locator:enabled: true #开启动态路由  开启通过应用名称找到服务的功能lower-case-service-id: true  # 开启服务名称小写

请求服务时需要带上对应的服务名称
在这里插入图片描述

方式二:
添加对应的配置:uri: lb://login-service #lb负载均衡

    gateway:enabled: trueroutes:- id: login-service-route # 路由的id  保持唯一#uri: http://localhost:8081 #uri统一资源标识符  url 统一资源定位符uri: lb://login-service #lb负载均衡predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上- Path=/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上- id: teacher-service-routeuri:  http://localhost:8082predicates:- Path=/teach

在这里插入图片描述

推荐使用方式一进行统一的配置负载均衡。

五、gateway过滤器

过滤器按照作用范围可以分为两种,Gateway Filter和Global Filter。

Gateway Filter:网关过滤器需要通过 spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过 spring.cloud.default-filters 配置在全局,作用在所有路由上。

Global Filter:全局过滤器,不需要配置路由,系统初始化作用在所有路由上。

全局过滤器一般用于:统计请求次数、限流、token校验、ip黑名单拦截等。

5.1自定义全局过滤器

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {/*** 过滤的方法* 职责链模式* 网关里面有使用 mybatis的二级缓存有变种职责链模式* @param exchange* @param chain* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//针对请求的过滤 拿到请求 header url 参数。。。ServerHttpRequest request = exchange.getRequest();//HttpServletRequest 这个是web里面的//ServerHttpRequest webFlux里面 响应式里面的String path=request.getURI().getPath();System.out.println(path);HttpHeaders headers=request.getHeaders();System.out.println(headers);String name = request.getMethod().name();String hostString = request.getHeaders().getHost().getHostString();System.out.println(hostString);//响应相关数据ServerHttpResponse response = exchange.getResponse();//微服务 肯定是前后端分离的 一般前后端通过数据传输是json格式//{"code":200,"msg":"ok"}//设置编码 响应头response.getHeaders().set("content-type","application/json;charset=utf-8");//组装业务返回值HashMap<String ,Object> map=new HashMap<>(4);map.put("code", HttpStatus.UNAUTHORIZED.value());map.put("msg","你未授权");ObjectMapper objectMapper=new ObjectMapper();//把一个map转换为字节byte[] bytes = new byte[0];try {bytes = objectMapper.writeValueAsBytes(map);} catch (JsonProcessingException e) {throw new RuntimeException(e);}//通过buffer工厂将字节数组包装成一个数据报DataBuffer wrap = response.bufferFactory().wrap(bytes);return response.writeWith(Mono.just(wrap));//放行,到下一个过滤器//return chain.filter(exchange);}/*** 制定顺序的方法* 越小越先执行* @return*/@Overridepublic int getOrder() {return 0;}
}

请求接口进行访问,可以发现当前请求已经被拦截下来。

在这里插入图片描述

六、使用gateway做token校验

6.1实现的大致流程示意图:

6.2实现代码

gateway服务

pom.xml文件

在pom文件中新增redis的依赖

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

注意博主是在本机安装的redis所以不需要在服务中添加对应的redis配置。

TokenCheckFilter类

新建TokenCheckFilter类并实现全局过滤器和Ordered

package com.wangwei.gatewayserver.filter;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
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.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.Arrays;
import java.util.HashMap;
import java.util.List;@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {/*** 指定好放行的路径,白名单*/public static final List<String> ALLOW_URL= Arrays.asList("/doLogin","/myUrl");@Autowiredprivate StringRedisTemplate redisTemplate;/*** 和前端约定好 一般放在请求头类里面一般key为 Authorization value bearer token* 1.拿到请求url* 2.判断放行* 3.拿到请求头* 4.拿到token* 5.校验* 6.放行/拦截* @param exchange* @param chain* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();if (ALLOW_URL.contains(path)) {return chain.filter(exchange);}//检查List<String> authorization = request.getHeaders().get("Authorization");if (!CollectionUtils.isEmpty(authorization)){String token = authorization.get(0);if(StringUtils.hasText(token)){//约定好的有前缀的bearer tokenString realToken = token.replaceFirst("bearer ", "");if (StringUtils.hasText(realToken)&&redisTemplate.hasKey(realToken)){return chain.filter(exchange);}}}//拦截ServerHttpResponse response = exchange.getResponse();response.getHeaders().set("content-type","application/json;charset=utf-8");//组装业务返回值HashMap<String ,Object> map=new HashMap<>(4);map.put("code", HttpStatus.UNAUTHORIZED);map.put("msg","未授权");ObjectMapper objectMapper=new ObjectMapper();//把一个map转换为字节byte[] bytes = new byte[0];try {bytes = objectMapper.writeValueAsBytes(map);} catch (JsonProcessingException e) {throw new RuntimeException(e);}//通过buffer工厂将字节数组包装成一个数据报DataBuffer wrap = response.bufferFactory().wrap(bytes);return response.writeWith(Mono.just(wrap));}@Overridepublic int getOrder() {return 0;}
}

login-service服务

pom.xml文件

在pom文件中新增redis的依赖

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

注意博主是在本机安装的redis所以不需要在服务中添加对应的redis配置。

LoginController类

在LoginController类中新增doLogin方法

@Autowiredpublic StringRedisTemplate redisTemplate;@PostMapping("doLogin")public String doLogin(@RequestBody User user){//tokenString token= UUID.randomUUID().toString();//存起来redisTemplate.opsForValue().set(token,user.toString(), Duration.ofSeconds(7200));return token;}

访问测试

http://localhost:81/doLogin

由于在gateway将该/doLogin放入了白名单,所以该请求不会进行token校验,发送请求成功之后会返回token
在这里插入图片描述
访问http://localhost:81/teach,并在请求头中添加token,最后可以看到请求访问成功。

在这里插入图片描述

如果我们不在请求头中添加对应的token或者token为错误token,那么gateway会将请求进行拦截。

在这里插入图片描述

总结

  1. gateway在微服务中起到了很重要的作用,作为项目请求的统一入口。
  2. gateway也体现了我们软件设计的复用思想,可以统一进行跨域处理,进行token校验。
  3. gateway比较重要的一块是关于他的过滤器,通过实现过滤器的接口,可以自定义过滤器,提供了很强大的可扩展支持

升华

通过学习gateway不光是学习gateway更重要的是学习软件设计思想。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/40696.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用Kali Linux进行密码破解?

今天我们探讨Kali Linux的应用&#xff0c;重点是如何使用它来进行密码破解。密码破解是渗透测试中常见的任务&#xff0c;Kali Linux为我们提供了强大的工具来帮助完成这项任务。 1. 密码破解简介 密码破解是一种渗透测试活动&#xff0c;旨在通过不同的方法和工具来破解密码…

力扣初级算法(数组拆分)

力扣初级算法&#xff08;数组拆分&#xff09; 每日一算法&#xff1a; 力扣初级算法&#xff08;数组拆分&#xff09; 学习内容&#xff1a; 1.问题描述 给定长度为 2n 的整数数组 nums &#xff0c;你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) …

机器人CPP编程基础-03变量类型Variables Types

机器人CPP编程基础-02变量Variables 全文AI生成。 C #include<iostream>using namespace std;main() {int a10,b35; // 4 bytescout<<"Value of a : "<<a<<" Address of a : "<<&a <<endl;cout<<"Val…

Vue+ElementUI实现选择指定行导出Excel

这里记录一下&#xff0c;今天写项目时 的一个需求&#xff0c;就是通过复选框选中指定行然后导出表格中选中行的Excel表格 然后这里介绍一个工具箱(模板)&#xff1a;vue-element-admin 将它拉取后&#xff0c;运行就可以看到如下界面&#xff1a; 这里面的很多功能都已经实现…

【NAS群晖drive异地访问】使用cpolar远程访问内网Synology Drive「内网穿透」

文章目录 前言1.群晖Synology Drive套件的安装1.1 安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用 2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用 3. 结语 前言 群晖作为专业的数据存储中心&…

jupyter切换conda虚拟环境

环境安装 conda install nb_conda 进入你想使用的虚拟环境&#xff1a; conda activate your_env_name 在你想使用的conda虚拟环境中&#xff1a; conda install -y jupyter 在虚拟环境中安装jupyter&#xff1a; conda install -y jupyter 重启jupyter 此时我们已经把该安装…

也许你正处于《孤注一掷》中的“团队”,要留心了

看完这部电影&#xff0c;心情久久不能平静&#xff0c;想了很多&#xff0c;倒不是担心自己哪天也成为“消失的yaozi”&#xff0c;而是在想&#xff0c;我们每天所赖以生存的工作&#xff0c;跟电影里他们的工作比&#xff0c;差别在哪里呢&#xff1f; 目录 1. 产品的本质…

【QT+ffmpeg】QT+ffmpeg 环境搭建

1.qt下载地址 download.qt.io/archive/ 2. win10sdk 下载 https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/ 安装 debug工具路径 qtcreater会自动识别 调试器选择

74、75、76——tomcat项目实战

tomcat项目实战 tomcat 依赖 java运行环境,必须要有jre , 选择 jdk1.8 JvmPertest 千万不能用 kyj易捷支付 项目机器 选择 一台机器 ,安装jdk1.8的机器下载tomcat的包 上传到机器,解压tomcattomcat文件 bin文件夹: 启动文件 堆栈配置文件 catalina.sh JAVA_OPTS="-Xm…

gitlab合并新项目和分支切换

一、新建项目 1、创建空白项目 2、先创建一个群组 3、编写群组信息 4、创建群组完成以后新建项目 ​​​​​​​ 二、将代码推送到gitlab 1、初始化 git init 2、关联gitlab地址 # 比如:http://192.168.139.128:7070/cloud/obwt_cloud.git git remote add origin <你…

FreeRTOS qemu mps2-an385 bsp 移植制作 :串口打印篇

相关文章 FreeRTOS qemu mps2-an385 bsp 移植制作 &#xff1a;环境搭建篇 FreeRTOS qemu mps2-an385 bsp 移植制作 &#xff1a;系统启动篇 FreeRTOS qemu mps2-an385 bsp 移植制作 &#xff1a;系统运行篇 开发环境 Win10 64位 VS Code&#xff0c;ssh 远程连接 ubuntu …

如何使用Python编写小游戏?

大家好&#xff0c;我是沐尘而生&#xff0c;如果你是一个热爱编程的小伙伴&#xff0c;又想尝试游戏开发&#xff0c;那么这篇文章一定能满足你的好奇心。不废话&#xff0c;让我们马上进入Python游戏开发的精彩世界吧&#xff01; Python游戏开发的魅力 编写小游戏不仅仅是锻…

BUUCTF [安洵杯 2019]easy_serialize_php 1 详细讲解

题目来自buuctf&#xff0c;这是一题关于php序列化逃逸的题 1. 题目 题目给出的代码 <?php$function $_GET[f];function filter($img){$filter_arr array(php,flag,php5,php4,fl1g);$filter /.implode(|,$filter_arr)./i;return preg_replace($filter,,$img); }if($_S…

时序预测 | MATLAB实现基于CNN-BiGRU卷积双向门控循环单元的时间序列预测-递归预测未来(多指标评价)

时序预测 | MATLAB实现基于CNN-BiGRU卷积双向门控循环单元的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于CNN-BiGRU卷积双向门控循环单元的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍 MATLAB实现基于…

Dubbo基础学习

目录 第一章、概念介绍1.1&#xff09;什么是RPC框架1.2&#xff09;什么是分布式系统1.3&#xff09;Dubbo概述1.3&#xff09;Dubbo基本架构 第二章、服务提供者(直连)2.1&#xff09;目录结构和依赖2.2&#xff09;model层2.3&#xff09;service层2.4&#xff09;resources…

【eNSP】交换机(vlan和vlan间通信)

【eNSP】交换机&#xff08;vlan和vlan间通信&#xff09; 原理术语过程 实验根据图片连接模块配置设备名称和IP地址配置交换机交换机链路指定sw1配置sw2配置 设置网关交换机互联实验设置查看设置结果 ospf配置 原理 HUB集线器&#xff1a;它的作用可以简单的理解为将一些机器…

【算法系列篇】双指针

文章目录 前言什么是双指针算法1.移动零1.1 题目要求1.2 做题思路1.3 Java代码实现 2.复写零2.1 题目要求2.2 做题思路2.3 Java代码实现 3.快乐数3.1 题目要求3.2 做题思路3.3 Java代码实现 4.盛最多水的容器4.1 题目要求4.2 做题思路4.3 Java代码实现 5.有效三角形的个数5.1 题…

k8s 自身原理 4

前面咱们分享了 mater 和 worker 节点里面都有哪些组件&#xff0c;他们又是各自主要负责的工作是什么&#xff0c;现在我们心里应该都有数了吧 master 节点&#xff1a; etcd 存储资源配置&#xff0c;ApiServer 提供 RESTful Api 用于交互&#xff0c;scheduler 用于调度 p…

VS2019 + Qt : setToolTip的提示内容出现乱码

VS2019 Qt : setToolTip的提示内容出现乱码 在使用setToolTip()时&#xff0c; setToolTip(QString("asd你好&#xff01;");标签提示只有英文是对的&#xff0c;中文是乱码&#xff01; 应该是编码出了问题。默认情况下&#xff0c;Qt使用的是UTF-8编码&#xf…

踩坑经验:JavaScript 中的 for...of 和 for...in 循环

在 JavaScript 编程中&#xff0c;for...of 和 for...in 是常用的循环语法&#xff0c;但它们在使用时可能会引发一些意想不到的问题。本文将分享我在使用这两种循环时所遇到的坑和经验。 两者的区别&#xff1a; 适用对象类型&#xff1a; for…of&#xff1a;主要用于遍历可…