springcloud实践二:gateway网关详解
ztj100 2024-10-29 18:18 17 浏览 0 评论
微服务框架当前大行其道,网关在微服务架构中是一个非常重要的部分,网关一般作为项目的统一请求入口提供给前端开发人员,前端开发人员不用知道每个微服务的请求地址。网关可以统一对所有请求做过滤、限流、负载均衡、监控等处理,而不必在每个微服务项目重复处理请求。网关配合注册中心也可以很好的实现微服务的动态扩容,只需要在网关将请求路由转发到注册中心的微服务上即可,由注册中心进行负载均衡处理。
Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到api,并为它们提供额外功能,例如:安全性、监控和弹性控制。
Spring Cloud Gateway基于Spring Boot 2.x, Spring WebFlux, 由Spring Webflux提供Netty的运行环境。它不能在传统Servlet容器中工作,也不能在构建为WAR时工作。
Spring Cloud Gateway底层通信框架基于Netty,不同于第一代网关Zuul,gateway是非阻塞的,并且是spring官方开发的,所以拥有强劲的性能。
下面介绍下Spring Cloud Gateway的实际使用。
spring-boot和spring-cloud版本
- spring-cloud Hoxton.SR8
- spring-boot 2.3.3.RELEASE
- spring-cloud-starter-gateway 2.2.5.RELEASE(不需要在pom指定,跟随spring cloud即可)
下面我会演示下实际的项目代码以及效果,我想尽量把需要的代码都贴出来,所以会写的比较细,如果已经掌握的部分可以跳着看即可。
我们现在新建了一个简单的父项目和网关子项目
项目结构
其中父项目只有一个pom文件,引入了一个公共工具依赖,定义了spring boot和spring cloud的版本,子项目中就不需要定义spring子项目的版本了。
父项目pom.xml文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.huangtl</groupId>
<artifactId>springcloud-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>gateway</module>
</modules>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--因为有些依赖仅在Spring Milestones中提供,所以要配置这个仓库地址-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
gateway项目
1.引入spring-cloud-starter-gateway依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud-parent</artifactId>
<groupId>com.huangtl</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
</project>
注意,spring-cloud-gateway是不支持spring-boot-starter-web依赖的,一般我们会把网关单独建一个项目。
2.配置文件
在resources目录下新建application.yml,我们将在这里配置大部分的gateway配置项,在配置这些配置项之前,我们先了解几个gateway配置项的概念。
- Route 路由配置项,包含一个id(唯一标志)、uri(请求转发到该uri)、predicates(请求的匹配条件)、filters(过滤请求)
- Predicate 请求的匹配条件,可以配置请求路径、请求头或参数、请求时间等的匹配条件,若符合条件则匹配路由
- Filter 可以修改请求和响应数据,比如修改请求路径、修改请求头、删除请求参数、修改响应头、设置断路器等等
predicate的配置项有快捷和完整两种写法:
快捷写法格式:- 表达式名称 = 参数(多参数可逗号隔开)
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- Cookie=mycookie,mycookievalue
完整写法用 - name 加上 args参数来配置,上面快捷写法对应的完整写法如下:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue
我们接下去的测试都会用快捷的写法。
3.新建一个springboot启动类
package com.huangtl.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApp {
public static void main(String[] args) {
SpringApplication.run(GatewayApp.class, args);
}
}
网关项目基本结构完成。
下游测试项目
1.我们新建一个项目用于网关的下游项目,即网关会将匹配的请求转发到该项目。我们新建一个user-service的服务项目。
我们在user-service项目pom.xml文件只引入一个springboot web的依赖,用于访问接口即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.配置文件
在resources目录下新建application.yml,简单配置下端口
server:
port: 8092
3.新建一个启动类
@SpringBootApplication
public class UserServiceApp {
public static void main(String[] args) {
SpringApplication.run(UserServiceApp.class, args);
}
}
整体项目结构
下面开始测试gateway的各种配置及效果
为了方便测试我们在用户服务下游项目中建立两个接口类
PrefixController.java (带“/user-api”前缀)
@RestController
@RequestMapping("/user-api")
public class PrefixController {
@RequestMapping("/")
public String hello(){
return "这是用户/user-api服务";
}
@RequestMapping("/a")
public String a(){
return "这是用户/user-api/a服务";
}
@RequestMapping("/b")
public String b(){
return "这是用户/user-api/b服务";
}
@RequestMapping("/get/a")
public String geta(){
return "这是用户/user-api/get/a服务";
}
@RequestMapping("/delete/a")
public String deletea(){
return "这是用户/user-api/delete/a服务";
}
}
RootController.java (根目录)
@RestController
@RequestMapping("/")
public class RootController {
@RequestMapping("/")
public String hello(){
return "这是用户/服务";
}
@RequestMapping("/a")
public String a(){
return "这是用户/a服务";
}
@RequestMapping("/b")
public String b(){
return "这是用户/b服务";
}
@RequestMapping("/get/a")
public String geta(){
return "这是用户/get/a服务";
}
@RequestMapping("/delete/a")
public String deletea(){
return "这是用户/delete/a服务";
}
}
用户服务项目完成了,启动用户项目,我们开始在网关项目测试配置请求匹配表达式和过滤器,我就不贴网关项目的端口8091配置了,贴主gateway配置相关的代码。
predicates请求匹配表达式配置示例
- 表达式一:Path
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- Path=/user-api/**
效果:匹配请求为/user-api/开头的请求,并转发到http://localhost:8092/user-api/**
可以逗号隔开匹配多个请求
- Path=/user-api/get/**,/user-api/delete/**
- 表达式二:Method
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- Method=GET,POST #匹配请求方法
匹配请求的http方法为get和post的请求
- 表达式三:Host
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- Host=**.somehost.org,**.anotherhost.org
匹配请求的主机头白名单,多个逗号隔开
- 表达式四:Header
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- Header=X-Request-Id, \d+
请求头部匹配,需要两个参数逗号隔开,后一个参数为正则表达式
- 表达式五:Cookie
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- Cookie=mycookie,mycookievalue
匹配cookie名称为mycookie且值为mycookievalue的请求,后一个参数为正则表达式
- 表达式六:Before
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Before=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]
匹配2017年1月20日之前发出的请求,很适合做如临时的维护接口
- 表达式七:After
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- After=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]
匹配2017年1月20日以后发出的请求
- 表达式八:Between
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- Between=2017-01-20T17:42:47.789-07:00[Asia/Shanghai], 2017-01-21T17:42:47.789-07:00[Asia/Shanghai]
此路由匹配在2017年1月20日17点42分之后和2017年1月21日17点42分之前发出的请求。这对于维护窗口非常有用。
- 表达式九:Query
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- Query=name
匹配查询参数包含name的请求如 http://localhost:8091/user-api/delete/a?name=11 。
也可以设置同时匹配查询参数和参数值,如下配置意味着匹配查询参数为name,值为zhangsan的请求
- Query=name, zhangsan
- 表达式十:RemoteAddr
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- RemoteAddr=192.168.1.1/24
请求ip匹配,ip/子网掩码
- 表达式十一:自定义条件
我们模仿Path条件来自定义一个表达式
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- My=/user-api/get/a
注意我们自定义表达式命名为“My”,我们需要新建一个类名必须为MyRoutePredicateFactory的条件表达式工厂类,需继承AbstractRoutePredicateFactory抽象类,重写apply方法。
package com.huangtl.gateway.factory;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* 自定义路由匹配表达式
* @Description 类名以前缀+RoutePredicateFactory的方式,比如MyRoutePredicateFactory,配置里
* @Author huangtl
**/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
public MyRoutePredicateFactory() {
super(Config.class);
}
/**
* 可以参考官方自带的一些表达式{@link org.springframework.cloud.gateway.handler.predicate}
* @param config
* @return
*/
@Override
public Predicate<ServerWebExchange> apply(Config config) {
// grab configuration from Config object
return exchange -> {
//根据exchange的一些信息判断是否通过
ServerHttpRequest request = exchange.getRequest();
//判断请求参数是否和配置的信息匹配
if(request.getPath().value().equals(config.getCustomPath())){
return true;
}
return false;
};
}
//需要加此方法apply方法的config才能获取到配置的值,官方文档示例坑爹
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("customPath");
}
public static class Config {
//Put the configuration properties for your filter here
private String customPath;
public String getCustomPath() {
return customPath;
}
public void setCustomPath(String customPath) {
this.customPath = customPath;
}
@Override
public String toString() {
return customPath;
}
}
}
注意继承AbstractRoutePredicateFactory类时的泛型指定了本类的Config配置。Config类定义了一个customPath属性,对应配置文件中的My对应的值
- My=/user-api/get/a
在apply重写方法中可以通过config.getCustomPath()取到该配置值,然后根据请求地址和该值做匹配判断即可。注意需要重写shortcutFieldOrder方法把customPath参数注册进去才可以。
filters过滤器配置示例
- 过滤器一:PrefixPath
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092 #具体地址方式
predicates:
- Path=/get/**
filters:
- PrefixPath=/user-api
会在请求地址前面加上PrefixPath设置的值,如 /get/** 会转发到下游项目的接口/user-api/get/** 。
可以看到我们请求http://localhost:8091/get/a,并没有转发到下游的/get/a接口,而是请求到了/user-api/get/a接口。
- 过滤器二:RewritePath
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- RewritePath=/user-api, /
两个参数逗号隔开,请求路径上第一个参数字符串替换为第二个参数字符串,如上配置的效果:将 /user-api/get/a 变成 /get/a
- 过滤器三:StripPrefix
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- StripPrefix=1
清除掉路径前缀数量:
值为1效果:/user-api/get/a ==> /get/a
值为2效果:/user-api/get/a ==> /a
- 过滤器四:SetPath
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- SetPath=/a
重新设置路径,效果:/user-api/a 变成 /a
可以看到,匹配的请求路径都被转发到下游项目的 /a 接口去了
- 过滤器五:CircuitBreaker
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fallback2
断路器配置,这里用的是完整写法,当请求下游项目发生异常或无法请求时,会改成请求fallbackUri配置的地址代替,上例中会请求到网关项目的/fallback2接口。
@RequestMapping("/fallback2")
public String fallback2(){
return "这是fallback2服务";
}
注意- name对应的CircuitBreaker不能更改,需要添加spring-cloud-starter-circuitbreaker-reactor-resilience4j依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
也有Hystrix断路器的用法,需要添加spring-cloud-starter-netflix-hystrix依赖,因为Netflix已经将Hystrix置于仅维护模式,spring官方建议使用Resilience4J。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Hystrix断路器配置
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback2
测试效果,我们不启动user-service下游服务项目,请求网关时会请求到断路器配置接口。
- 过滤器六:AddRequestHeader
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- AddRequestHeader=X-Request-name, huangtl
添加请求头,逗号隔开,第一个值为请求头名称,第二个值为请求头的值。
- 过滤器七:AddRequestParameter
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- AddRequestParameter=name, huangtl
添加请求参数,逗号隔开,第一个值为请求参数名称,第二个值为请求参数的值。
- 过滤器八:AddResponseHeader
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- AddResponseHeader=X-Response-name, huangtl
添加响应头,逗号隔开,第一个值为响应头名称,第二个值为响应头的值。
- 过滤器九:MapRequestHeader
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- MapRequestHeader=host, host1
修改请求头,上例中将host请求头替换成host1请求头重新生成内容
- 过滤器十:RemoveRequestHeader
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- RemoveRequestHeader=x-forwarded-for
删除请求头,在转发到下游项目前删除设置的请求头。
- 过滤器十一:RemoveResponseHeader
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- RemoveResponseHeader=Content-Length
删除响应头,下游项目响应回来后删除设置的响应头。
- 过滤器十二:RemoveRequestParameter
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- RemoveRequestParameter=name
删除请求参数。
- 过滤器十三:SetRequestHeader
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- SetRequestHeader=X-Request-Red, Blue
替换所有请求头,逗号隔开,前一个是请求头key,第二个是请求头替换后的的值。
- 过滤器十四:SetResponseHeader
spring:
cloud:
gateway:
routes:
- id: user_route
uri: http://localhost:8092
predicates:
- Path=/user-api/**
filters:
- SetResponseHeader=X-Response-Red, Blue
替换所有响应头,逗号隔开,前一个是响应头key,第二个是响应头替换后的的值。
好了,gateway网关的大部分配置内容就介绍完了,spring cloud gateway的官方文档总体上比其他子项目的要容易理解一些,有兴趣的可以自己查阅。
希望本文对你有所帮助。
相关推荐
- 从IDEA开始,迈进GO语言之门(idea got)
-
前言笔者在学习GO语言编程的时候,GO语言在国内还没有像JAVA/Php/Python那样普及,绕了不少的弯路,要开始入门学习一门编程语言,最好就先从选择一个好的编程语言的开发环境开始,有了这个开发环...
- 基于SpringBoot+MyBatis的私人影院java网上购票jsp源代码Mysql
-
本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于SpringBoot...
- 基于springboot的个人服装管理系统java网上商城jsp源代码mysql
-
本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...
- 基于springboot的美食网站Java食品销售jsp源代码Mysql
-
本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...
- 贸易管理进销存springboot云管货管账分析java jsp源代码mysql
-
本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目描述贸易管理进销存spring...
- SpringBoot+VUE员工信息管理系统Java人员管理jsp源代码Mysql
-
本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍SpringBoot+V...
- 目前见过最牛的一个SpringBoot商城项目(附源码)还有人没用过吗
-
帮粉丝找了一个基于SpringBoot的天猫商城项目,快速部署运行,所用技术:MySQL,Druid,Log4j2,Maven,Echarts,Bootstrap...免费给大家分享出来前台演示...
- SpringBoot+Mysql实现的手机商城附带源码演示导入视频
-
今天为大家带来的是基于SpringBoot+JPA+Thymeleaf框架的手机商城管理系统,商城系统分为前台和后台、前台用的是Bootstrap框架后台用的是SpringBoot+JPA都是现在主...
- 全网首发!马士兵内部共享—1658页《Java面试突击核心讲》
-
又是一年一度的“金九银十”秋招大热门,为助力广大程序员朋友“面试造火箭”,小编今天给大家分享的便是这份马士兵内部的面试神技——1658页《Java面试突击核心讲》!...
- SpringBoot数据库操作的应用(springboot与数据库交互)
-
1.JDBC+HikariDataSource...
- SpringBoot 整合 Flink 实时同步 MySQL
-
1、需求在Flink发布SpringBoot打包的jar包能够实时同步MySQL表,做到原表进行新增、修改、删除的时候目标表都能对应同步。...
- SpringBoot + Mybatis + Shiro + mysql + redis智能平台源码分享
-
后端技术栈基于SpringBoot+Mybatis+Shiro+mysql+redis构建的智慧云智能教育平台基于数据驱动视图的理念封装element-ui,即使没有vue的使...
- Springboot+Mysql舞蹈课程在线预约系统源码附带视频运行教程
-
今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的Springboot+Mysql舞蹈课程在线预约系统,系统项目源代码在【猿来入此】获取!https://www.yuan...
- SpringBoot+Mysql在线众筹系统源码+讲解视频+开发文档(参考论文
-
今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的在线众筹管理系统,主要实现了普通用户在线参与众筹基本操作流程的全部功能,系统分普通用户、超级管理员等角色,除基础脚手架外...
- Docker一键部署 SpringBoot 应用的方法,贼快贼好用
-
这两天发现个Gradle插件,支持一键打包、推送Docker镜像。今天我们来讲讲这个插件,希望对大家有所帮助!GradleDockerPlugin简介...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 从IDEA开始,迈进GO语言之门(idea got)
- 基于SpringBoot+MyBatis的私人影院java网上购票jsp源代码Mysql
- 基于springboot的个人服装管理系统java网上商城jsp源代码mysql
- 基于springboot的美食网站Java食品销售jsp源代码Mysql
- 贸易管理进销存springboot云管货管账分析java jsp源代码mysql
- SpringBoot+VUE员工信息管理系统Java人员管理jsp源代码Mysql
- 目前见过最牛的一个SpringBoot商城项目(附源码)还有人没用过吗
- SpringBoot+Mysql实现的手机商城附带源码演示导入视频
- 全网首发!马士兵内部共享—1658页《Java面试突击核心讲》
- SpringBoot数据库操作的应用(springboot与数据库交互)
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)