第10 CURD操作与RedisCache缓存的强制清理的实现

using System.Net;

using Microsoft.Extensions.Caching.Distributed;

using Core.Caching;

using Core.Configuration;

using StackExchange.Redis;

namespace Services.Caching

{

    /// <summary>

    /// Redis分布式缓存数据库软件管理器--类】

    /// <remarks>

    /// 摘要:

    ///    通过该类中的方法成员实现了通过“StackExchange.Redis”中间件实例对Redis软件缓存数据更加底层的操作及其的“DistributedCacheManager”抽象类的实例化。

    /// </remarks>

    /// </summary>

    public class RedisCacheManager : DistributedCacheManager

    {

        #region 变量--私有/保护

        /// <summary>

        /// Redis连接封装器】

        /// <remarks>

        /// 摘要:

        ///     Redis连接封装器的1个指定实例。

        /// </remarks>

        /// </summary>

        private static RedisConnectionWrapper _connectionWrapper;

        /// <summary>

        /// 【数据库】

        /// <remarks>

        /// 摘要:

        ///     用于建立当前程序与Redis分布式缓存数据库软件中分布式缓存数据库的连接。

        /// </remarks>

        /// </summary>

        private readonly IDatabase _db;

        #endregion

        #region 拷贝构造方法

        /// <param name="appSettings">应用配置类的1个指定实例。</param>

        /// <param name="distributedCache">分布式缓存接口实例,该实例实际上是:“StackExchange.Redis”中间件的实例,通过该实例实现当前程序通过“StackExchange.Redis”中间件的实例与Redis分布式缓存数据库的交互操作。</param>

        /// <summary>

        /// 【拷贝构造方法】

        /// <remarks>

        /// 摘要:

        ///     通过拷贝构造方法,对当前类中的同名变量成员进行实例化。

        /// </remarks>

        /// </summary>

        public RedisCacheManager(AppSettings appSettings, IDistributedCache distributedCache) : base(appSettings,

            distributedCache)

        {

            _connectionWrapper ??=

                new RedisConnectionWrapper(appSettings.Get<DistributedCacheConfig>().ConnectionString);

            _db = _connectionWrapper.GetDatabase();

        }

        #endregion

        #region 方法--私有/保护

        /// <param name="endPoint">Redis分布式缓存数据库软件中所有可用的终结点。</param>

        /// <param name="prefix">1个指定的前缀字符串,默认值:null,即获取所有的Redis缓存键实例。</param>

        /// <summary>

        /// 【获取服务】

        /// <remarks>

        /// 摘要:

        ///     Redis分布式缓存数据库软件所有的分布式缓存数据库(0-15)中,获取与指定前缀字符串相匹配的所有的Redis缓存键实例。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     与指定前缀字符串相匹配的所有的Redis缓存键实例。

        /// </returns>

        /// </summary>

        protected virtual IEnumerable<RedisKey> GetKeys(EndPoint endPoint, string prefix = null)

        {

            var server = _connectionWrapper.GetServer(endPoint);

            var keys = server.Keys(_db.Database, string.IsNullOrEmpty(prefix) ? null : $"{prefix}*");

            return keys;

        }

        #endregion

        #region 方法

        /// <param name="prefix">1个指定的前缀字符串。</param>

        /// <param name="prefixParameters">数组实例,该实例中存储着n个泛型实例,这些实例为前缀字符串的拼接提供数据支撑。</param>

        /// <summary>

        /// 【异步通过前缀字符串移除实例】

        /// <remarks>

        /// 摘要:

        ///    该方法的覆写方法用于根据1个新的前缀字符串,从缓存数据库中移除1/n个指定的缓存项(/(“JSON”编码格式的)值对)

        /// </remarks>

        /// </summary>

        public override async Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters)

        {

            prefix = PrepareKeyPrefix(prefix, prefixParameters);

            foreach (var endPoint in _connectionWrapper.GetEndPoints())

            {

                var keys = GetKeys(endPoint, prefix);

                _db.KeyDelete(keys.ToArray());

            }

            await RemoveByPrefixInstanceDataAsync(prefix);

        }

