首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

SpringCloud Gateway 集成 Sentinel 详解 及实现动态监听Nacos规则配置实时更新流控规则

  • 25-04-24 10:03
  • 3398
  • 10285
blog.csdn.net
目录
    • 一、前言
    • 二、版本选择和适配
      • 2.1、本文使用各组件版本
      • 2.2、官方推荐版本
    • 三、部署sentinel-dashboard
      • 3.1、下载 sentinel-dashboard jar包
      • 3.2、启动 sentinel-dashboard
    • 四、Gateway 集成 Sentinel实现控制台配置流控规则测试
      • 4.1、添加Gateway 集成 Sentinel 包
      • 4.2、添加 Gateway 服务启动JVM参数
        • 4.2.1、配置说明
        • 4.2.2、启动说明
          • 4.2.2.1、使用 jar 包启动Gateway添加JVM启动参数
          • 4.2.2.2、IDEA中配置JVM启动参数(IDEA版本2022.2.1)
      • 4.3、启动 Gateway 注册到 Sentinel-dashboard 实现接口流控规则动态配置
        • 4.3.1、启动 Gateway 注册到 Sentinel-dashboard
        • 4.3.2、通过 Sentinel-dashboard 配置指定接口限流
      • 4.4、注意事项
    • 五、Gateway 集成 Sentinel 常用配置
      • 5.1、热加载
      • 5.2、降级处理配置(这里提供代码配置和使用配置文件配置)
        • 5.2.1、通过代码配置(配置文件配置比代码配置优先级高)
        • 5.2.2、通过配置文件配置
    • 六、自定义本地加载流控规则
      • 6.1、通过代码加载流控规则
      • 6.2、通过本地配置文件加载流控规则
        • 6.2.1、API分组规则json文件编写(gateway-sentinel-api-groups.json)
        • 6.2.2、流控规则json文件编写(gateway-sentinel-flow-rules.json)
        • 6.2.3、配置文件配置加载API分组和流控规则json文件
    • 七、动态监听Nacos规则配置实时更新流控规则实现(推荐)
      • 7.1、添加sentinel集成nacos包
      • 7.2、Nacos配置中心添加API分组规则json(gateway-sentinel-api-groups)
      • 7.3、Nacos配置中心添加流控规则json(gateway-sentinel-flow-rules)
      • 7.4、配置文件配置加载Nacos配置中心API分组和流控规则json
      • 7.5、测试效果

一、前言

Sentinel 是 SpringCloud Alibaba 家族的服务保护组件,很多项目在前中期没有遇到流量突增不太注意服务保护的重要性,当流量突增打爆应用服务或数据库时束手无策,可以不配置流控规则,但是需要时一定可以热加载使用,本文会对集成Sentinel以及动态拉取Nacos配置规则实现热加载流控规则进行讲解。

官网地址

二、版本选择和适配

使用 SpringCloud Alibaba 家族组件,要注意一下版本兼容问题,避免出现一些奇怪的问题,这里会说明本文使用的各组件版本,以及 SpringCloud Alibaba 推荐的各版本适配。

2.1、本文使用各组件版本

部分组件对版本兼容要求其实没有那么高,比如Nacos,不一定要按照官方推荐版本,差几个小版本没有什么影响,我本地使用的一直是Nacos2.0.2,代码集成实现基本都一样。

JDK:1.8.0
Spring-Boot:2.3.12.RELEASE
Spring-Cloud:Hoxton.SR12
Spring-Cloud-Alibaba:2.2.9.RELEASE
Nacos:2.0.2
Sentinel:1.8.5

2.2、官方推荐版本

官方版本说明
在这里插入图片描述

三、部署sentinel-dashboard

3.1、下载 sentinel-dashboard jar包

这里使用sentinel-dashboard-1.8.5,这里提供两个下载地址,需要其它版本可以自行去github下载。
github下载地址
百度网盘地址
在这里插入图片描述

