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 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 在.NE6 WebApi中使用分布式缓存Redis -> 正文阅读

[大数据]在.NE6 WebApi中使用分布式缓存Redis

首先安装Redis
https://github.com/MicrosoftArchive/redis/releases

https://github.com/cinience/RedisStudio/releases

Redis 启动和关闭
安装好之后,我们可以通过服务启动和关闭 Redis 了,我们可以查看系统的任务管理器,检查是否启动了 redis-server,下面是 redis-server 的启动和关闭命令:

$ redis-server.exe  --service-start # 启动
$ redis-server.exe  --service-stop # 关闭

使用 cli
确认 redis-server 启动之后,我们可以执行 redis 目录里面的 redis-cli.exe 文件,这样就可以连接上 Redis 了。当然,我们也可以通过命令行 CMD 来链接 Redis。

可以直接进行 Redis 存取了:

$ redis-cli.exe -h 127.0.0.1 -p 6379
127.0.0.1:6379> set firstKey "moyufed"
OK
127.0.0.1:6379> get firstKey
"moyufed"
127.0.0.1:6379>

增加密码
在 Redis 的安装目录中,有 redis.windows.conf ,redis.windows-service.conf 两个文件,我们使用记事本打开(推荐Notepad++),找到需要设置密码的行,可以 Ctrl + F 查找 “requirepass” 的行(大概386行位置),分别设置 Redis 密码(去掉前面的 “#”),例子:

requirepass moyufed

使用 Redis
修改好之后重启 redis-server ,此时连接 Redis 之后进行 Redis 存取需要登录,例子:

Administrator@WIN-1706081829 C:\Users\Administrator
$ redis-server.exe  --service-stop
[42636] 25 Feb 18:42:53.622 # Redis service successfully stopped.

Administrator@WIN-1706081829 C:\Users\Administrator
$ redis-server.exe  --service-start
[38828] 25 Feb 18:42:58.576 # Redis service successfully started.

Administrator@WIN-1706081829 C:\Users\Administrator
$ redis-cli.exe -h 127.0.0.1 -p 6379
127.0.0.1:6379> set firstKey "moyufed"
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth moyufed
OK
127.0.0.1:6379> set firstKey "moyufed"
OK
127.0.0.1:6379> get firstKey
"moyufed"
127.0.0.1:6379>

参考文档
Window配置Redis环境和简单使用:https://www.cnblogs.com/wxjnew/p/9160855.html

Windows下Redis安装配置和使用注意事项:https://www.cnblogs.com/LMJBlogs/p/11550170.html

首先配置.NET6的安装环境

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.4" />
	  <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.3" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="6.0.3" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.3" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.3" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.3">
		  <PrivateAssets>all</PrivateAssets>
		  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	  <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
	  <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
	  <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
	  <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
	  <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
	  <PackageReference Include="NModbus4.NetCore" Version="2.0.1" />
	  <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
  </ItemGroup>

</Project>

配置数据仓库:

public record Book
{
	public int Id { get; set; }
	public string Name { get; set; }
	public double Price { get; set; }
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

class BookConfig : IEntityTypeConfiguration<Book>
{
    public void Configure(EntityTypeBuilder<Book> builder)
    {
        builder.ToTable("Books");
        builder.HasKey(x => x.Id);
    }
}
using Microsoft.EntityFrameworkCore;

public class MyDbContext : DbContext
{
	private ConfigurationBuilder cfgBuilder = new ConfigurationBuilder();
	private string path = Directory.GetCurrentDirectory();
	public DbSet<Book> Books { get; set; }
	public MyDbContext()
	{
		//configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("Ipcfg.json").Build();
	}
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		base.OnModelCreating(modelBuilder);
		modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
	}

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
		cfgBuilder.AddJsonFile("VariableNode.json", optional: true, reloadOnChange: true);
		IConfigurationRoot configRoot = cfgBuilder.Build();
		string conn = configRoot.GetSection("ConnectionStrings:SqliteConnectionString").Value;
		string connString = "Data Source=" + path + conn;
		optionsBuilder.UseSqlite(connString);
	}
}
{
  "ConnectionStrings": {
    "SqliteConnectionString": "\\bin\\Debug\\net6.0\\Database\\DbSqlite.db",
    "MySQLConnectionString": "server=192.168.85.102; database=OneToMany; uid=root; pwd=123456;"
  },
  "ModbusNode": {
    "Id": 1,
    "NodeClassId": 1,
    "Name": "ModbusTCPClient",
    "Description": "1#ZG上位机测试",
    "ModbusType": "2000",
    "ConnectTimeOut": "2000",
    "CreateTime": "0",
    "ReConnectTime": "5000",
    "IsActive": "True",
    "MaxErrorTimes": "1",
    "KeyWay": "VarName",
    "UseAlarmCheck": "True",
    "ServerURL": "127.0.0.1",
    "Port": "502",
    "DataFormat": "ABCD",
    "VarNum": "6",
    "Type": "ModbusTCP",
    "SlaveID": "1",
    "Length": "10",
    "Start": "0",
    "Variable": [
      {
        "Id": 1,
        "Number": "1",
        "Name": "Float1",
        "Description": "40001-40002",
        "Type": "ModbusTCP",
        "VarAddress": 0,
        "Scale": "1",
        "Offset": "0",
        "Start": "0",
        "AccessProperty": "读写",
        "AlarmEnable": "True",
        "ArchiveEnable": "True",
        "SetLimitEnable": "True",
        "AlarmType": "True",
        "DiscreteAlarmType": "False",
        "DiscreteAlarmPriority": "0",
        "DiscreteAlarmNote": "null",
        "LoLoAlarmEnable": "True",
        "LoLoAlarmValue": "0",
        "LoLoAlarmPriority": "0",
        "LoLoAlarmNote": "40001-40002低低报警",
        "LowAlarmEnable": "True",
        "LowAlarmValue": "20",
        "LowAlarmPriority": "0",
        "LowAlarmNote": "40001-40002低报警",
        "HighAlarmEnable": "True",
        "HighAlarmValue": "80",
        "HighAlarmPriority": "0",
        "HighAlarmNote": "40001-40002高报警",
        "HiHiAlarmEnable": "True",
        "HiHiAlarmValue": "100",
        "HiHiAlarmPriority": "0",
        "HiHiAlarmNote": "40001-40002高高报警",
        "ArchivePeriod": "80",
        "SetLimitMax": "100",
        "SetLimitMin": "0",
        "VarType": "Float",
        "StoreType": "HoldingRegister4x",
        "InsertTime": "0",
        "Value": "0",
        "ModbusGroupId": 1
      },
      {
        "Id": 2,
        "Number": "2",
        "Name": "Float2",
        "Description": "40003-40004",
        "Type": "ModbusTCP",
        "VarAddress": 2,
        "Scale": "1",
        "Offset": "0",
        "Start": "0",
        "AccessProperty": "读写",
        "AlarmEnable": "True",
        "ArchiveEnable": "True",
        "SetLimitEnable": "True",
        "AlarmType": "True",
        "DiscreteAlarmType": "False",
        "DiscreteAlarmPriority": "0",
        "DiscreteAlarmNote": "null",
        "LoLoAlarmEnable": "True",
        "LoLoAlarmValue": "0",
        "LoLoAlarmPriority": "0",
        "LoLoAlarmNote": "40003-40004低低报警",
        "LowAlarmEnable": "True",
        "LowAlarmValue": "20",
        "LowAlarmPriority": "0",
        "LowAlarmNote": "40003-40004低报警",
        "HighAlarmEnable": "True",
        "HighAlarmValue": "80",
        "HighAlarmPriority": "0",
        "HighAlarmNote": "40003-40004高报警",
        "HiHiAlarmEnable": "True",
        "HiHiAlarmValue": "100",
        "HiHiAlarmPriority": "0",
        "HiHiAlarmNote": "40003-40004高高报警",
        "ArchivePeriod": "80",
        "SetLimitMax": "100",
        "SetLimitMin": "0",
        "VarType": "Float",
        "StoreType": "HoldingRegister4x",
        "InsertTime": "0",
        "Value": "0",
        "ModbusGroupId": 1
      },
      {
        "Id": 3,
        "Number": "3",
        "Name": "Float3",
        "Description": "40005-40006",
        "Type": "ModbusTCP",
        "VarAddress": 4,
        "Scale": "1",
        "Offset": "0",
        "Start": "0",
        "AccessProperty": "读写",
        "AlarmEnable": "True",
        "ArchiveEnable": "True",
        "SetLimitEnable": "True",
        "AlarmType": "True",
        "DiscreteAlarmType": "False",
        "DiscreteAlarmPriority": "0",
        "DiscreteAlarmNote": "null",
        "LoLoAlarmEnable": "True",
        "LoLoAlarmValue": "0",
        "LoLoAlarmPriority": "0",
        "LoLoAlarmNote": "40005-40006低低报警",
        "LowAlarmEnable": "True",
        "LowAlarmValue": "20",
        "LowAlarmPriority": "0",
        "LowAlarmNote": "40005-40006低报警",
        "HighAlarmEnable": "True",
        "HighAlarmValue": "80",
        "HighAlarmPriority": "0",
        "HighAlarmNote": "40005-40006高报警",
        "HiHiAlarmEnable": "True",
        "HiHiAlarmValue": "100",
        "HiHiAlarmPriority": "0",
        "HiHiAlarmNote": "40005-40006高高报警",
        "ArchivePeriod": "80",
        "SetLimitMax": "100",
        "SetLimitMin": "0",
        "VarType": "Float",
        "StoreType": "HoldingRegister4x",
        "InsertTime": "0",
        "Value": "0",
        "ModbusGroupId": 1
      },
      {
        "Id": 4,
        "Number": "4",
        "Name": "Float4",
        "Description": "40007-40008",
        "Type": "ModbusTCP",
        "VarAddress": 6,
        "Scale": "1",
        "Offset": "0",
        "Start": "0",
        "AccessProperty": "读写",
        "AlarmEnable": "True",
        "ArchiveEnable": "True",
        "SetLimitEnable": "True",
        "AlarmType": "True",
        "DiscreteAlarmType": "False",
        "DiscreteAlarmPriority": "0",
        "DiscreteAlarmNote": "null",
        "LoLoAlarmEnable": "True",
        "LoLoAlarmValue": "0",
        "LoLoAlarmPriority": "0",
        "LoLoAlarmNote": "40003-40004低低报警",
        "LowAlarmEnable": "True",
        "LowAlarmValue": "20",
        "LowAlarmPriority": "0",
        "LowAlarmNote": "40003-40004低报警",
        "HighAlarmEnable": "True",
        "HighAlarmValue": "80",
        "HighAlarmPriority": "0",
        "HighAlarmNote": "40003-40004高报警",
        "HiHiAlarmEnable": "True",
        "HiHiAlarmValue": "100",
        "HiHiAlarmPriority": "0",
        "HiHiAlarmNote": "40003-40004高高报警",
        "ArchivePeriod": "80",
        "SetLimitMax": "100",
        "SetLimitMin": "0",
        "VarType": "Float",
        "StoreType": "HoldingRegister4x",
        "InsertTime": "0",
        "Value": "0",
        "ModbusGroupId": 1
      },
      {
        "Id": 5,
        "Number": "5",
        "Name": "Ushort1",
        "Description": "40009",
        "Type": "ModbusTCP",
        "VarAddress": 8,
        "Scale": "1",
        "Offset": "0",
        "Start": "0",
        "AccessProperty": "读写",
        "AlarmEnable": "True",
        "ArchiveEnable": "True",
        "SetLimitEnable": "True",
        "AlarmType": "True",
        "DiscreteAlarmType": "False",
        "DiscreteAlarmPriority": "0",
        "DiscreteAlarmNote": "null",
        "LoLoAlarmEnable": "True",
        "LoLoAlarmValue": "0",
        "LoLoAlarmPriority": "0",
        "LoLoAlarmNote": "40009低低报警",
        "LowAlarmEnable": "True",
        "LowAlarmValue": "20",
        "LowAlarmPriority": "0",
        "LowAlarmNote": "40009低报警",
        "HighAlarmEnable": "True",
        "HighAlarmValue": "80",
        "HighAlarmPriority": "0",
        "HighAlarmNote": "40009高报警",
        "HiHiAlarmEnable": "True",
        "HiHiAlarmValue": "100",
        "HiHiAlarmPriority": "0",
        "HiHiAlarmNote": "40009高高报警",
        "ArchivePeriod": "80",
        "SetLimitMax": "100",
        "SetLimitMin": "0",
        "VarType": "UShort",
        "StoreType": "HoldingRegister4x",
        "InsertTime": "0",
        "Value": "0",
        "ModbusGroupId": 1
      },
      {
        "Id": 6,
        "Number": "6",
        "Name": "Ushort2",
        "Description": "40010",
        "Type": "ModbusTCP",
        "VarAddress": 9,
        "Scale": "1",
        "Offset": "0",
        "Start": "0",
        "AccessProperty": "读写",
        "AlarmEnable": "True",
        "ArchiveEnable": "True",
        "SetLimitEnable": "True",
        "AlarmType": "True",
        "DiscreteAlarmType": "False",
        "DiscreteAlarmPriority": "0",
        "DiscreteAlarmNote": "null",
        "LoLoAlarmEnable": "True",
        "LoLoAlarmValue": "0",
        "LoLoAlarmPriority": "0",
        "LoLoAlarmNote": "40009低低报警",
        "LowAlarmEnable": "True",
        "LowAlarmValue": "20",
        "LowAlarmPriority": "0",
        "LowAlarmNote": "40009低报警",
        "HighAlarmEnable": "True",
        "HighAlarmValue": "80",
        "HighAlarmPriority": "0",
        "HighAlarmNote": "40009高报警",
        "HiHiAlarmEnable": "True",
        "HiHiAlarmValue": "100",
        "HiHiAlarmPriority": "0",
        "HiHiAlarmNote": "40009高高报警",
        "ArchivePeriod": "80",
        "SetLimitMax": "100",
        "SetLimitMin": "0",
        "VarType": "UShort",
        "StoreType": "HoldingRegister4x",
        "InsertTime": "0",
        "Value": "0",
        "ModbusGroupId": 1
      }
    ]
  }
}
namespace RedisDemonWebApi
{
    public interface IRepository
    {
        Book? GetBooksByIdAsync(int a);
    }
}