        /// <param name="prefix">1个指定的前缀字符串。</param>

        /// <param name="prefixParameters">数组实例,该实例中存储着n个泛型实例,这些实例为前缀字符串的拼接提供数据支撑。</param>

        /// <summary>

        /// 【通过前缀字符串移除实例】

        /// <remarks>

        /// 摘要:

        ///     该方法是抽象方法,该方法的覆写方法用于根据1个新的前缀字符串,从缓存数据库中移除1/n个指定的缓存项(/(“JSON”编码格式的)值对)

        /// </remarks>

        /// </summary>

        public override void RemoveByPrefix(string prefix, params object[] prefixParameters)

        {

            prefix = PrepareKeyPrefix(prefix, prefixParameters);

            foreach (var endPoint in _connectionWrapper.GetEndPoints())

            {

                var keys = GetKeys(endPoint, prefix);

                _db.KeyDelete(keys.ToArray());

            }

            RemoveByPrefixInstanceData(prefix);

        }

        /// <summary>

        /// 【异步清理终结点】

        /// <remarks>

        /// 摘要:

        ///     清理释放Redis分布式缓存数据库软件分布式缓存数据库(0-15)中所有缓存项。

        /// </remarks>

        /// </summary>

        public override async Task ClearAsync()

        {

            await _connectionWrapper.FlushDatabaseAsync();

            ClearInstanceData();

        }

        #endregion

        #region 嵌套类

        /// <summary>

        /// Redis连接封装器--类】

        /// <remarks>

        /// 摘要:

        ///    通过该类中的方法成员实现了调用“StackExchange.Redis”中间件的实例,从而Redis软件对缓存数据的管理操作,这些管理操作包含:缓存数据加载,缓存数据移除和缓存数据清理等。。

        /// </remarks>

        /// </summary>

        protected class RedisConnectionWrapper

        {

            #region 变量--私有/保护

            /// <summary>

            /// 【锁】

            /// <remarks>

            /// 摘要:

            ///     锁实例。

            /// </remarks>

            /// </summary>

            private readonly object _lock = new();

            /// <summary>

            /// 【分布式缓存】

            /// <remarks>

            /// 摘要:

            ///     Redis分布式缓存数据库软件连接器实例,该实例实际上是:“StackExchange.Redis”中间件的实例,通过该实例实现当前程序通过“StackExchange.Redis”中间件的实例与Redis分布式缓存数据库的交互操作。

            /// </remarks>

            /// </summary>

            private volatile ConnectionMultiplexer _connection;

            /// <summary>

            /// 【连接字符串】

            /// <remarks>

            /// 摘要:

            ///    Redis分布式缓存数据库软件的连接字符串。

            /// </remarks>

            /// </summary>

            private readonly Lazy<string> _connectionString;

            #endregion

            #region 构造方法

            /// <param name="connectionString">应用配置类的1个指定实例。</param>

            /// <summary>

            /// 【默认构造方法】

            /// <remarks>

            /// 摘要:

            ///     通过默认构造方法,实例化Redis分布式缓存数据库软件的连接字符串。

            /// </remarks>

            /// </summary>

            public RedisConnectionWrapper(string connectionString)

            {

                _connectionString = new Lazy<string>(connectionString);

            }

            #endregion

            #region 方法----私有/保护

            /// <summary>

            /// 【获取连接】

            /// <remarks>

            /// 摘要:

            ///     建立当前程序与Redis分布式缓存数据库软件的连接。

            /// </remarks>

            /// <returns>

            /// 返回:

            ///     Redis分布式缓存数据库软件的连接器实例。

            /// </returns>

            /// </summary>

            protected ConnectionMultiplexer GetConnection()

            {

                if (_connection != null && _connection.IsConnected)

                    return _connection;

                lock (_lock)

                {

                    if (_connection != null && _connection.IsConnected)

                        return _connection;

                    //显式销毁释放内存中Redis分布式缓存数据库软件的连接器实例,该实例实际上是:显式销毁释放内存中的“StackExchange.Redis”中间件实例。

                    _connection?.Dispose();

                    //实例化Redis分布式缓存数据库软件的连接器实例,该实例实际上是:“StackExchange.Redis”中间件实例。

                    _connection = ConnectionMultiplexer.Connect(_connectionString.Value);

                }

                return _connection;

            }