3.2、启动 sentinel-dashboard
java -Dserver.port=8180 -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.5.jar
  • 1
  • -Dserver.port=8180
    sentine 服务控制台端口
  • -Dsentinel.dashboard.auth.username=sentinel
    sentine 控制台登录账号,不设置默认sentinel
  • -Dsentinel.dashboard.auth.password=123456
    sentine 控制台登录密码,不设置默认sentinel
  • -Dcsp.sentinel.dashboard.server=localhost:8180
    将控制台自身注册到server
  • -Dproject.name=sentinel-dashboard
    控制台服务自己项目名称

在这里插入图片描述
在这里插入图片描述

四、Gateway 集成 Sentinel实现控制台配置流控规则测试

4.1、添加Gateway 集成 Sentinel 包

在原有网关项目基础上添加上这两个包,这两个包会将gateway集成sentinel,并且默认是自动配置的,无需手动配置。

        
            com.alibaba.cloud
            spring-cloud-alibaba-sentinel-gateway
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-sentinel
        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
4.2、添加 Gateway 服务启动JVM参数
4.2.1、配置说明

Gateway 连接 Sentinel 控制台的配置,Sentinel1.7.0 版本以下不支持配置文件配置,推荐直接使用JVM参数配置。

  • 添加JVM启动参数:
    -Dcsp.sentinel.dashboard.server=127.0.0.1:8180
    -Dcsp.sentinel.api.port=18719
    -Dproject.name=kerwin-gateway
    -Dcsp.sentinel.app.type=1

  • 参数说明:
    -Dcsp.sentinel.dashboard.server:指定控制台地址和端口。
    -Dproject.name:在sentinel控制台中展示的项目名称。
    -Dcsp.sentinel.api.port:指定客户端监控 API 的端口(默认是 8719),如控制台修改规则,则会向该端口推送规则信息。
    -Dcsp.sentinel.app.type:从 1.6.3 版本开始,控制台支持网关流控规则管理。该启动参数设置成1会将您的服务标记为 API Gateway,在接入控制台时您的服务会自动注册为网关类型,然后您即可在控制台配置网关规则和 API 分组。

4.2.2、启动说明

这里提供服务打成jar包启动和使用IDEA开发工具添加JVM参数启动示例。

4.2.2.1、使用 jar 包启动Gateway添加JVM启动参数
java -Dcsp.sentinel.dashboard.server=127.0.0.1:8180 -Dcsp.sentinel.api.port=18719 -Dproject.name=kerwin-gateway -Dcsp.sentinel.app.type=1 -jar kerwin-gateway.jar
  • 1
4.2.2.2、IDEA中配置JVM启动参数(IDEA版本2022.2.1)

不同版本IDEA可以配置入口不同,有需要可以自己查询
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3、启动 Gateway 注册到 Sentinel-dashboard 实现接口流控规则动态配置

这里需要注意,Gateway 集成 Sentinel-dashboard 默认是懒加载的,需要调用一次接口才能注册到 Sentinel-dashboard,也可以直接在 Gateway 中配置成热加载,添加spring.cloud.sentinel.eager:true实现服务器启动了自动心跳注册。

4.3.1、启动 Gateway 注册到 Sentinel-dashboard

在这里插入图片描述

4.3.2、通过 Sentinel-dashboard 配置指定接口限流

这里需要注意,这个流控规则是按照组级别来的,一个组内所有匹配规则会共用一个阈值,如果需要在网关应用配置单独接口流控规则目前来看只能配置多个分组,然后单独配置规则。

  • 1、新增 API 分组
    在这里插入图片描述

  • 2、自定义分组内 API 匹配规则
    在这里插入图片描述

  • 3、新增网关流控规则
    在这里插入图片描述

  • 4、配置网关流控规则(这里需要注意,这个流控规则是按照组级别来的,一个组内所有匹配规则共用一个阈值)
    在这里插入图片描述

  • 5、测试限流规则
    快速请求两次可以看到服务端响应 Blocked by Sentinel: ParamFlowException,响应内容也是可以自定义的,这个会在后面说明。
    在这里插入图片描述

