ps:废话不多说。直接上代码:源码地址:
Configuration的配置
说明:基于三种方式的读取配置文件以及自定义读取自定义配置文件
文件结构
代码
PropertiesConfigurationProvider.cs
public class PropertiesConfigurationProvider:ConfigurationProvider{ string _path = string.Empty; public PropertiesConfigurationProvider(string path) { this._path = path; } ////// 用于解析1.properties /// public override void Load() { var lines = File.ReadAllLines(this._path); var dict = new Dictionary(); foreach (var line in lines) { var items = line.Split('='); dict.Add(items[0], items[1]); } this.Data = dict; }}
PropertiesConfigurationSource.cs
public class PropertiesConfigurationSource : IConfigurationSource{ string _path = string.Empty; public PropertiesConfigurationSource(string path) { this._path = path; } public IConfigurationProvider Build(IConfigurationBuilder builder) { return new PropertiesConfigurationProvider(_path); }}
PropertiesConfigurationExtensions.cs
public static class PropertiesConfigurationExtensions{ public static IConfigurationBuilder AddPropertiesFile(this IConfigurationBuilder builder, string path) { builder.Add(new PropertiesConfigurationSource(path)); return builder; }}
1.properties
port=8080host=127.0.0.1
appsettings.json
{ "mysql": { "host": "127.0.0.1", "port": 3306 }, "shopidlist": [ { "entid": 20 }, { "entid": 25 } ]}
Program.cs
class Program{ static void Main(string[] args) { IConfiguration Configuration = new ConfigurationBuilder() .SetBasePath(Environment.CurrentDirectory) .AddJsonFile("appsettings.json", optional:true,reloadOnChange:true) .AddPropertiesFile("1.properties")//加载自定义文件 .Build(); Appsettings appsettings = new Appsettings(); Configuration.Bind(appsettings); Console.WriteLine(appsettings.mysql.port); Console.WriteLine(Configuration["mysql:port"]); Console.WriteLine(Configuration.GetValue ("mysql:port")); Console.WriteLine(Configuration.GetSection("mysql").GetSection("port").Value); //读取自定义文件 Console.WriteLine(Configuration["host"]); Console.Read(); }}public class Appsettings{ public Mysql mysql { get; set; } public Shopidlist[] shopidlist { get; set; }}public class Mysql{ public string host { get; set; } public int port { get; set; }}public class Shopidlist{ public int entid { get; set; }}
CollectionService注入
文件结构
代码
Program.cs
class Program{ static void Main(string[] args) { //IOC容器 ServiceCollection services = new ServiceCollection(); //注册服务 services.AddScoped(); //注册日志服务 services.AddLogging(); var provider = services.BuildServiceProvider(); provider.GetService ().AddConsole(LogLevel.Debug); var fly = provider.GetService (); fly.Flay(); }}public interface IFlay{ void Flay();}public class Pig : IFlay{ ILogger logger = null; public Pig(ILoggerFactory loggerFactory) { logger=loggerFactory.CreateLogger (); } public void Flay() { logger.LogInformation("这是Console日志"); Console.WriteLine("风口来了,猪都会飞了!"); }}
AOP AspectCore
文件结构
代码
Program.cs
//https://github.com/dotnetcore/AspectCore-Frameworkclass Program{ static void Main(string[] args) { ServiceCollection services = new ServiceCollection(); //注册AspectCore动态代理服务 services.AddDynamicProxy(); services.AddTransient(); var provider= services.BuildDynamicProxyServiceProvider(); var mysql = provider.GetService (); //走业务逻辑 var msg= mysql.GetData(10); Console.WriteLine(msg); //走缓存 msg = mysql.GetData(10); Console.WriteLine(msg); }}public class MysqlInterceptorAttribute : AbstractInterceptorAttribute{ private Dictionary cacheDict = new Dictionary (); public override Task Invoke(AspectContext context, AspectDelegate next) { //用id作为cachekey var cacheKey = string.Join(",", context.Parameters); if (cacheDict.ContainsKey(cacheKey)) { context.ReturnValue = cacheDict[cacheKey]; return Task.CompletedTask; } var task = next(context); //ReturnValue实际上就是一个传递值的媒介 var cacheValue = context.ReturnValue; cacheDict.Add(cacheKey, $"from Cache:{cacheValue.ToString()}"); return task; //Console.WriteLine("开始记录日志。。。。"); //var task = next(context); //Console.WriteLine("结束记录日志。。。。"); //return task; }}public interface IMySql{ [MysqlInterceptor] string GetData(int id);}public class MySql : IMySql{ public string GetData(int id) { var msg = $"已经获取到id={id}的数据"; //Console.WriteLine(msg); return msg; }}
MemoryCache
文件结构
代码
Program.cs
class Program{ static void Main(string[] args) { { //cache的最大限度为100 MemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions() { SizeLimit = 100 }); for (int i = 0; i < 1000; i++) { memoryCache.Set(i.ToString(), i.ToString(), new MemoryCacheEntryOptions() { Size = 1 }); Console.WriteLine(memoryCache.Count); } } { //被动过期,设置过期回调 MemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions() { }); var cacheOptions = new MemoryCacheEntryOptions { AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(3),//3秒过期 }; cacheOptions.RegisterPostEvictionCallback((key, value, reason, obj) => { Console.WriteLine(reason); Console.WriteLine("执行过期回调"); }); memoryCache.Set("key", "value", cacheOptions); Console.WriteLine("3秒后过期将执行回调"); while (true) { Thread.Sleep(1000); Console.WriteLine(memoryCache.Get ("key")); } Console.ReadKey(); } { //主动过期(10秒被动过期,2秒主动使用token让他过期) CancellationTokenSource tokenSource = new CancellationTokenSource(); MemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions() { }); var cacheOptions = new MemoryCacheEntryOptions { AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10),//10秒过期 }; cacheOptions.AddExpirationToken(new CancellationChangeToken(tokenSource.Token));//加入Token cacheOptions.RegisterPostEvictionCallback((key, value, reason, obj) => { Console.WriteLine(reason); Console.WriteLine("执行过期回调"); }); memoryCache.Set("key", "value", cacheOptions); Console.WriteLine("10秒后过期将执行回调"); tokenSource.CancelAfter(TimeSpan.FromSeconds(2)); while (true) { Thread.Sleep(1000); Console.WriteLine(memoryCache.Get ("key")); } Console.ReadKey(); } }}
Redis MongoDB
文件结构
代码
Program.cs
class Program{ static void Main(string[] args) { //Redis { RedisCache redisCache = new RedisCache(new RedisCacheOptions() { Configuration = "127.0.0.1:6379", InstanceName = "test" }); //在Redis中是以Hash的模式存放的 redisCache.SetString("username", "jack", new DistributedCacheEntryOptions { AbsoluteExpiration = DateTime.Now.AddDays(1), }); var info = redisCache.GetString("username"); Console.WriteLine(info); } //MongoDB { MongoDBCache mongoDBCache = new MongoDBCache(new MongoDBCacheOptions() { ConnectionString = "mongodb://127.0.0.1:27017", DatabaseName = "mydb", CollectionName = "mytest" }); mongoDBCache.Set("username", Encoding.UTF8.GetBytes("jack"), new DistributedCacheEntryOptions { AbsoluteExpiration = DateTime.Now.AddDays(1) }); var info= Encoding.UTF8.GetString(mongoDBCache.Get("username")); Console.WriteLine(info); } }}
Mysql.Data Dapper
文件结构
代码
class Program{ static void Main(string[] args) { //MySql.Data { var connectString = "server=127.0.0.1;por=3306;userid=root;pwd=123456;database=datamip;SslMode=none;"; //MySqlConnection mySqlConnection = new MySqlConnection(connectString); //select var ds = MySqlHelper.ExecuteDataset(connectString, "select * from users"); //select2 var reader = MySqlHelper.ExecuteReader(connectString, "select * from users"); var user = new Users(); while (reader.Read()) { user.UserId = reader.GetInt32("UserID"); user.UserNick = reader.GetString("UserNick"); user.LoginIP = reader.GetString("LoginIP"); user.Email = reader.GetString("Email"); } reader.Close(); //update var result = MySqlHelper.ExecuteNonQuery(connectString, "update users set Email='786744873@qq.com' where UserID=1"); } //Dapper { var connectString = "server=127.0.0.1;por=3306;userid=root;pwd=123456;database=datamip;SslMode=none;"; MySqlConnection mySqlConnection = new MySqlConnection(connectString); //select var userList = mySqlConnection.Query("select * from users where UserID=@UserID",new { UserID=2 }); //update var nums = mySqlConnection.Execute("update users set Email='786744873@qq.com' where UserID=1"); } } class Users { public int UserId { get; set; } public string UserNick { get; set; } public string LoginIP { get; set; } public string Email { get; set; } }}
nohup supervisord部署Unbuntu
不得不说Ubuntu18.04版本是个巨坑。还是老老实实用16.04吧,错了,是VirtualBox是个巨坑,这wtf软件各种问题,浪费我几天时间,艹
文件结构
代码
ConfigurationManager.cs
public class ConfigurationManager{ public static IConfigurationRoot Configuration { get { var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile(string.Format("appsettings.{0}.json", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")),optional:true,reloadOnChange:true) .AddJsonFile(string.Format("ops.{0}.json", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")),optional:true,reloadOnChange:true) .Build(); return configuration; } }}
Program.cs
class Program{ static void Main(string[] args) { var tokenSource = new CancellationTokenSource(); Task.Factory.StartNew(() => { while (!tokenSource.IsCancellationRequested) { Console.WriteLine($"{DateTime.Now}:业务逻辑处理中"); Thread.Sleep(1000); } }).ContinueWith(t => { Console.WriteLine("服务安全退出!"); Environment.Exit(0);//强制退出 }); Console.WriteLine("服务成功开启"); while (!"Y".Equals(ConfigurationManager.Configuration["isquit"],StringComparison.InvariantCultureIgnoreCase)) { Thread.Sleep(1000); } tokenSource.Cancel(); }}
ops.Development.json || ops.Production.json
{ "isquit": "N"}
部署过程
直接发布,发布以及虚拟机安装参照之前的博客:,这里主要讲如何进行持久化
ubuntu16.04开机提示:检测到系统程序出现问题
打开终端,输入 sudo gedit /etc/default/apport 把enabled=1改成enabled=0
方案一:nohub部署
先执行 nohup dotnet 程序集名称 &
nohup dotnet ConsoleApp7.dll & #(符号&使程序在后台运行) exit #(退出nohup模式)
然后输入 exit 进行退出,不然的话后台进程会被终结
启动后,会将程序运行输出记录在当前目录下的nohup.out文件下,如果当前目录不可写,则会被记录在Home目录下的nohup.out文件中
nohup dotnet ConsoleApp7.dll > my_nohup.log 2>&1 & #(将日志输出在my_nohup.log文件中,并将stderr重定向至stdout)
查看dotnet进程
ps -ef |grep dotnet
查看后10行nohup输出
tail /home/wyt/nohup.out
查看运行的后台进程
ps -ef | grep dotnet
停止程序
kill -9 12462 #(根据进程号关闭程序)
方案二:supervisord部署
安装
ubuntu安装: sudo apt-get install supervisor centos安装: yum install -y supervisor
安装成功后,会在 /etc/supervisor 目录下,生成 supervisord.conf 配置文件。
你也可以使用 echo_supervisord_conf > supervisord.conf 命令,生成默认的配置文件(不建议,内容比较多)。
supervisord.conf 示例配置:
; supervisor config file[unix_http_server]file=/var/run/supervisor.sock ; (the path to the socket file)chmod=0700 ; sockef file mode (default 0700)[supervisord]logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP); the below section must remain in the config file for RPC; (supervisorctl/web interface) to work, additional interfaces may be; added by defining them in separate rpcinterface: sections[rpcinterface:supervisor]supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface[supervisorctl]serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket; The [include] section can just contain the "files" setting. This; setting can list multiple files (separated by whitespace or; newlines). It can also contain wildcards. The filenames are; interpreted as relative to this file. Included files *cannot*; include files themselves.[include]files = /etc/supervisor/conf.d/*.conf
进程配置会读取 /etc/supervisor/conf.d 目录下的 *.conf 配置文件,我们在此目录下创建一个 ConsoleApp7.conf 进程配置文件:
[program:ConsoleApp7]directory=/data/outputcommand=/usr/bin/dotnet /data/output/ConsoleApp7.dllautostart=trueautorestart=truestartretries=10redirect_stderr=truestdout_logfile=/data/output/logs/out.logstderr_logfile=/data/output/logs/out.logenvironment=ASPNETCORE_ENVIRONMENT="Development"
需要注意的是,如果不是 root 账号,需要对这些目录进行权限设置,要不然会报一些错误(一定要在 root 账号下进行配置,要不然一系列权限引起的问题?)。
sudo chmod 777 /var/runsudo chmod 777 /etc/supervisor
接着就可以启动 Supervisord 了:
sudo supervisorctl reload sudo supervisord sudo supervisorctl ConsoleApp7
supervisorctl 常用命令:
命令 | 说明 |
---|---|
sudo supervisorctl stop program_name | 停止某个进程 |
sudo supervisorctl start program_name | 启动某个进程 |
sudo supervisorctl restart program_name | 重启某个进程 |
sudo supervisorctl stop all | 停止全部进程 |
sudo supervisorctl reload | 载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程 |
sudo supervisorctl update | 根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启 |
运行成功后查看守护进程信息
sudo supervisorctl status all
使用NSSM进行Windows部署
是一款可将Nodejs项目注册为Windows系统服务的工具。当你的Node.js项目需要部署在Windows Server上时,NSSM是一个不错的选择。
使用
- 下载NSSM .
- 根据自己的平台,将32/64位nssm.exe文件解压至任意文件夹。
- cmd定位至nssm.exe所在目录。
- 输入
nssm install {服务名称}
,即注册服务的名称。注册服务弹出如下NSSM界面。
Socket实现简单Web服务器
文件结构
代码
WebServer.cs
1 public class WebServer 2 { 3 static Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 4 5 public static void Start() 6 { 7 socket.Bind(new IPEndPoint(IPAddress.Any, 8200)); 8 socket.Listen(100); 9 10 //接收客户端的Socket请求11 socket.BeginAccept(OnAccept, socket);12 13 Console.WriteLine("当前Web服务器启动成功,端口号为8200");14 }15 16 //接收请求17 public static void OnAccept(IAsyncResult async)18 {19 var serverSocket = async.AsyncState as Socket;20 21 //获取到客户端的Socket22 var clientSocket = serverSocket.EndAccept(async);23 24 //进行下一步监听25 serverSocket.BeginAccept(OnAccept, socket);26 27 //获取其内容28 var bytes = new byte[10240];29 var len = clientSocket.Receive(bytes);30 var request = Encoding.UTF8.GetString(bytes, 0, len);31 32 var response = string.Empty;33 34 if (!string.IsNullOrEmpty(request)&&!request.Contains("favicon.ico"))35 {36 //1.html37 var filePath = request.Split("\r\n")[0].Split(" ")[1].Replace("/", "");38 39 //获取文件内容40 response = File.ReadAllText(filePath, Encoding.UTF8);41 }42 43 //按照Http响应码返回44 var responseHeader = string.Format(@"HTTP/1.1 200 OK45 Date: Tue, 11 Dec 2018 07:08:44 GMT46 Content-Type: text/html; charset=utf-847 Content-Length: {0}48 Connection: keep-alive49 Vary: Accept-Encoding50 Cache-Control: private, max-age=1051 Expires: Tue, 11 Dec 2018 07:08:54 GMT52 Last-Modified: Tue, 11 Dec 2018 07:08:44 GMT53 X-UA-Compatible: IE=1054 X-Frame-Options: SAMEORIGIN55 56 ", Encoding.UTF8.GetByteCount(response));57 58 //返回给客户端59 clientSocket.Send(Encoding.UTF8.GetBytes(responseHeader));60 clientSocket.Send(Encoding.UTF8.GetBytes(response));61 62 clientSocket.Close(); 63 }64 65 }
Program.cs
class Program{ static void Main(string[] args) { //浏览器访问127.0.0.1:8200/1.html WebServer.Start(); Console.ReadKey(); }}
1.html
这是1.html的内容
2.html
这是2.html的内容
效果:
自定义Middleware
文件结构
代码
MyStaticFileMiddleware.cs
public class MyStaticFileMiddleware{ private readonly RequestDelegate _next; public MyStaticFileMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext context) { //1.logic 如果我当前的Context中有png,那么就直接从文件中读取,否则放下传递 var path = context.Request.Path.Value; if (path.Contains(".png")) { var mypath = path.Trim('/'); return context.Response.SendFileAsync(mypath); } var task = _next(context); return task; }}
Nginx反向代理
安装
命令:
sudo apt-get install nginx
因为是首次安装 Nginx,通过运行以下命令显式启动
sudo service nginx start
在浏览器地址栏中输入类似于 http://192.168.XXX.XXX
的IP地址或 http://域名
就可以显示 Nginx 的默认登陆页了。
配置 Nginx
若要将 Nginx 配置为转发请求向 ASP.NET Core 应用程序的反向代理,修改 /etc/nginx/sites-available/default 。 在文本编辑器中打开它,并将内容替换为以下内容:
server { listen 80; server_name test.com *.test.com; location / { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $http_host; proxy_cache_bypass $http_upgrade; }}
Nginx 接受主机标头使用的端口 80 上的公共流量, Nginx 将匹配的请求转发到在 Kestrel http://localhost:5000
。
sudo nginx -t
显示结果如下所示:
nginx: the configuration file /etc/nginx/nginx.conf syntax is oknginx: configuration file /etc/nginx/nginx.conf test is successful
重新加载配置:
sudo nginx -s reload
Polly
github地址:
使用
Install-Package Polly
代码
重试:RetryTest.cs
public class RetryTest{ //重试策略的设定 public static void Run() { var retrayPloicy = Policy.Handle() .WaitAndRetry(new List () { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5) }, (ex, dt) => { Console.WriteLine($"{DateTime.Now} {ex.Message} {dt} "); }); int count = 0; var html = retrayPloicy.Execute(() => { var url = "http://1cnblogs.com"; if (count == 3) url = "http://cnblogs.com"; return GetHtml(url, ref count); }); //这个是我调用第三次获取到的内容。。 Console.WriteLine(html); } /// /// 获取页面内容 /// /// ///public static string GetHtml(string url, ref int count) { var html = string.Empty; try { var webClient = new WebClient(); html = webClient.DownloadString(url); } catch (Exception ex) { count++; throw; } return html; }}
熔断:CircuitBreakderTest.cs
public class CircuitBreakderTest{ public static void Run() { //如果当前连续有两个异常,那么触发熔断,10s内不能调用,10s之后重新调用。 //一旦调用成功了,熔断就解除了。 var policy = Policy.Handle() .CircuitBreaker(2, TimeSpan.FromSeconds(10), (ex, timespan, context) => { //触发熔断 Console.WriteLine($"{DateTime.Now} 熔断触发:{timespan}"); }, (context) => { //恢复 Console.WriteLine($"{DateTime.Now} 熔断恢复"); }); var errCount = 1; for (int i = 0; i < 100; i++) { try { var msg = policy.Execute ((context, token) => { var url = "http://cnblogs1.com"; //模拟,当一定的时间后,恢复正常 if (errCount > 10) { url = "http://cnblogs.com"; } return GetHtml(url); }, new Dictionary () { { i.ToString(), i.ToString() } }, CancellationToken.None); Console.WriteLine("logic:" + msg); } catch (Exception ex) { Console.WriteLine(string.Format("{0} 业务逻辑异常:'{1}'", DateTime.Now, ex.Message)); System.Threading.Thread.Sleep(2000); errCount++; } } } /// /// 获取页面内容 /// /// ///public static string GetHtml(string url) { var html = string.Empty; try { var webClient = new WebClient(); html = webClient.DownloadString(url).Substring(0, 10); } catch (Exception ex) { throw; } return html; }}
超时:TimeoutTest.cs
public class TimeoutTest{ public static void Run() { var cancellationToken = new CancellationToken(); //10s后 cancellationToken过期 var policy = Policy.Timeout(10); //通过cancellationToken进行传值,如果10s过去,IsCancellationRequested 自动变成取消状态 policy.Execute((token) => { for (int i = 0; i < 1000; i++) { //大家需要根据这个状态来做具体的业务逻辑 Console.WriteLine(token.IsCancellationRequested); System.Threading.Thread.Sleep(1000); } }, cancellationToken); Console.Read(); } ////// 获取页面内容 /// /// ///public static string GetHtml(string url) { var html = string.Empty; Thread.Sleep(20); return html; }}
隔离:BulkheadTest.cs
public class BulkheadTest{ public static void Run() { var policy = Policy.Bulkhead(5, 1000); Parallel.For(0, 100, (i) => { //这里只能做一次调用 var result = policy.Execute(() => { return GetHtml("http://cnblogs.com"); }); Console.WriteLine("已成功获取到数据:{0}", result); }); Console.Read(); } /// /// 获取页面内容 /// /// ///public static string GetHtml(string url) { var html = string.Empty; try { var webClient = new WebClient(); html = webClient.DownloadString(url); html = html.Substring(0, 17); Console.WriteLine($"当前线程ID={Thread.CurrentThread.ManagedThreadId}"); System.Threading.Thread.Sleep(2000); } catch (Exception ex) { throw; } return html; }}
回退:FallbackTest.cs
public class FallbackTest{ public static void Run() { var result = Policy.Handle () .Fallback(() => { return "接口失败,这个fake的值给你!"; }) .Execute(() => { return GetHtml("http://1cnblogs.com"); }); Console.WriteLine(result); Console.Read(); } /// /// 获取页面内容 /// /// ///public static string GetHtml(string url) { var html = string.Empty; try { var webClient = new WebClient(); html = webClient.DownloadString(url); } catch (Exception ex) { throw; } return html; }}
c#7.0
out语法
//out{ { //old string val = "10"; int result; int.TryParse(val, out result); Console.WriteLine(result); } { //new string val = "10"; int.TryParse(val, out int result); Console.WriteLine(result); }}
Tuple/ValueTuple
public static TupleGetData(){ return Tuple.Create(true, "操作成功");}public static (bool, string) GetDataNew(){ return (true, "操作成功");}//Tuple{ var tuple = GetData(); Console.WriteLine(tuple.Item1+" "+tuple.Item2); var (isSuccess, msg) = GetDataNew(); Console.WriteLine(isSuccess.ToString(),msg);}
Deconstruct(解构函数)
public class Person{ private string _name; private int _age; public Person(string name, int age) { this._name = name; this._age = age; } public void Deconstruct(out string name, out int age) { name = this._name; age = this._age; }}//Deconstruct{ var (Name, Age) = new Person("jack", 18); Console.WriteLine(Name + " " + Age);}
Local
//Local内部方法{ Console.WriteLine(GetLength("hello world")); int GetLength(string str) { return str.Length; }}
switch增强
//switch增强{ var list = new List