            #endregion

            #region 方法--销毁

            /// <summary>

            /// 【销毁】

            /// <remarks>

            /// 摘要:

            ///     显式销毁释放内存中的连接器实例,该实例实际上是:显式销毁释放内存中的“StackExchange.Redis”中间件实例。

            /// </remarks>

            /// </summary>

            public void Dispose()

            {

                //显式销毁释放内存中的连接器实例,该实例实际上是:显式销毁释放内存中的“StackExchange.Redis”中间件实例。

                _connection?.Dispose();

            }

            #endregion

            #region 方法

            /// <summary>

            /// 【获取数据库】

            /// <remarks>

            /// 摘要:

            ///     建立当前程序与Redis分布式缓存数据库软件中分布式缓存数据库的连接。

            /// </remarks>

            /// <returns>

            /// 返回:

            ///    数据库连接接口实例,该实例用于建立当前程序与Redis分布式缓存数据库软件中分布式缓存数据库的连接。

            /// </returns>

            /// </summary>

            public IDatabase GetDatabase()

            {

                return GetConnection().GetDatabase();

            }

            /// <param name="endPoint">Redis分布式缓存数据库软件中所有可用的终结点。</param>

            /// <summary>

            /// 【获取服务】

            /// <remarks>

            /// 摘要:

            ///     获取Redis分布式缓存数据库软件中所有可用的终结点,即所有的分布式缓存数据库(0-15)

            /// </remarks>

            /// <returns>

            /// 返回:

            ///     服务接口实例,该实例用于建立当前程序与Redis分布式缓存数据库软件中分布式缓存数据库的连接。

            /// </returns>

            /// </summary>

            public IServer GetServer(EndPoint endPoint)

            {

                return GetConnection().GetServer(endPoint);

            }

            /// <summary>

            /// 【获取终结点】

            /// <remarks>

            /// 摘要:

            ///     获取Redis分布式缓存数据库软件中所有可用的终结点,即所有的分布式缓存数据库(0-15)

            /// </remarks>

            /// <returns>

            /// 返回:

            ///     数组实例,该实例存储着Redis分布式缓存数据库软件中所有可用的终结点,即所有的分布式缓存数据库(0-15)

            /// </returns>

            /// </summary>

            public EndPoint[] GetEndPoints()

            {

                return GetConnection().GetEndPoints();

            }

            /// <summary>

            /// 【异步清理终结点】

            /// <remarks>

            /// 摘要:

            ///     清理释放Redis分布式缓存数据库软件分布式缓存数据库(0-15)中所有缓存项。

            /// </remarks>

            /// </summary>

            public async Task FlushDatabaseAsync()

            {

                var endPoints = GetEndPoints();

                foreach (var endPoint in endPoints)

                    await GetServer(endPoint).FlushDatabaseAsync();

            }

            #endregion

        }

        #endregion

    }

}

2 Srvices.Caching.MemoryDistributedCacheManager

using Microsoft.Extensions.Caching.Distributed;

using Core.Caching;

using Core.Configuration;

namespace Nop.Services.Caching

{

    /// <summary>

    /// Redis分布式缓存委托事件内存管理器--类】

    /// <remarks>

    /// 摘要:

    ///    通过该类中的方法成员实现了把对Redis分布式缓存数据库操作的委托事件存储到一个列表实例(内存)中,从而通过对该列表实例(内存)的操作实现对Redis分布式缓存数据库操作委托事件的集中管理。

    /// </remarks>

    /// </summary>

    public class MemoryDistributedCacheManager : DistributedCacheManager

    {

        #region 变量--私有/保护

        /// <summary>

        /// 【键列表实例】

        /// <remarks>

        /// 摘要:

        ///     1个指定的列表实例,该列表实例(内存)中存储着Redis分布式缓存数据库操作的所有委托事件。