PS:1秒内同时请求一次 /user/info 接口 和 /user/list 接口,也会响应Blocked by Sentinel: ParamFlowException,因为同一个分组共用一个阈值,如果要单独配置某一个接口目前看只能整多个分组。

4.4、注意事项

需要注意:如果不做特殊处理,通过Sentinel控制台配置的规则在应用服务重启后就没了,通过Sentinel控制台配置流控规则的本质其实就是将编辑好的规则加载到应用服务缓存中,并不会进行持久化,如果想要持久化Sentinel控制台配置的规则需要特殊处理,后续会进行说明。

五、Gateway 集成 Sentinel 常用配置

5.1、热加载
spring:
  cloud:
    sentinel:
      # 设置sentinel为热加载 默认为false
      eager: true
  • 1
  • 2
  • 3
  • 4
  • 5
5.2、降级处理配置(这里提供代码配置和使用配置文件配置)
5.2.1、通过代码配置(配置文件配置比代码配置优先级高)
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;

@Configuration
public class SentinelGatewayConfiguration {

    /**
     * 自定义降级处理响应
     */
    @PostConstruct
    public void init(){
        GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
            @Override
            public Mono handleRequest(ServerWebExchange exchange, Throwable throwable) {
                return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).bodyValue("{"code":500,"msg":"代码配置-被限流了!"}");
            }
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

在这里插入图片描述

5.2.2、通过配置文件配置
spring:
  cloud:
    sentinel:
      # 设置sentinel为热加载 默认为false
      eager: true
      scg:
        # 降级处理配置 也可以在代码中实现
        fallback:
          # 指定降级处理的模式为返回响应,也可以配置成重定向redirect,配置重定向需要指定重定向地址
          mode: 'response'
          response-status: 200
          response-body: '{"code":500,"msg":"配置文件配置-被限流了!"}'
          # mode 为 redirect 时使用
          redirect: 'https://blog.csdn.net/weixin_44606481'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

六、自定义本地加载流控规则

因为 sentinel-dashboard 不会持久化手动配置的流控规则,一般情况下我们都会提前配置一些我们需要的规则,可以通过代码或者配置文件配置。

6.1、通过代码加载流控规则

Sentinel 的API管理存储在 GatewayApiDefinitionManager 类中,流控规则存储在 GatewayRuleManager 类中,添加好自己需要的配置即可。

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Set;

@Configuration
public class SentinelGatewayRuleConfiguration {

    @PostConstruct
    public void initRule(){
        // 加载根据路由 ID 配置限流规则
        this.initGatewayFlowRules();
        // 加载根据API分组配置限流规则
        this.initApiDefinitions();
    }

    private void initGatewayFlowRules() {
        // 存储限流规则的集合
        Set rules = GatewayRuleManager.getRules();
        if(rules == null){
            rules = new HashSet<>();
        }
        // 根据路由 ID 配置限流规则
        GatewayFlowRule rule1 = new GatewayFlowRule("kerwin-user")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
                .setCount(1) // QPS阈值
                .setIntervalSec(1); // 间隔
        rules.add(rule1);
        // 加载限流规则
        GatewayRuleManager.loadRules(rules);
    }