namespace RedisDemonWebApi
{
    public class Repository : IRepository
    {
        private MyDbContext db;

        public Repository(MyDbContext db)
        {
            this.db = db;
        }


        public Book? GetBooksByIdAsync(int a)
        {
            Book? book = db.Books.Where(x => x.Id == a).SingleOrDefault();
            if (book==null)
            {
                return null;
            }
            else
            {
                return book;
            }
            //return db.Books.Single(x => x.Id == a);
        }
    }
}


依赖注入 :

global using RedisDemonWebApi;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddMemoryCache();//内存缓存
builder.Services.AddScoped<IMemoryCacheHelper, MemoryCacheHelper>();
builder.Services.AddDbContext<MyDbContext>();
builder.Services.AddScoped<IRepository, Repository>();
builder.Services.AddStackExchangeRedisCache(opt =>
{
    opt.Configuration = "localhost";
    opt.InstanceName = "cache1_";
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

控制器:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using System.Text.Json;

namespace RedisDemonWebApi.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private readonly IDistributedCache distCache;
        private readonly IDistributedCacheHelper helper;
        private readonly ILogger <TestController> logger;
        private readonly IMemoryCache memCache;
        private readonly IRepository repository;


        public TestController(IRepository repository, IMemoryCache memCache, ILogger<TestController> logger, IDistributedCache distCache)
        {
            //this.distCache = distCache;
            //this.helper = helper;
            this.repository = repository;
            this.memCache = memCache;
            this.logger = logger;
            this.distCache = distCache;
        }

        //[HttpGet]
        //public string Now()
        //{
        //    string s = distCache.GetString("Now");
        //    if (s == null)
        //    {
        //        s = DateTime.Now.ToString();
        //        var opt = new DistributedCacheEntryOptions();
        //        opt.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
        //        distCache.SetString("Now", s, opt);
        //    }
        //    return s;
        //}

        [HttpGet]
        //public ActionResult<Book?> GetBookById(int Id)
        //{
        //    Book? book = repository.GetBooksByIdAsync(Id);
        //    return Ok(book);
        //}

        //内存缓存
        public async Task<ActionResult<Book?>> GetBookById(int Id)
        {
            logger.LogInformation($"开始执行GetBookById,Id={Id}");
            //先查找内存缓存, 没有再进入repository查找
            Book? b = await memCache.GetOrCreateAsync("Book" + Id, async (e) =>
               {
                   logger.LogInformation($"缓存中没找到,去数据库,Id={Id}");
                   return repository.GetBooksByIdAsync(Id);
               });
            logger.LogInformation($"GetOrCreateAsync结果{b}");
            if (b == null)
            {
                return NotFound("找不到");
            }
            else
            {
                return Ok(b);
            }
        }

        //分布式缓存
        [HttpGet]
        public async Task<ActionResult<Book?>> GetBookFromRedisById(int Id)
        {
            Book? book=null;
            logger.LogInformation($"开始执行GetBookById,Id={Id}");
            //先去分布式缓存中寻找
            string? s = await distCache.GetStringAsync("Book" + Id);
            //如果没找到, 去数据库中去找, 然后存入分布式缓存
            if (s==null)
            {
                logger.LogInformation($"缓存中没找到,去数据库找,Id={Id}");
                book = repository.GetBooksByIdAsync(Id);

                //设置Redis中的过期时间opt
                var opt = new DistributedCacheEntryOptions();
                opt.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
                //json序列化成字符串存入缓存中
                await distCache.SetStringAsync("Book" + Id, JsonSerializer.Serialize(book), opt);
                logger.LogInformation($"在数据库中找到,Id={Id},将其序列化为字符串存入Redis");
            }

            //如果在缓存中找到, 需要将缓存中的字符串s反序列化为book
            else
            {
                book = JsonSerializer.Deserialize<Book?>(s);
                logger.LogInformation($"直接在Redis中找到了,Id={Id}");
            }

            //都没找到就是不存在了
            if (book==null)
            {
                logger.LogInformation($"数据库和Redis中都不存在Id={Id}的书籍");
                return NotFound("不存在");
            }
            else
            {
                return book;
            }
        }



    }
}

使用Swagger 测试 :

在这里插入图片描述

日志 :

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7063
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5063
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\cSharpe\code\RedisDemon1\RedisDemonWebApi\
info: RedisDemonWebApi.Controllers.TestController[0]
      开始执行GetBookById,Id=2
info: RedisDemonWebApi.Controllers.TestController[0]
      缓存中没找到,去数据库找,Id=2
info: RedisDemonWebApi.Controllers.TestController[0]
      在数据库中找到,Id=2,将其序列化为字符串存入Redis
info: RedisDemonWebApi.Controllers.TestController[0]
      开始执行GetBookById,Id=2
info: RedisDemonWebApi.Controllers.TestController[0]
      直接在Redis中找到了,Id=2

Redis中 :

在这里插入图片描述

如果查询的Id对应的内容不存在, 则Redis也会把null值存在Redis, 这样避免了缓存穿透的问题

在这里插入图片描述

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-04-23 10:53:19  更:2022-04-23 10:55:24 
 
开发: 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年11日历 -2024/11/24 3:01:24-

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