        /// </remarks>

        /// </summary>

        private static readonly List<string> _keysList = new();

        #endregion

        #region 拷贝构造方法

        /// <param name="appSettings">应用配置类的1个指定实例。</param>

        /// <param name="distributedCache">分布式缓存接口实例,该实例实际上是:“StackExchange.Redis”中间件的实例,通过该实例实现当前程序通过“StackExchange.Redis”中间件的实例与Redis分布式缓存数据库的交互操作。</param>

        /// <summary>

        /// 【拷贝构造方法】

        /// <remarks>

        /// 摘要:

        ///     通过拷贝构造方法,把Redis分布式缓存数据库操作的所有委托事件存储到列表实例中或从列表实例中移除。

        /// </remarks>

        /// </summary>

        public MemoryDistributedCacheManager(AppSettings appSettings, IDistributedCache distributedCache) : base(

            appSettings, distributedCache)

        {

            _onKeyAdded += key =>

            {

                using var _ = _locker.Lock();

                if (!_keysList.Contains(key.Key))

                    _keysList.Add(key.Key);

            };

            _onKeyRemoved += key =>

            {

                using var _ = _locker.Lock();

                if (_keysList.Contains(key.Key))

                    _keysList.Remove(key.Key);

            };

        }

        #endregion

        #region 方法

        /// <param name="prefix">1个指定的前缀字符串。</param>

        /// <param name="prefixParameters">数组实例,该实例中存储着n个泛型实例,这些实例为前缀字符串的拼接提供数据支撑。</param>

        /// <summary>

        /// 【异步通过前缀字符串移除实例】

        /// <remarks>

        /// 摘要:

        ///    该方法的覆写方法用于根据1个新的前缀字符串,从缓存数据库中移除1/n个指定的缓存项(/(“JSON”编码格式的)值对)后,并从列表实例中移除相应的Redis分布式缓存数据库操作的委托事件。

        /// </remarks>

        /// </summary>

        public override async Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters)

        {

            using (var _ = _locker.Lock())

            {

                prefix = PrepareKeyPrefix(prefix, prefixParameters);

                //从缓存数据库中移除1/n个指定的缓存项(/(“JSON”编码格式的)值对)后,并从列表实例中移除相应的Redis分布式缓存数据库操作的委托事件。

                foreach (var key in _keysList

                             .Where(key => key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))

                             .ToList())

                {

                    await _distributedCache.RemoveAsync(key);

                    _keysList.Remove(key);

                }

            }

            await RemoveByPrefixInstanceDataAsync(prefix);

        }

        /// <param name="prefix">1个指定的前缀字符串。</param>

        /// <param name="prefixParameters">数组实例,该实例中存储着n个泛型实例,这些实例为前缀字符串的拼接提供数据支撑。</param>

        /// <summary>

        /// 【通过前缀字符串移除实例】

        /// <remarks>

        /// 摘要:

        ///    该方法的覆写方法用于根据1个新的前缀字符串,从缓存数据库中移除1/n个指定的缓存项(/(“JSON”编码格式的)值对)后,并从列表实例中移除相应的Redis分布式缓存数据库操作的委托事件。

        /// </remarks>

        /// </summary>

        public override void RemoveByPrefix(string prefix, params object[] prefixParameters)

        {

            using (var _ = _locker.Lock())

            {

                prefix = PrepareKeyPrefix(prefix, prefixParameters);

                //从缓存数据库中移除1/n个指定的缓存项(/(“JSON”编码格式的)值对)后,并从列表实例中移除相应的Redis分布式缓存数据库操作的委托事件。

                foreach (var key in _keysList

                             .Where(key => key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))

                             .ToList())

                {

                    _distributedCache.Remove(key);

                    _keysList.Remove(key);

                }

            }

            RemoveByPrefixInstanceData(prefix);

        }

        /// <summary>

        /// 【异步清理】

        /// <remarks>

        /// 摘要:

        ///    根据列表实例从缓存数据库中移除与其相关的所有缓存项后,清理该列表实例。

        /// </remarks>

        /// </summary>

        public override async Task ClearAsync()

        {

            using (var _ = _locker.Lock())

            {

                //根据列表实例从缓存数据库中移除与其相关的所有缓存项。

                foreach (var key in _keysList)

                    await _distributedCache.RemoveAsync(key);

                //执行列表实例清理操作。

                _keysList.Clear();

            }

            ClearInstanceData();

        }

        #endregion

    }

}