    private void initApiDefinitions() {
        Set apiDefinitions = GatewayApiDefinitionManager.getApiDefinitions();
        if(apiDefinitions == null){
            apiDefinitions = new HashSet<>();
        }
        // 创建一个 API 分组
        ApiDefinition apiDefinition1 = new ApiDefinition("user服务API组");
        // API 分组 URL 匹配规则
        Set apiPathPredicateItems = new HashSet<>();
        // 添加精确匹配 匹配为 /api-user/user/info 的url
        apiPathPredicateItems.add(new ApiPathPredicateItem().setPattern("/api-user/user/info")
                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));
        // 添加前缀匹配 用于匹配以 /api-user/user 开头的URL
        apiPathPredicateItems.add(new ApiPathPredicateItem().setPattern("/api-user/user/**")
                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
//        // 添加正则匹配 用于匹配以 list 结尾的 URL
//        apiPathPredicateItems.add(new ApiPathPredicateItem().setPattern("^.*list$")
//                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_REGEX));

        apiDefinition1.setPredicateItems(apiPathPredicateItems);
        apiDefinitions.add(apiDefinition1);

        // 根据 API 分组配置限流规则
        Set rules = GatewayRuleManager.getRules();
        if(rules == null){
            rules = new HashSet<>();
        }
        GatewayFlowRule rule1 = new GatewayFlowRule(apiDefinition1.getApiName())
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(1) // QPS阈值
                .setIntervalSec(1); // 间隔
        rules.add(rule1);

        // 加载 API 分组定义
        GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions);
        // 加载限流规则
        GatewayRuleManager.loadRules(rules);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

项目启动后可以在Sentinel控制台看到代码配置的API分组和流控规则。

6.2、通过本地配置文件加载流控规则

通过代码手动配置会比较麻烦而且不易调整,Gateway集成Sentinel包提供了通过配置文件加载API分组和流控规则实现,这里会将API分组和流控规则都分别写入不同的json文件中,交由对于实现类去进行加载。

6.2.1、API分组规则json文件编写(gateway-sentinel-api-groups.json)

在resource目录创建gateway-sentinel-api-groups.json将API分组规则内容填进去。

[
  {
    "apiName": "user服务API组",
    "predicateItems": [
      {
        "pattern": "/api-user/user/info",
        "matchStrategy": 0
      },
      {
        "pattern": "/api-user/user/**",
        "matchStrategy": 1
      }
    ]
  }
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

解释:

  • apiName:
    字符串,代表 API 组名称,这里是 “user服务API组”,用于统一管理相关 API。
  • predicateItems:
    数组,包含多个 predicateItem 对象,用于描述 API 的匹配模式。
  • predicateItem 内部元素:
    • pattern:
      字符串,使用不同风格的表达式。
      例如,“/api-user/user/info” 精确匹配该路径;“/api-user/user/**” 匹配 /api-user/user/ 下的所有路径(包括子路径)。
    • matchStrategy:
      整数,匹配策略:
      0:精确匹配,路径需与 pattern 完全一致。
      1:前缀匹配,路径以 pattern 开头即可。
      2:正则匹配,使用 pattern 作为正则表达式进行匹配。
6.2.2、流控规则json文件编写(gateway-sentinel-flow-rules.json)

在resource目录创建gateway-sentinel-flow-rules.json将流控规则内容填进去。

[
  {
    "resource": "user服务API组",
    "resourceMode": 1,
    "count": 1,
    "intervalSec": 1,
    "burst": 0,
    "paramItem": null,
    "controlBehavior": 0,
    "maxQueueingTimeoutMs": 0
  },
  {
    "resource": "kerwin-user",
    "resourceMode": 0,
    "count": 1,
    "intervalSec": 1,
    "burst": 0,
    "paramItem": null,
    "controlBehavior": 0,
    "maxQueueingTimeoutMs": 0
  }
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

字段解释:

  • resource:资源名称,用于标识要进行流控的目标。可以是路由 ID、自定义 API 分组名称等。在网关场景中,资源可以代表一个具体的路由或者一组路由的集合。
  • resourceMode:
    • 0:代表资源模式,对应 SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID,表示基于 Route ID 进行资源匹配。
    • 1:API分组模式,对应 SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME,标识基于API分组进行资源匹配。
  • count:流控阈值,根据 grade 字段的不同,其代表的意义也不同。如果 grade 为 QPS 限流,count 表示每秒允许通过的请求数量;如果 grade 为并发线程数限流,count 表示允许的最大并发线程数。
  • grade:流控阈值类型,取值有两种。1 表示 QPS(每秒查询率)限流,0 表示并发线程数限流。
  • intervalSec:统计时间窗口,单位为秒。表示在多长时间内统计请求数量以判断是否触发流控。
  • controlBehavior:流控效果,即当请求超过阈值时的处理方式
    • 0:快速失败,请求超过阈值时直接拒绝并抛出异常
    • 2:匀速排队,请求会进入队列,按照固定速率处理,避免流量突发
  • burst:仅在 controlBehavior 为 0(快速失败)时有效,表示突发流量容忍值。在短时间内允许超过阈值的额外请求数量,用于应对突发流量场景。
  • maxQueueingTimeoutMs:仅在 controlBehavior 为 2(匀速排队)时有效,表示请求在队列中的最大排队时间,单位为毫秒。超过该时间的请求将被拒绝。
6.2.3、配置文件配置加载API分组和流控规则json文件

在配置文件中添加API分组和流控规则读取数据源配置,这里省略了其它Sentinel配置需要可以看之前配置内容添加。

spring:
  cloud:
    sentinel:
      # 设置sentinel为热加载 默认为false
      eager: true
      # API分组&流控规则配置文件配置
      datasource:
        ds1:
          file:
            file: classpath:gateway-sentinel-api-groups.json
            ruleType: gw-api-group # 网关API分组
            dataType: json
        ds2:
          file:
            file: classpath:gateway-sentinel-flow-rules.json
            ruleType: gw-flow # 网关流控规则
            dataType: json
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这里就不贴图了,配置完成后启动看看Sentinel控制台是否有初始化这些配置,要记得开启热加载。

七、动态监听Nacos规则配置实时更新流控规则实现(推荐)

通过Nacos配置中心动态拉取加载流控规则和本地配置文件配置是类似的,编写的规则json是一样的,只是需要指定一下加载的数据源为Nacos读取,并且已经实现了动态加载,在Nacos配置中心修改规则会进行实时同步。

7.1、添加sentinel集成nacos包

sentinel读取nacos配置需要添加这个适配包。

        
            com.alibaba.csp
            sentinel-datasource-nacos
        
  • 1
  • 2
  • 3
  • 4
7.2、Nacos配置中心添加API分组规则json(gateway-sentinel-api-groups)

配置的内容和本文6.2.1中一致。
在这里插入图片描述

7.3、Nacos配置中心添加流控规则json(gateway-sentinel-flow-rules)

配置的内容和本文6.2.2中一致。
在这里插入图片描述

7.4、配置文件配置加载Nacos配置中心API分组和流控规则json
spring:
  cloud:
    sentinel:
      # 设置sentinel为热加载 默认为false
      eager: true
      # API分组&流控规则配置文件配置
      datasource:
        ds1:
          nacos:
            server-addr: 172.16.8.169:8848
            data-id: gateway-sentinel-api-groups
            namespace: springcloud-component-example
            group-id: DEFAULT_GROUP
            username: nacos
            password: nacos
            data-type: json
            rule-type: gw-api-group
        ds2:
          nacos:
            server-addr: 172.16.8.169:8848
            data-id: gateway-sentinel-flow-rules
            namespace: springcloud-component-example
            group-id: DEFAULT_GROUP
            username: nacos
            password: nacos
            data-type: json
            rule-type: gw-flow
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
7.5、测试效果
  • 1、启动网关服务,查看注册API分组和流控规则
    在这里插入图片描述
  • 2、Nacos配置中心修改流控规则
    在这里插入图片描述
  • 3、查看sentinel控制台同步情况
    在这里插入图片描述
注:本文转载自blog.csdn.net的m0_74823892的文章"https://blog.csdn.net/m0_74823892/article/details/146432027"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

115
云原生
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top