IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringCloud Alibaba之Sentinelt组件 -> 正文阅读

[Java知识库]SpringCloud Alibaba之Sentinelt组件




该篇博文根据B站尚硅谷课程——尚硅谷SpringCloud整理而出



一、Sentinel熔断与限流

A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)

类比Spring Cloud Nexfix的Hystrix组件,用于服务的熔断降级。

官网



随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。



Sentinel 的主要特性:

请添加图片描述



Sentinel 的开源生态:

请添加图片描述



Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。






二、控制台安装

1、Sentinel控制台安装

下载你想要的版本

请添加图片描述

由于是一个jar包,我们直接可以直接使用java -jar命令来运行这个jar包。当然前提是你的8080端口没有被占用,Sentinel默认端口也是8080。



访问测试:

账号和密码均是sentinel

请添加图片描述






三、规则讲解

规则讲解可以简单理解为Sentinel控制台的使用讲解。下面的小节也将按照控制台面板的顺序依次往下进行讲解

请添加图片描述




1、实时监控

1、 新建一个端口为8401的项目

2、添加maven依赖

<?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>com.springcloud</artifactId>
        <groupId>com.sc2020</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-sentinel-service8401</artifactId>
    <dependencies>
        <!--   nacos config     -->
        <!--        <dependency>-->
        <!--            <groupId>com.alibaba.cloud</groupId>-->
        <!--            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>-->
        <!--        </dependency>-->
        <!--  SpringCloud alibaba nacos    -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--    后续做持久化用到    -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!--   sentinel     -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--   openfeign     -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--  web组件      -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
    </dependencies>

</project>

3、修改配置文件

1、配置端口号

2、配置项目名字

3、配置将注册到那个位置的注册中心

4、配置被哪个端口的进行限流监控(8080将会监控8401)

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        # 配置sentinel dashboard地址
        dashboard: localhost:8080
        #默认端口,如果被占用则从8719依次+1扫描
        port: 8719
management:
  endpoints:
    web:
      exposure:
        include: "*"

4、添加controller层接口类

public class FlowLimitController {

    @GetMapping("/testA")
    public String testA(){
        return "---------testA";
    }

    @GetMapping("/testB")
    public String testB(){
        return "----------testB";
    }
}

5、访问测试

1、启动sentinel服务

2、启动被监控程序

3、访问对应接口

4、观察Sentinel控制台变化

分别访问接口:http://localhost:8401/testA和http://localhost:8401/testB

请添加图片描述


我们可以发现Sentinel控制台已经能够成功监控我们的访问结果,此时即表示测试成功




2、流控规则

流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

请添加图片描述



流量控制规则面板

请添加图片描述

1、资源名:唯一名称,默认请求路径

2、针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)

3、阈值类型/单机阈值:

  • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流。
  • 线程数:当调用该api的线程数达到阈值的时候,进行限流

4、是否集群:不需要集群