3 重构appsettings.json

{

  "ConnectionStrings": {

    "ConnectionString": "Data Source=.;Initial Catalog=ShopRazor;Integrated Security=False;Persist Security Info=False;User ID=zz;Password=zz;MultipleActiveResultSets=true;Trust Server Certificate=True",

    "DataProvider": "sqlserver",

    "SQLCommandTimeout": null

  },

  "CacheConfig": {

    "DefaultCacheTime": 60,

    "ShortTermCacheTime": 3,

    "BundledFilesCacheTime": 120

  },

  "DistributedCacheConfig": {

    "DistributedCacheType": "redis",

    "Enabled": true,

    "ConnectionString": "127.0.0.1:6379,ssl=False",

    "SchemaName": "dbo",

    "TableName": "DistributedCache"

  }

}

4 重构Program.cs文件

using Core.Configuration;

using Core.Infrastructure;

using Data;

using Data.Configuration;

using Microsoft.EntityFrameworkCore;

using Framework.Infrastructure.Extensions;

using Core.Caching;

using Core.Events;

using Services.Events;

using Nop.Services.Caching;

using Services.Caching;

var builder = WebApplication.CreateBuilder(args);

//如果启动项中不存在“appsettings.json”文件,则通过.Net(Core)的内置方法自动新建“appsettings.json”文件。

builder.Configuration.AddJsonFile("appsettings.json", true, true);

//把当前程序中所有继承了“IConfig”接口的具体实现类的实例,依赖注入到.Net(Core)内置依赖注入容器实例中,如果需要并把这些数据持久化存储到"appsettings.json"文件。

builder.Services.ConfigureApplicationSettings(builder);

builder.Services.AddScoped<INopFileProvider, NopFileProvider>();

var appSettings = Singleton<AppSettings>.Instance;

var distributedCacheConfig = appSettings.Get<DistributedCacheConfig>();

//必须先设定 "Enabled": true,且启动Redis分布式数据库,如果 "Enabled": false,则必须把下面2行给注释掉。

if (!distributedCacheConfig.Enabled)

    return;

switch (distributedCacheConfig.DistributedCacheType)

{

    case DistributedCacheType.Memory:

        builder.Services.AddDistributedMemoryCache();

        break;

    case DistributedCacheType.Redis:

        builder.Services.AddStackExchangeRedisCache(options =>

        {

            options.Configuration = distributedCacheConfig.ConnectionString;

        });

        break;

}

if (distributedCacheConfig.Enabled)

{

    switch (distributedCacheConfig.DistributedCacheType)

    {

        case DistributedCacheType.Memory:

            builder.Services.AddScoped<ILocker, MemoryDistributedCacheManager>();

            builder.Services.AddScoped<IStaticCacheManager, MemoryDistributedCacheManager>();

            break;

        case DistributedCacheType.Redis:

            builder.Services.AddScoped<ILocker, RedisCacheManager>();

            builder.Services.AddScoped<IStaticCacheManager, RedisCacheManager>();

            break;

    }

}

else

{

    builder.Services.AddSingleton<ILocker, MemoryCacheManager>();

    builder.Services.AddSingleton<IStaticCacheManager, MemoryCacheManager>();

}

builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

builder.Services.AddSingleton<IEventPublisher, EventPublisher>();

var typeFinder = Singleton<ITypeFinder>.Instance;

var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();

foreach (var consumer in consumers)

    foreach (var findInterface in consumer.FindInterfaces((type, criteria) =>

    {

        var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());

        return isMatch;

    }, typeof(IConsumer<>)))

        builder.Services.AddScoped(findInterface, consumer);

