ASP.NET Core中使用EasyCaching作为缓存抽象层
⒈是什么?
和CacheManager差不多,两者的定位和功能都差不多。
EasyCaching主要提供了下面的几个功能
- 统一的抽象缓存接口
- 多种常用的缓存Provider(InMemory,Redis,Memcached,SQLite)
- 为分布式缓存的数据序列化提供了多种选择
- 二级缓存
- 缓存的AOP操作(able, put,evict)
- 多实例支持
- 支持Diagnostics
- Redis的特殊Provider
⒉示例(以InMemory为例)
1.安装Nuget包
EasyCaching.InMemory
2.在Startup中配置服务及请求管道
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyCaching.Core;
using EasyCaching.InMemory;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; namespace Coreqi.EasyCaching
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddEasyCaching(option =>
{
// 使用InMemory最简单的配置
option.UseInMemory("default"); //// 使用InMemory自定义的配置
//option.UseInMemory(options =>
//{
// // DBConfig这个是每种Provider的特有配置
// options.DBConfig = new InMemoryCachingOptions
// {
// // InMemory的过期扫描频率,默认值是60秒
// ExpirationScanFrequency = 60,
// // InMemory的最大缓存数量, 默认值是10000
// SizeLimit = 100
// };
// // 预防缓存在同一时间全部失效,可以为每个key的过期时间添加一个随机的秒数,默认值是120秒
// options.MaxRdSecond = 120;
// // 是否开启日志,默认值是false
// options.EnableLogging = false;
// // 互斥锁的存活时间, 默认值是5000毫秒
// options.LockMs = 5000;
// // 没有获取到互斥锁时的休眠时间,默认值是300毫秒
// options.SleepMs = 300;
// }, "m2"); //// 读取配置文件
//option.UseInMemory(Configuration, "m3");
}); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseStaticFiles();
app.UseCookiePolicy(); // 如果使用的是Memcached或SQLite,还需要下面这个做一些初始化的操作
app.UseEasyCaching(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
3.创建一个实体类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace Coreqi.EasyCaching.Models
{
[Serializable]
public class User
{
public int id { get; set; }
public string username { get; set; }
public string password { get; set; }
public int enabled { get; set; }
}
}
4.模拟一个服务层
using Coreqi.EasyCaching.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace Coreqi.EasyCaching.Services
{
public class UserService:IUserService
{
public static IList<User> users;
public UserService()
{
users = new List<User>
{
new User{ id = ,username = "fanqi",password = "admin",enabled = },
new User{ id = ,username = "gaoxing",password = "admin",enabled = }
};
}
public IList<User> getAll()
{
return users;
}
public User getById(int id)
{
return users.FirstOrDefault(f => f.id == id);
}
public User add(User user)
{
users.Add(user);
return user;
}
public User modify(User user)
{
delete(user.id);
add(user);
return user;
}
public void delete(int id)
{
users.Remove(getById(id));
}
}
}
5.控制器中使用缓存
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Coreqi.EasyCaching.Models;
using Coreqi.EasyCaching.Services;
using EasyCaching.Core;
using Microsoft.AspNetCore.Mvc; namespace Coreqi.EasyCaching.Controllers
{
[Route("api/[controller]")]
public class UserController : Controller
{
private readonly IEasyCachingProvider _cache;
private readonly IUserService _service;
public UserController(IEasyCachingProvider cache, IUserService service)
{
this._cache = cache;
this._service = service;
} [HttpGet]
[Route("add")]
public async Task<IActionResult> Add()
{
IList<User> users = _service.getAll();
_cache.Set("users", users, TimeSpan.FromMinutes());
await _cache.SetAsync("users2", users, TimeSpan.FromMinutes());
return await Task.FromResult(new JsonResult(new { message = "添加成功!" }));
} [HttpGet]
[Route("remove")]
public async Task<IActionResult> Remove()
{
_cache.Remove("users");
await _cache.RemoveAsync("users2");
return await Task.FromResult(new JsonResult(new { message = "删除成功!" }));
} [HttpGet]
[Route("get")]
public async Task<IActionResult> Get()
{
var users = _cache.Get<List<User>>("users");
var users2 = await _cache.GetAsync<List<User>>("users2");
return await Task.FromResult(new JsonResult(new { users1 = users.Value,users2 = users2.Value }));
}
}
}
⒊改造为Redis示例
1.安装Nuget包
EasyCaching.Redis
2.在Startup中配置服务及请求管道
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IUserService, UserService>();
services.AddEasyCaching(option =>
{
//使用redis
option.UseRedis(config =>
{
config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", ));
}, "localhostRedis");
}); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
3.启动程序
然后就发现数据缓存到Redis中了,不过,
对于序列化,一般都会有一个基于BinaryFormatter的默认实现,因为这个并不依赖于第三方类库,如果我们没有指定其它的,EasyCaching就会使用这个去进行数据的序列化,除了这个默认的实现,EasyCaching还提供了三种额外的选择。Newtonsoft.Json,MessagePack和Protobuf。切换方法如下:
1.安装以下Nuget包(你用那个序列化就装那个)
EasyCaching.Serialization.Json
EasyCaching.Serialization.MessagePack
EasyCaching.Serialization.Protobuf
2.配置序列化方式
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IUserService, UserService>();
services.AddEasyCaching(option =>
{
//使用redis
option.UseRedis(config =>
{
config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", ));
}, "localhostRedis")
//.WithMessagePack(); //使用MessagePack替换BinaryFormatter
//.WithProtobuf(); //使用Protobuf替换BinaryFormatter
.WithJson(); //使用Newtonsoft.Json替换BinaryFormatter
}); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
⒋多示例支持
多实例指在同一个项目中同时使用多个缓存提供者,包括多个同一类型的缓存提供者或着是不同类型的缓存提供者。
在代码中借助IEasyCachingProviderFactory
来指定使用那个缓存提供者。
1.先添加两个缓存提供者
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IUserService, UserService>();
services.AddEasyCaching(option =>
{
option.UseInMemory("m1"); //配置一个InMemory,名称为m1
//使用redis
option.UseRedis(config =>
{
config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", ));
}, "localhostRedis")
//.WithMessagePack(); //使用MessagePack替换BinaryFormatter
//.WithProtobuf(); //使用Protobuf替换BinaryFormatter
.WithJson(); //使用Newtonsoft.Json替换BinaryFormatter
}); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
2.在代码中使用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Coreqi.EasyCaching.Models;
using Coreqi.EasyCaching.Services;
using EasyCaching.Core;
using Microsoft.AspNetCore.Mvc; namespace Coreqi.EasyCaching.Controllers
{
[Route("api/[controller]")]
public class LoginController : Controller
{
private readonly IEasyCachingProviderFactory _cacheFactory;
private readonly IUserService _userService;
public LoginController(IEasyCachingProviderFactory cacheFactory, IUserService userService)
{
this._cacheFactory = cacheFactory;
this._userService = userService;
} [HttpGet]
[Route("add")]
public async Task<IActionResult> Add()
{
var _cache1 = _cacheFactory.GetCachingProvider("m1"); //获取名字为m1的provider
var _cache2 = _cacheFactory.GetCachingProvider("localhostRedis"); //获取名字为localhostRedis的provider
IList<User> users = _userService.getAll();
IList<string> loginNames = users.Select(s => s.username).ToList();
_cache1.Set("loginNames", loginNames, TimeSpan.FromMinutes());
await _cache2.SetAsync("users", users, TimeSpan.FromMinutes());
return await Task.FromResult(new JsonResult(new { message = "添加成功!" }));
} [HttpGet]
[Route("get")]
public async Task<IActionResult> Get()
{
var _cache1 = _cacheFactory.GetCachingProvider("m1"); //获取名字为m1的provider
var _cache2 = _cacheFactory.GetCachingProvider("localhostRedis"); //获取名字为localhostRedis的provider
IList<string> loginNames = _cache1.Get<List<string>>("loginNames").Value;
IList<User> users = (await _cache2.GetAsync<List<User>>("users")).Value;
return await Task.FromResult(new JsonResult(new { loginNames = loginNames,users = users}));
}
}
}
⒌缓存的Aop操作
一句话,提供和Java Spring中的@Cacheable、@CacheEvict、@CachePut等注解类似的Aop操作
例如,我们以前的查询代码一般是这样的
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Coreqi.EasyCaching.Models;
using Coreqi.EasyCaching.Services;
using EasyCaching.Core;
using Microsoft.AspNetCore.Mvc; namespace Coreqi.EasyCaching.Controllers
{
[Route("api/[controller]")]
public class CacheAopController : Controller
{
private readonly IEasyCachingProvider _cache;
private readonly IUserService _service;
public CacheAopController(IEasyCachingProvider cache, IUserService service)
{
this._cache = cache;
this._service = service;
} [HttpGet]
[Route("get/{id}")]
public async Task<User> GetUserByIdAsync(int id)
{
string cacheKey = $"user:{id}";
var userInfo = await _cache.GetAsync<User>(cacheKey);
if (userInfo.HasValue)
{
return userInfo.Value;
}
var user = _service.getById(id);
if (user != null)
{
_cache.Set<User>(cacheKey, user, TimeSpan.FromHours());
}
return user;
}
}
}
先去查询缓存数据,没有的话再去查库然后保存到缓存中,一个查询是这样,那么多的CRUD都这样岂不是要写到吐?
而我们使用EasyCaching的缓存AOP来简化这一操作。
1.Nuget包
EasyCaching.Interceptor.AspectCore
2.在接口的定义上添加一个Attribute标识。
using Coreqi.EasyCaching.Models;
using EasyCaching.Core.Interceptor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace Coreqi.EasyCaching.Services
{
public interface IUserService
{
IList<User> getAll(); [EasyCachingAble(Expiration =)]
User getById(int id); User add(User user); User modify(User user); void delete(int id);
}
}
3.配置服务使其生效
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IUserService, UserService>(); //必须将Service添加到IOC中,否则AOP不成功
services.AddEasyCaching(option =>
{
option.UseInMemory("m1"); //配置一个InMemory,名称为m1
//使用redis
option.UseRedis(config =>
{
config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", ));
}, "localhostRedis")
//.WithMessagePack(); //使用MessagePack替换BinaryFormatter
//.WithProtobuf(); //使用Protobuf替换BinaryFormatter
.WithJson(); //使用Newtonsoft.Json替换BinaryFormatter
}); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); return services.ConfigureAspectCoreInterceptor(options =>
{
options.CacheProviderName = "localhostRedis"; //指定要使用的缓存提供者名称
});
}
*也可以在Attribute特性上指定
using Coreqi.EasyCaching.Models;
using EasyCaching.Core.Interceptor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace Coreqi.EasyCaching.Services
{
public interface IUserService
{
IList<User> getAll(); [EasyCachingAble(Expiration =,CacheProviderName ="m1")]
User getById(int id); User add(User user); User modify(User user); void delete(int id);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Coreqi.EasyCaching.Models;
using Coreqi.EasyCaching.Services;
using EasyCaching.Core;
using Microsoft.AspNetCore.Mvc; namespace Coreqi.EasyCaching.Controllers
{
[Route("api/[controller]")]
public class CacheAopController : Controller
{
private readonly IEasyCachingProvider _cache;
private readonly IUserService _service;
public CacheAopController(IEasyCachingProvider cache, IUserService service)
{
this._cache = cache;
this._service = service;
} [HttpGet]
[Route("get/{id}")]
public User GetUserById(int id)
{
return _service.getById(id);
}
}
}
完成上面的操作后可以在调用方法的时候优先取缓存,没有缓存的时候才会去执行方法。
EasyCaching提供的AOP Attritebute有一些通用的属性。
配置名 | 说明 |
---|---|
CacheKeyPrefix | 指定生成缓存键的前缀,正常情况下是用在修改和删除的缓存上 |
CacheProviderName | 可以指定特殊的provider名字 |
IsHightAvailability | 缓存相关操作出现异常时,是否还能继续执行业务方法 |
EasyCachingAble和EasyCachingPut还有一个同名和配置。
配置名 | 说明 |
---|---|
Expiration | key的过期时间,单位是秒 |
EasyCachingEvict有两个特殊的配置。
配置名 | 说明 |
---|---|
IsAll | 这个要搭配CacheKeyPrefix来用,就是删除这个前缀的所有key |
IsBefore | 在业务方法执行之前删除缓存还是执行之后 |
⒍支持Diagnostics【诊断系统】
提供了Diagnostics的支持方便接入第三方的APM,实现追踪。
有一个接入Jaeger的一个案例。大家可以去看看。
⒎二级缓存
二级缓存,多级缓存,其实在缓存的小世界中还算是一个比较重要的东西!
一个最为头疼的问题就是不同级的缓存如何做到近似实时的同步。
在EasyCaching中,二级缓存的实现逻辑大致就是下面的这张图。
如果某个服务器上面的本地缓存被修改了,就会通过缓存总线去通知其他服务器把对应的本地缓存移除掉。
1.添加Nuget包。
EasyCaching.InMemory
EasyCaching.Redis
EasyCaching.HybridCache
EasyCaching.Bus.Redis
2.添加配置
services.AddEasyCaching(option =>
{
option.UseInMemory("m1"); //配置一个InMemory,名称为m1
//使用redis
option.UseRedis(config =>
{
config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", ));
config.DBConfig.Database = ;
}, "localhostRedis")
//.WithMessagePack(); //使用MessagePack替换BinaryFormatter
//.WithProtobuf(); //使用Protobuf替换BinaryFormatter
.WithJson(); //使用Newtonsoft.Json替换BinaryFormatter //使用Hybird
option.UseHybrid(config =>
{
config.EnableLogging = false; //是否开启日志
config.TopicName = "test_topic"; //缓存总线的订阅主题
config.LocalCacheProviderName = "m1"; //本地缓存的名字
config.DistributedCacheProviderName = "localhostRedis"; //分布式缓存的名字
}); //使用redis作为缓存总线
option.WithRedisBus(config =>
{
config.Endpoints.Add(new ServerEndPoint("127.0.0.1", ));
config.Database = ;
}); //使用CSRedis作为缓存总线
//option.WithCSRedisBus(config =>
//{
// config.ConnectionStrings = new List<string>
// {
// "127.0.0.1:6379,defaultDatabase=6,poolsize=10"
// };
//}); //使用RabbitMq作为缓存总线
//option.WithRabbitMQBus(config =>
//{
// config = new RabbitMQBusOptions();
//});
});
3.使用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyCaching.Core;
using Microsoft.AspNetCore.Mvc; namespace Coreqi.EasyCaching.Controllers
{
[Route("api/[controller]")]
public class HybridCachingController : Controller
{
private readonly IHybridCachingProvider _provider;
public HybridCachingController(IHybridCachingProvider provider)
{
this._provider = provider;
} [HttpGet]
[Route("add")]
public string Add()
{
string cacheKey = "coreqiTest";
_provider.Set(cacheKey, "", TimeSpan.FromSeconds());
return "添加成功!";
}
}
}
⒏特殊的Redis缓存提供者
Redis支持多种数据结构,还有一些原子递增递减的操作等等。为了支持这些操作,EasyCaching提供了一个独立的接口,IRedisCachingProvider。
这个接口,目前也只支持了百分之六七十常用的一些操作,还有一些可能用的少的就没加进去。
同样的,这个接口也是支持多实例的,也可以通过IEasyCachingProviderFactory
来获取不同的provider实例。
在注入的时候,不需要额外的操作,和添加Redis是一样的。不同的是,在使用的时候,不再是用IEasyCachingProvider
,而是要用IRedisCachingProvider
。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyCaching.Core;
using Microsoft.AspNetCore.Mvc; namespace Coreqi.EasyCaching.Controllers
{
[Route("api/[controller]")]
public class MultiRedisController : Controller
{
private readonly IRedisCachingProvider _redis1;
private readonly IRedisCachingProvider _redis2; public MultiRedisController(IEasyCachingProviderFactory factory)
{
this._redis1 = factory.GetRedisProvider("redis1");
this._redis2 = factory.GetRedisProvider("redis2");
} [HttpGet]
[Route("get")]
public string Get()
{
_redis1.StringSet("keyredis1", "val"); var res1 = _redis1.StringGet("keyredis1");
var res2 = _redis2.StringGet("keyredis1"); return $"redis1 cached value: {res1}, redis2 cached value : {res2}";
}
}
}
⒐EasyCaching扩展性功能
除了这些基础功能,还有一些扩展性的功能,在这里要非常感谢yrinleung,他把EasyCaching和WebApiClient,CAP等项目结合起来了。感兴趣的可以看看这个项目EasyCaching.Extensions。
参考文章:一篇短文带您了解一下EasyCaching-----作者@Catcher ( 黄文清 )
ASP.NET Core中使用EasyCaching作为缓存抽象层的更多相关文章
- ASP.NET Core 中的缓存
目录 缓存的基本概念 缓存原理 缓存设计 分布式缓存 Memcache 与 Redis 的比较 缓存穿透,缓存击穿,缓存雪崩解决方案 数据一致性 使用内置 MemoryCache 使用分布式缓存 Re ...
- ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存
分享 最近在公司成功落地了一个用ASP.NET Core 开发前台的CMS项目,虽然对于表层的开发是兼容MVC5的,但是作为爱好者当然要用尽量多的ASP.NET Core新功能了. 背景 在项目开发的 ...
- C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入
C# 嵌入dll 在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...
- ASP.NET Core中使用Cache缓存
ASP.NET Core中使用Cache缓存 缓存介绍: 通过减少生成内容所需的工作,缓存可以显著提高应用的性能和可伸缩性. 缓存对不经常更改的数据效果最佳. 缓存生成的数据副本的返回速度可以比从原始 ...
- ASP.NET Core中的Http缓存
ASP.NET Core中的Http缓存 Http响应缓存可减少客户端或代理对web服务器发出的请求数.响应缓存还减少了web服务器生成响应所需的工作量.响应缓存由Http请求中的header控制. ...
- ASP.NET Core教程:ASP.NET Core中使用Redis缓存
参考网址:https://www.cnblogs.com/dotnet261010/p/12033624.html 一.前言 我们这里以StackExchange.Redis为例,讲解如何在ASP.N ...
- 在ASP.NET Core中使用百度在线编辑器UEditor
在ASP.NET Core中使用百度在线编辑器UEditor 0x00 起因 最近需要一个在线编辑器,之前听人说过百度的UEditor不错,去官网下了一个.不过服务端只有ASP.NET版的,如果是为了 ...
- ASP.NET Core 中文文档 第三章 原理(13)管理应用程序状态
原文:Managing Application State 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:高嵩 在 ASP.NET Core 中,有多种途径可以对应用程序的状态进行 ...
- ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】
本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...
随机推荐
- codevs 1094 FBI树 2004年NOIP全国联赛普及组 x
题目描述 Description 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串. ...
- Java集合框架之接口Iterator
简述 Iterator迭代器的定义:迭代器(Iterator)模式,又叫做游标(Cursor)模式.GOF给出的定义是,提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象 ...
- python中的定时器threading.Timer
由浅入深学SQL Server 2012 --> python开发中用到,定时操作.例如每隔1s执行一次,发现 threading.Timer,这个东西,可以直接用. 其原理为执行函数中置定时 ...
- HearthBuddy卡牌无法识别
界面上无法识别,提示是 [Unidentified card ID :DAL_010][Unidentified card ID :DAL_415] Unidentified card ID :HER ...
- ambari部署Hadoop集群(2)
准备本地 repository 1. 下载下面的包 wget http://public-repo-1.hortonworks.com/ambari/centos7/2.x/updates/2.7.3 ...
- 转:extjs 添加loading状态的三种解决办法:
extjs 添加loading状态的三种解决办法: 方法一: //materialGrid 指需要显示loading状态的控件id var o=Ext.getCmp('materialGrid'); ...
- IDEA工具上传项目报:Push rejected: Push to origin/master was rejected
原文:https://blog.csdn.net/a137151062/article/details/78820806 解决方案如下: 1.切换到自己项目所在的目录,右键选择GIT BASH Her ...
- linux常用命令(21)tar命令
通过SSH访问服务器,难免会要用到压缩,解压缩,打包,解包等,这时候tar命令就是是必不可少的一个功能强大的工具.linux中最流行的tar是麻雀虽小,五脏俱全,功能强大. tar命令可以为linux ...
- Python基于回溯法解决01背包问题实例
Python基于回溯法解决01背包问题实例 这篇文章主要介绍了Python基于回溯法解决01背包问题,结合实例形式分析了Python回溯法采用深度优先策略搜索解决01背包问题的相关操作技巧,需要的朋友 ...
- CentOS下安装完php外网无法访问的问题
1. cd /etc/selinux/ vim config SELINUX=disabled 2.通过界面关闭防火墙