5、流控模式:

  • 直接: api达到限流条件时,直接限流
  • 关联:当关联的资源达到阈值时,就限流自己
  • 链路:只记录指定链路上的流量((指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】

6、流控效果:

  • 快速失败:直接失败,抛异常
  • Warm Up:根据codeFactor (冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
  • 排队等待:方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法



2.1 流控模式——直接

QPS的阈值限之就是限制每秒的访问次数,当每秒钟访问的次数超过设定的阈值就会返回对应的被限流提示。线程数表示,单次处理的线程数量。

即前者理解为让你排队进入房间,后者理解为排队进行服务,任何的拥挤 现象都会返回流量被限制提示。


QPS直接

1、新增流量控制

请添加图片描述


2、设置对应的控制规则

这里设置为QPS,每秒1次,超过数量则快速失败

资源名:唯一名称,默认请求路径

请添加图片描述


3、访问接口测试

当我们访问的频率超过设定的阈值(1)就会触发设定的流控规则

请添加图片描述



线程数直接

1、修改我们的接口方法

让一个线程的处理时间边长,这样模拟处理服务的时间。如果在一个线程处理服务的时候其他线程进来了,那么就可以理解为拥挤,这样就会返回对应的错误信息。

@GetMapping("/testA")
public String testA(){
    try {
        TimeUnit.MILLISECONDS.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "---------testA";
}

2、修改对应的控制规则

请添加图片描述


3、访问接口测试

一个线程还没有处理完,另一个线程进来了,就会触设定的流控规则

请添加图片描述


2.2 流控模式——关联

流控模式关联

当关联的资源达到阈值时,就限流自己

A关联B,当B达到阈值时,限流A

1、新增对应的控制规则

阈值设置为1

请添加图片描述


2、使用Postman模拟人的快速访问

请添加图片描述


3、保存对应的测试路径,并设置对应的访问次数及时间间隔(访问20次,0.3秒一次)

请添加图片描述


4、测试访问接口/testA

由于大量的访问/testB接口,设定的规则QPS为1,超过了阈值,所以访问test A接口被限制

请添加图片描述



2.3 流控效果——Warm Up

默认coldFactor为3,即请求QPS从(threshold / 3)开始,经多少预热时长才逐渐升至设定的QPS阈值。

1、设置流控规则

请添加图片描述

以上图为例,设置阈值为10,预热时长为5秒。

系统初始化的阈值为10/3约等于3,即阈值刚开始为3;然后过了5秒后阈值才慢慢升高恢复到10


2、访问对应的接口

在预热刚开始,访问的速度超过阈值(这里约等于3),则会返回限流提示。5秒钟的预热完成之后,只要访问的速度超过阈值(10)才会返回对应的限流提示。

如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。



2.4 流控效果——排队等待

1、设置对应的限流规则

请添加图片描述


2、修改接口方法,打印访问时间

@GetMapping("/testB")
public String testB(){
    SimpleDateFormat sdf = new SimpleDateFormat();
    sdf.applyPattern("yyyy-MM-dd HH:mm:ss a");
    Date date = new Date();//
    System.out.println(sdf.format(date)+Thread.currentThread().getName() + "testB");
    return "----------testB";
}

3、使用postman模拟快速访问测试(0.3秒一个,一共10个)

请添加图片描述


4、查看测试结果

尽管我们在postman中设置的规则是0.3秒访问一次,但是由于我们定义了流控效果,并且对应的效果是排队等待,故所有的访问都会执行,并且根据QPS的设定速度访问,一秒一个

请添加图片描述




3、降级规则

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

请添加图片描述


Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。



3.1 RT

1、新增方法

@GetMapping("/testD")
public String testD(){
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("testD 测试RT");
    int age = 10/0;
    return "------testD";
}

2、配置规则

请添加图片描述

永远一秒钟打进来10个线程(大于5个)调用testD(可以使用JMeter工具模拟),我们希望200毫秒处理完本次任务,如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,返回指定的降级信息



3.2 异常比例

1、修改方法

@GetMapping("/testD")
public String testD(){
    log.info("testD 异常比例");
    int age = 10/0;
    return "------testD";
}

2、配置规则

访问的80%要正确,否则就会有一秒钟的不可用

请添加图片描述


按照上述配置以后,单独访问一次,必然会报错(代码存在10/0),访问接口,返回异常错误信息。在高并发的场景下(使用JMeter模拟),直接高并发发送请求,多次调用达到了我们的配置条件(请求资源数每秒到达5个及以上),断路器开启(保险丝跳闸),微服务不可用,此时则不再返回异常错误信息,而是返回对应的降级提示信息。



3.3 异常数

1、新增方法

@GetMapping("/testE")
public String testE(){
    log.info("testE 测试异常数");
    int age = 10/0;
    return "------testE 测试异常数";
}

2、配置规则

请添加图片描述


3、访问测试

访问接口,根据设置的规则,访问5次以后返回指定的降级提示信息

前几次会返回异常信息

请添加图片描述



4、热点规则

1、新增方法

配置限流规则和我们SentinelResource注解上的value一致

blockHandler = "deal_testHotKey"表示当触发热点的限流规则时,会跳转的方法,如果不配置该项则会返回到系统默认的限流界面

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2", required = false) String p2) {
    return "-------testHotKey";
}

public String deal_testHotKey(String p1, String p2,
                         BlockException exception){
    return "----deal_testHotKey,o(╥﹏╥)o";

}

2、配置限流规则

资源名和上面@SentinelResource注解的value值对应,只能使用QPS模式,该配置表示访问接口的第一个参数,如果在1秒钟内单机访问的次数超过一次,那么就会触发限流规则

请添加图片描述


3、访问测试

1秒1次的访问,会返回正确的指定的结果

超过这个阈值,就会触发我们的自定义的限流规则

只要路径中带有第一个参数就算符合热点Key的限流规则,如http://localhost:8401/testHotKey?p1=3&p2=3

请添加图片描述


高级配置

类似于VIP配置项

当我们的参数值为5时,允许QPS阈值为10,其他情况下为阈值1

此时访问对应的路径时,第一个参数值为5时,并发量可以提高

参数类型只支持8大基本数据类型+String字符串类型

请添加图片描述




5、系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。



系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt估算得出。设定参考值一般是 CPU cores * 2.5。
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

该配置表示,项目外部的整体QPS达到阈值1以后,就会返回指定规则的信息

请添加图片描述




6、@SentinelResource

从HystrixCommand到@SentinelResource

官方文档地址

常规情况下流控规则的资源名为我们接口的映射路径名

我们还可以使用@SentinelResource注解的value值来配置(前面热点Key的限流章节已经演示)

请添加图片描述



只要我们使用了@SentinelResource注解,就可以根据配置的限流规则进行跳转(自定义方法或者是使用默认的跳转路径)

请添加图片描述

针对我们配置的限流方法blockHandler的思考

1、系统默认的,没有体现我们自己的业务要求。

2、依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。

3、每个业务方法都添加一个兜底的,那代码膨胀加剧。

4、全局统—的处理方法没有体现。



限流配置

前文演示过在同一个类中定义限流方法,此处我们使用一个单独的类配置限流方法

1、新建异常返回类

// 限流方法类
public class CustomerBlockHandler {

    public static String handlerException(BlockException exception) {
    	return "客户自定义方法==1==,global handlerException";
    }
    public static String handlerException2(BlockException exception) {
		return "客户自定义方法==2==,global handlerException";
    }
}
@GetMapping("/rateLimit/CustomerBlockHandler")
// 指定value,以及限流的类+具体方法
@SentinelResource(value = "CustomerBlockHandler", blockHandlerClass = CustomerBlockHandler.class,
        blockHandler = "handlerException2")
public String CustomerBlockHandler() {
    return "限流方法test";
}

2、配置对应的限流规则

请添加图片描述


3、接口访问测试

当访问接口的次数超过阈值,就会根据@SentinelResource注解配置的方法进行返回,使用一个类单独配置可以完成解耦

请添加图片描述



服务熔断

还是借助@SentinelResource注解

1、新建访问接口

@GetMapping("/fallBackHandler/{id}")
// 使用fallback指定服务熔断后跳转的方法
@SentinelResource(value = "fallBackTest", fallback = "fallBackHandler")
public String fallBackTest(@PathVariable("id") Integer id) {
    if (id == 0) {
        return "限流方法test";
    }
    if (id == 1) {
        throw new RuntimeException("id等于1,运行时异常");
    }
    if (id == 2) {
        throw new NullPointerException("id等于2,空指针异常");
    }
    return null;
}

2、添加服务熔断方法

// 服务熔断方法自定义异常打印信息
public static String fallBackHandler(@PathVariable Integer id, Throwable e) {
    return "自定义服务熔断异常:"+"id" +e.getMessage();
}

3、接口访问测试

当我们的id等于2时,就会抛出我们的空指针异常,由于我们配置了熔断功能,所以我们会跳转到我们自定义的异常返回方法中

请添加图片描述



服务降级和服务熔断同时配置

当我们同时指定了降级和熔断方法,降级方法的提示是要优于熔断方法出现。

@SentinelResource(value = “fallBackTest”, blockHandler = “deal_testHotKey”, fallback = “fallBackHandler”)

其实这并不难理解,当访问的QPS达到了阈值,且同时方法发生异常,触发服务熔断,前者可以抽象的理解为在整体项目的最外层安装了一个流量监控器,当访问的次数达到了监控的阈值,接下来的访问直接就被隔断在了项目之外,既然如此那又何来的抛出异常,触发服务熔断一说?


熔断框架对比

SentinelHystrixresilience4j
隔离策略信号量隔离(并发线程数限流)线程池隔离/信号量隔离信号量隔离
熔断降级策略基于响应时间、异常比例、异常数基于异常比例基于异常比率、响应时间
实时统计实现滑动窗口滑动窗口(基于RxJava)Ring Bit Buffer
动态规则配置支持多种数据源支持多种数据源有限支持
扩展性多个扩展点插件的形式接口的形式
基于注解的支持支持支持支持
限流基于QPS,支持基于调用关系的限流有限的支持Rate Limiter






四、规则持久化

1、引入依赖

<!-- 前面的pom.xml中已经引用过 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

2、编写配置文件

请添加图片描述

datasource:
  dsl:
    nacos:
      server-addr: localhost:8848
      dataId: cloudalibaba-sentinel-service
      groupId: DEFAULT_GROUP
      data_type: json
      rule-type: flow

3、nacos客户端新建服务

请添加图片描述

[
    {
        "resource":"/rateLimit/byUrl",
        "limitApp":"default",
        "grade":1,
        "count":1,
        "strategy":0,
        "controlBehavior":0,
        "clusterMode":false
    }
]

配置内容可以理解为我们前面控制台中的流控规则配置,我们直接通过json字符串配置一个流控规则

Data Id为我们在配置文件中的新增的dataId选项

  • resource:资源名称
  • limitApp:应用来源
  • grade:阈值类型,0表示线程数,1表示QPS
  • count:单机阈值
  • strategy:流控模式,0表示直接,1表示关联,2表示链路
  • controlBehavior:流控效果,0表示快速失败,1表示Wram Up,2表示排队等待
  • clusterMode:是否集群

4、新建流控规则

请添加图片描述


5、访问对应的接口,观察流控是否生效

请添加图片描述


6、重启项目,观察流控规则是否持久化

当然,重启项目之前添加的流控规则依然存在,即规则持久化配置成功

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 17:20:35  更:2021-08-03 17:20:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 4:29:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码