//注意:在动态对EntityFrameworkCore中间件进行实例化时,必须使用“AddDbContextPool” 内置方法替换“AddDbContext”内置方法,

//否则就会出现异常:“System.InvalidOperationException: The service collection cannot be modified because it is read-only”

builder.Services.AddDbContextPool<EFCoreContext>(options => {

    //从单例实例的字典成员实例中获取当前程序所有配置相关数据。

    AppSettings _appSettings = Singleton<AppSettings>.Instance;

    //从应用配置类实例中获取数据库连接相关数据。

    DataConfig _dataConfig = _appSettings.Get<DataConfig>();

    //说明:如果想要“EntityFrameworkCore”中间件支持多数据库软件,则把选择条件中的所有中间件都注入到依赖注入到.Net(Core)框架内置容器即可,

    //选择条件来限定当前程序只支持所设定的1个数据库软件,当然“DataConfig”类与“appsettings.json”文件也必须为支持多数据库软件进行重构。

    if (_dataConfig.DataProvider.ToString().Equals("sqlserver", StringComparison.InvariantCultureIgnoreCase))

    {

        //通过“DbContextOptionsBuilder”实例中的参数实例,为“Microsoft.EntityFrameworkCore.SqlServer”中间件的实例化提供参数实例,

        //最终把“Microsoft.EntityFrameworkCore.SqlServer”中间件实例,依赖注入到.Net(Core)框架内置容器中。

        //IIS发布部署连接字符串必须使用“SQL Server身份认证数据库连接方式,才能实现发布部署程序与数据库的CURD的操作。

        options.UseSqlServer(_dataConfig.ConnectionString);

    }

});

//通过AddRazorRuntimeCompilation依赖注入中间件实现页面修改热加载(Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation)

builder.Services

    .AddControllersWithViews()

    .AddRazorRuntimeCompilation();

//把具体现类和中间件注入到内置依赖注入容器后,并把.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例通过“Engine”(引擎)单例实例存储到单例类的字典属性成员实例中。

//注意:从依赖注入到.Net(Core)框架内置容器中,获取“IServiceProvider”接口实例,必须定义在最后,

//否则“GetServices”/“GetRequiredService”方法将有可能不能获取取1个指定类的实例,因为该类的实例还没有依赖注入到.Net(Core)框架内置容器中。

builder.Services.ConfigureApplicationServices(builder);

var app = builder.Build();

// Configure the HTTP request pipeline.

if (!app.Environment.IsDevelopment())

{

    app.UseExceptionHandler("/Home/Error");

    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.

    app.UseHsts();

}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

/*注意:

    .net7最好不要直接使用app.UseEndpointsapp.UseMvc来集成路由匹配模式,否则会出现:“ASP0014”警告信息,

为了避免该警告信息直接使用最小APIapp.MapControllerRoute来集成路由匹配模式。

 */

app.MapControllerRoute(

        name: "areaRoute",

        pattern: $"{{area:exists}}/{{controller=Install}}/{{action=Index}}/{{id?}}");

//动态实例化EntityFrameworkCore中间件,必须把InstallRefactoring/Index设定为默认启动页面,

//否则依然会因为当前类的拷贝构造方法实例化EntityFrameworkCore中间件是在程序启动前就被实例化的,如果无数据库如果数据库连接字符串,

//那么就会因EntityFrameworkCore中间件实例中不包含数据库如果数据库连接字符串,从而造成上述异常。

app.MapControllerRoute(

      name: "default",

      pattern: "{controller=InstallRefactoring}/{action=Index}/{id?}");

// 通过.NetCore框架的内置管道接口实例,实例化“IServiceProvider”接口实例,同时把继承于“IStartup”所有具体现类中的中间件实例集成到.NetCore框架内置管道中。

app.ConfigureRequestPipeline();

app.Run();

5 运行过程

 

 

 

 

 

 

 

 

 

 

 对以上功能更为具体实现和注释见230510_010ShopRazor(CURD操作与RedisCache缓存的强制清理的实现)。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023零售店铺管理系统最新排名,这5款性价比高!

很多零售店铺的老板&#xff0c;每天都在被开单收银、记账对账、商品销售、销售数据等各种琐事困扰&#xff0c;使用传统的人工管理模式&#xff0c;耗费了大量的时间和成本&#xff0c;也没有达到理想的效果。 其实&#xff0c;零售店铺管理也可以很简单省事&#xff0c;借助零…

TCP三次握手

TCP三次握手 文章目录 TCP三次握手1. TCP三次握手过程和状态变迁1. 准备工作2. 进行连接 2. 能把三次握手改为两次握手吗&#xff1f;3. 改为两次握手会出现什么后果&#xff1f;4. 改为四次握手行不行&#xff1f;5. TCP第三次握手失败了怎么办&#xff1f;6. 三次握手是否可以…

探索 Elasticsearch 8.X Terms Set 检索的应用与原理

1、Terms Set 检索简介 Terms Set查询是Elasticsearch中一种强大的查询类型&#xff0c;主要用于处理多值字段中的文档匹配。 其核心功能在于&#xff0c;它可以检索至少匹配一定数量给定词项的文档&#xff0c;其中匹配的数量可以是固定值&#xff0c;也可以是基于另一个字段的…

抖音商城小程序搭建的注意事项

抖音商城小程序已经成为了越来越多电商企业的选择&#xff0c;毕竟它具有强大的用户资源和社交传播力。但是&#xff0c;在搭建抖音商城小程序的过程中&#xff0c;还有一些需要注意的事项。 1、制定明确的策略和目标 在搭建抖音商城小程序前&#xff0c;必须事先制定明确的策…

三步配置轻量级服务器nginx

这里写目录标题 一 nginx及其应用场景1.什么是nginx2.应用场景背诵 二 nginx的配置安装1.选择nginx版本2. 安装配置环境3.安装nginx4.启动nginx 三 访问 一 nginx及其应用场景 1.什么是nginx 一款轻量级的Web服务器&#xff0c;反向代理服务器&#xff0c;以及电子邮件代理服…

java获取resources路径的方法

我们在写程序的时候&#xff0c;有时候会发现代码不能正常运行&#xff0c;出现提示异常的问题&#xff0c;这就说明我们的代码没有执行完&#xff0c;也就是没有 resource&#xff0c;其实遇到这种情况&#xff0c;我们只需要把代码重新执行一遍即可。 在 java中是可以实现 re…

实验六 触发器与存储过程

实验六 触发器与存储过程 目录 实验六 触发器与存储过程 1、SQL触发器&#xff1a;删除学生数据题目代码题解 2、SQL触发器&#xff1a;创建成绩表插入触发器题目代码题解 3、 SQL存储过程&#xff1a;查询订单题目代码题解 4、SQL存储过程&#xff1a;建立存储过程&#xff0c…

浅析S32K324的时钟树

S32K3XX系列的时钟树梳理 如下图为S32K3XX系列的时钟树图&#xff1a; 一、时钟源 时钟源说明FIRC快速内部RC时钟源SIRC慢速内部RC时钟源FXOSC快速外部晶振时钟源SXOSC慢速外部晶振时钟源 S32K3系列的mcu有4个时钟源如上表。 二、FIRC&#xff08;快速内部RC时钟源&#…

【A200】 TX1核心 JetPack4.6.2版本如何修改DTB文件测试全部SPI

大家好&#xff0c;我是虎哥&#xff0c;很长时间没有发布新内容&#xff0c;主要是这段时间集中精力&#xff0c;研究DTB设备树的修改&#xff0c;以适配不同载板&#xff0c;同时也是专门做了一个TX1&TX2核心&#xff0c;双网口&#xff0c;可以使用SPI 扩展CAN接口的载板…

Actuators + jolokia

Actuators + jolokia Jolokia造成的XXE漏洞 首先我们查看我们当前环境http://x.x.x.x/jolokia/list地址,是否存在reloadByURL这个方法, 这个方法是造成RCE的关键。因为logback组件提供的reloadByURL操作使我们可以从外部URL重新加载日志配置 创建logback.xml和file.dtd文件…

【神经网络】tensorflow实验9--分类问题

1. 实验目的 ①掌握逻辑回归的基本原理&#xff0c;实现分类器&#xff0c;完成多分类任务&#xff1b; ②掌握逻辑回归中的平方损失函数、交叉熵损失函数以及平均交叉熵损失函数。 2. 实验内容 ①能够使用TensorFlow计算Sigmoid函数、准确率、交叉熵损失函数等&#xff0c…

tomcat集群下的session共享和负载均衡

环境 操作系统&#xff1a;windows tomcat1&#xff1a;Apache Tomcat/7.0.52&#xff08;8085&#xff09; tomcat2&#xff1a;Apache Tomcat/7.0.52&#xff08;8086&#xff09; jre&#xff1a;1.7.0_80 nginx&#xff1a;nginx-1.20.1&#xff08;8070&#xff09; redis…

人工智能洗稿-免费自媒体洗稿工具

文字洗稿 文字洗稿是指通过修改、重组、删除、替换等手段对文本进行优化、清理和调整&#xff0c;以达到改善文章质量、增加独特性和提高可读性的目的。文字洗稿是自媒体行业的一个重要需求&#xff0c;尤其是在批量撰写文章或需要大量修改文本的情况下。文字洗稿分为自动洗稿…

如何用ChatGPT搭建品牌文本体系?(品牌名+slogan+品牌故事)

该场景对应的关键词库&#xff08;26个&#xff09;&#xff1a; 品牌名、奶茶、中文名、情感联想度、饮料、价值观/理念、发音、slogan、产品功能导向、行业性质导向、经营理念导向、消费者观念导向、口语化、修辞手法、品牌故事、创始人初心品牌故事、里程碑事件故事、产品初…

Intellij中使用Spotless 格式化代码

Spotless简介 在一些大型项目或开源项目&#xff0c;由于开发人员太多&#xff0c;导致各个代码格式不统一。会让整体项目的代码可读性变差。统一代码格式使用maven中的Spotless插件就是不错的选择。 Spotless 是一个代码格式化工具&#xff0c;它有以下功能&#xff1a; 支…

第6章 静态代理

第6章 静态代理 把所有静态资源的访问改为访问nginx&#xff0c;而不是访问tomcat&#xff0c;这种方式叫静态代理。因为nginx更擅长于静态资源的处理&#xff0c;性能更好&#xff0c;效率更高。 ​ 所以在实际应用中&#xff0c;我们将静态资源比如图片、css、html、js等交…

helm和chart

Helm helm是Kubernetes 应用的包管理工具&#xff0c;主要用来管理 Charts&#xff0c;类似Linux系统的yum。Helm Chart 是用来封装 Kubernetes 原生应用程序的一系列 YAML 文件。可以在你部署应用的时候自定义应用程序的一些 Metadata&#xff0c;以便于应用程序的分发。 he…

keil移植linux(makefile)

文章目录 运行环境&#xff1a;1.1 freeRTOS_LED工程移植1)修改cubeMX配置2)setting设置3)launch设置4)修改makefile5)修改代码6)实验效果 运行环境&#xff1a; ubuntu18.04.melodic 宏基暗影骑士笔记本 stm32f427IIH6 stlink 9-24v可调电源 robomaster A 板 1.1 freeRTOS_L…

第二章——进程与线程(上)

上船不思岸上人&#xff0c;下船不提船上事 文章目录 2.1.1 进程的概念&#xff0c;组成&#xff0c;特征知识总览进程的概念进程的组成——PCB程序是如何运行的进程的组成进程的特征知识回顾 2.1.2 进程的状态与转换&#xff0c;进程的组织知识总览创建态&#xff0c;就绪态运…

yolov5 8系列 labelme数据标注 并生成训练数据集

yolov5 8系列 labelme数据标注 数据集生成终极教程 一.数据集准备二.转换为yolo 数据集 一.数据集准备 创建一个data 文件夹在data文件夹下创建一个images 文件夹将所有图片数据放入images文件夹下 使用labelme标注数据 python环境下使用 pip install labelme 安装labelme在c…