首页 文章 精选 留言 我的

精选列表

搜索[micro],共862篇文章
优秀的个人博客,低调大师

(4)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Consul服务发现和消费

上一章说了 Consul服务注册 现在我要连接上Consul里面的服务 请求它们的API接口 应该怎么做呢? 1.找Consul要一台你需要的服务器 1.1 获取Consul下的所有注册的服务 using (var consulClient = new ConsulClient(c => c.Address = new Uri("http://127.0.0.1:8500"))) { var services = consulClient.Agent.Services().Result.Response; foreach(var service in services.Values) { Console.WriteLine($"id={service.ID},name={service.Service},ip={service.Address},port={service.Port}"); } } 1.2 随机取一个Name为MsgService的服务 下面的代码使用当前 TickCount 进行取模的方式达到随机获取一台服务器实例的效果,这叫做“客户端负载均衡”: using (var consulClient = new ConsulClient(c => c.Address = new Uri("http://127.0.0.1:8500"))) { var services = consulClient.Agent.Services().Result.Response.Values.Where(s => s.Service.Equals("MsgService", StringComparison.OrdinalIgnoreCase)); if(!services.Any()) { Console.WriteLine("找不到服务的实例"); } else { var service = services.ElementAt(Environment.TickCount%services.Count()); Console.WriteLine($"{service.Address}:{service.Port}"); } } 当然在一个毫秒之类会所有请求都压给一台服务器,基本就够用了。也可以自己写随机、轮询等客户端负载均衡算法,也可以自己实现按不同权重分配(注册时候 Tags 带上配置、权重等信息)等算法。 2.请求服务器的接口 你拿到了http地址 难道还不会请求接口么 找个httphelper 直接请求就好了 如果还是不会 就来群里问吧 群号:608188505 给大家上一个 我常用的httphelper 可能被我该的不像样了 不过相信大家都会用 不会的话 来群里找我吧。 1 using System; 2 using System.Collections.Generic; 3 using System.Diagnostics; 4 using System.IO; 5 using System.IO.Compression; 6 using System.Linq; 7 using System.Net; 8 using System.Net.Security; 9 using System.Runtime.InteropServices; 10 using System.Security.Cryptography.X509Certificates; 11 using System.Text; 12 using System.Text.RegularExpressions; 13 using System.Threading.Tasks; 14 15 namespace ClientApp 16 {/// <summary> 17 /// Http连接操作帮助类 18 /// </summary> 19 public class HttpHelper 20 { 21 private const int ConnectionLimit = 100; 22 //编码 23 private Encoding _encoding = Encoding.Default; 24 //浏览器类型 25 private string[] _useragents = new string[]{ 26 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36", 27 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)", 28 "Mozilla/5.0 (Windows NT 6.1; rv:36.0) Gecko/20100101 Firefox/36.0", 29 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20130401 Firefox/31.0" 30 }; 31 32 private String _useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36"; 33 //接受类型 34 private String _accept = "text/html, application/xhtml+xml, application/xml, */*"; 35 //超时时间 36 private int _timeout = 30 * 1000; 37 //类型 38 private string _contenttype = "application/x-www-form-urlencoded"; 39 //cookies 40 private String _cookies = ""; 41 //cookies 42 private CookieCollection _cookiecollection; 43 //custom heads 44 private Dictionary<string, string> _headers = new Dictionary<string, string>(); 45 46 public HttpHelper() 47 { 48 _headers.Clear(); 49 //随机一个useragent 50 _useragent = _useragents[new Random().Next(0, _useragents.Length)]; 51 //解决性能问题? 52 ServicePointManager.DefaultConnectionLimit = ConnectionLimit; 53 } 54 55 public void InitCookie() 56 { 57 _cookies = ""; 58 _cookiecollection = null; 59 _headers.Clear(); 60 } 61 62 /// <summary> 63 /// 设置当前编码 64 /// </summary> 65 /// <param name="en"></param> 66 public void SetEncoding(Encoding en) 67 { 68 _encoding = en; 69 } 70 71 /// <summary> 72 /// 设置UserAgent 73 /// </summary> 74 /// <param name="ua"></param> 75 public void SetUserAgent(String ua) 76 { 77 _useragent = ua; 78 } 79 80 public void RandUserAgent() 81 { 82 _useragent = _useragents[new Random().Next(0, _useragents.Length)]; 83 } 84 85 public void SetCookiesString(string c) 86 { 87 _cookies = c; 88 } 89 90 /// <summary> 91 /// 设置超时时间 92 /// </summary> 93 /// <param name="sec"></param> 94 public void SetTimeOut(int msec) 95 { 96 _timeout = msec; 97 } 98 99 public void SetContentType(String type) 100 { 101 _contenttype = type; 102 } 103 104 public void SetAccept(String accept) 105 { 106 _accept = accept; 107 } 108 109 /// <summary> 110 /// 添加自定义头 111 /// </summary> 112 /// <param name="key"></param> 113 /// <param name="ctx"></param> 114 public void AddHeader(String key, String ctx) 115 { 116 //_headers.Add(key,ctx); 117 _headers[key] = ctx; 118 } 119 120 /// <summary> 121 /// 清空自定义头 122 /// </summary> 123 public void ClearHeader() 124 { 125 _headers.Clear(); 126 } 127 128 /// <summary> 129 /// 获取HTTP返回的内容 130 /// </summary> 131 /// <param name="response"></param> 132 /// <returns></returns> 133 private String GetStringFromResponse(HttpWebResponse response) 134 { 135 String html = ""; 136 try 137 { 138 Stream stream = response.GetResponseStream(); 139 StreamReader sr = new StreamReader(stream, Encoding.UTF8); 140 html = sr.ReadToEnd(); 141 142 sr.Close(); 143 stream.Close(); 144 } 145 catch (Exception e) 146 { 147 Trace.WriteLine("GetStringFromResponse Error: " + e.Message); 148 } 149 150 return html; 151 } 152 153 /// <summary> 154 /// 检测证书 155 /// </summary> 156 /// <param name="sender"></param> 157 /// <param name="certificate"></param> 158 /// <param name="chain"></param> 159 /// <param name="errors"></param> 160 /// <returns></returns> 161 private bool CheckCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) 162 { 163 return true; 164 } 165 166 /// <summary> 167 /// 发送GET请求 168 /// </summary> 169 /// <param name="url"></param> 170 /// <returns></returns> 171 public String HttpGet(String url) 172 { 173 return HttpGet(url, url); 174 } 175 176 177 /// <summary> 178 /// 发送GET请求 179 /// </summary> 180 /// <param name="url"></param> 181 /// <param name="refer"></param> 182 /// <returns></returns> 183 public String HttpGet(String url, String refer) 184 { 185 String html; 186 try 187 { 188 ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckCertificate); 189 HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); 190 request.UserAgent = _useragent; 191 request.Timeout = _timeout; 192 request.ContentType = _contenttype; 193 request.Accept = _accept; 194 request.Method = "GET"; 195 request.Referer = refer; 196 request.KeepAlive = true; 197 request.AllowAutoRedirect = true; 198 request.UnsafeAuthenticatedConnectionSharing = true; 199 request.CookieContainer = new CookieContainer(); 200 //据说能提高性能 201 //request.Proxy = null; 202 if (_cookiecollection != null) 203 { 204 foreach (Cookie c in _cookiecollection) 205 { 206 c.Domain = request.Host; 207 } 208 209 request.CookieContainer.Add(_cookiecollection); 210 } 211 212 foreach (KeyValuePair<String, String> hd in _headers) 213 { 214 request.Headers[hd.Key] = hd.Value; 215 } 216 217 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 218 html = GetStringFromResponse(response); 219 if (request.CookieContainer != null) 220 { 221 response.Cookies = request.CookieContainer.GetCookies(request.RequestUri); 222 } 223 224 if (response.Cookies != null) 225 { 226 _cookiecollection = response.Cookies; 227 } 228 if (response.Headers["Set-Cookie"] != null) 229 { 230 string tmpcookie = response.Headers["Set-Cookie"]; 231 _cookiecollection.Add(ConvertCookieString(tmpcookie)); 232 } 233 234 response.Close(); 235 return html; 236 } 237 catch (Exception e) 238 { 239 Trace.WriteLine("HttpGet Error: " + e.Message); 240 return String.Empty; 241 } 242 } 243 244 /// <summary> 245 /// 获取MINE文件 246 /// </summary> 247 /// <param name="url"></param> 248 /// <returns></returns> 249 public Byte[] HttpGetMine(String url) 250 { 251 Byte[] mine = null; 252 try 253 { 254 ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckCertificate); 255 HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); 256 request.UserAgent = _useragent; 257 request.Timeout = _timeout; 258 request.ContentType = _contenttype; 259 request.Accept = _accept; 260 request.Method = "GET"; 261 request.Referer = url; 262 request.KeepAlive = true; 263 request.AllowAutoRedirect = true; 264 request.UnsafeAuthenticatedConnectionSharing = true; 265 request.CookieContainer = new CookieContainer(); 266 //据说能提高性能 267 request.Proxy = null; 268 if (_cookiecollection != null) 269 { 270 foreach (Cookie c in _cookiecollection) 271 c.Domain = request.Host; 272 request.CookieContainer.Add(_cookiecollection); 273 } 274 275 foreach (KeyValuePair<String, String> hd in _headers) 276 { 277 request.Headers[hd.Key] = hd.Value; 278 } 279 280 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 281 Stream stream = response.GetResponseStream(); 282 MemoryStream ms = new MemoryStream(); 283 284 byte[] b = new byte[1024]; 285 while (true) 286 { 287 int s = stream.Read(b, 0, b.Length); 288 ms.Write(b, 0, s); 289 if (s == 0 || s < b.Length) 290 { 291 break; 292 } 293 } 294 mine = ms.ToArray(); 295 ms.Close(); 296 297 if (request.CookieContainer != null) 298 { 299 response.Cookies = request.CookieContainer.GetCookies(request.RequestUri); 300 } 301 302 if (response.Cookies != null) 303 { 304 _cookiecollection = response.Cookies; 305 } 306 if (response.Headers["Set-Cookie"] != null) 307 { 308 _cookies = response.Headers["Set-Cookie"]; 309 } 310 311 stream.Close(); 312 stream.Dispose(); 313 response.Close(); 314 return mine; 315 } 316 catch (Exception e) 317 { 318 Trace.WriteLine("HttpGetMine Error: " + e.Message); 319 return null; 320 } 321 } 322 323 /// <summary> 324 /// 发送POST请求 325 /// </summary> 326 /// <param name="url"></param> 327 /// <param name="data"></param> 328 /// <returns></returns> 329 public String HttpPost(String url, String data) 330 { 331 return HttpPost(url, data, url,null); 332 } 333 334 /// <summary> 335 /// 发送POST请求 336 /// </summary> 337 /// <param name="url"></param> 338 /// <param name="data"></param> 339 /// <param name="refer"></param> 340 /// <returns></returns> 341 public String HttpPost(String url, String data, String refer,string cookie) 342 { 343 String html; 344 try 345 { 346 ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckCertificate); 347 HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); 348 request.UserAgent = _useragent; 349 request.Timeout = _timeout; 350 request.Referer = refer; 351 request.ContentType = _contenttype; 352 request.Accept = _accept; 353 request.Method = "POST"; 354 request.KeepAlive = true; 355 request.AllowAutoRedirect = true; 356 357 request.CookieContainer = new CookieContainer(); 358 if (!string.IsNullOrEmpty(cookie)) 359 { 360 _cookiecollection = this.ConvertCookieString(cookie); 361 } 362 //据说能提高性能 363 request.Proxy = null; 364 365 if (_cookiecollection != null) 366 { 367 foreach (Cookie c in _cookiecollection) 368 { 369 c.Domain = request.Host; 370 if (c.Domain.IndexOf(':') > 0) 371 c.Domain = c.Domain.Remove(c.Domain.IndexOf(':')); 372 } 373 request.CookieContainer.Add(_cookiecollection); 374 } 375 376 foreach (KeyValuePair<String, String> hd in _headers) 377 { 378 request.Headers[hd.Key] = hd.Value; 379 } 380 byte[] buffer = _encoding.GetBytes(data.Trim()); 381 request.ContentLength = buffer.Length; 382 request.GetRequestStream().Write(buffer, 0, buffer.Length); 383 request.GetRequestStream().Close(); 384 385 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 386 html = GetStringFromResponse(response); 387 if (request.CookieContainer != null) 388 { 389 response.Cookies = request.CookieContainer.GetCookies(request.RequestUri); 390 } 391 if (response.Cookies != null) 392 { 393 _cookiecollection = response.Cookies; 394 } 395 if (response.Headers["Set-Cookie"] != null) 396 { 397 string tmpcookie = response.Headers["Set-Cookie"]; 398 _cookiecollection.Add(ConvertCookieString(tmpcookie)); 399 } 400 401 response.Close(); 402 return html; 403 } 404 catch (Exception e) 405 { 406 Trace.WriteLine("HttpPost Error: " + e.Message); 407 return String.Empty; 408 } 409 } 410 411 412 public string UrlEncode(string str) 413 { 414 StringBuilder sb = new StringBuilder(); 415 byte[] byStr = _encoding.GetBytes(str); 416 for (int i = 0; i < byStr.Length; i++) 417 { 418 sb.Append(@"%" + Convert.ToString(byStr[i], 16)); 419 } 420 421 return (sb.ToString()); 422 } 423 424 /// <summary> 425 /// 转换cookie字符串到CookieCollection 426 /// </summary> 427 /// <param name="ck"></param> 428 /// <returns></returns> 429 private CookieCollection ConvertCookieString(string ck) 430 { 431 CookieCollection cc = new CookieCollection(); 432 string[] cookiesarray = ck.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); 433 for (int i = 0; i < cookiesarray.Length; i++) 434 { 435 string[] cookiesarray_2 = cookiesarray[i].Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); 436 for (int j = 0; j < cookiesarray_2.Length; j++) 437 { 438 string[] cookiesarray_3 = cookiesarray_2[j].Trim().Split("=".ToCharArray()); 439 if (cookiesarray_3.Length == 2) 440 { 441 string cname = cookiesarray_3[0].Trim(); 442 string cvalue = cookiesarray_3[1].Trim(); 443 if (cname.ToLower() != "domain" && cname.ToLower() != "path" && cname.ToLower() != "expires") 444 { 445 Cookie c = new Cookie(cname, cvalue); 446 cc.Add(c); 447 } 448 } 449 } 450 } 451 452 return cc; 453 } 454 455 456 public void DebugCookies() 457 { 458 Trace.WriteLine("**********************BEGIN COOKIES*************************"); 459 foreach (Cookie c in _cookiecollection) 460 { 461 Trace.WriteLine(c.Name + "=" + c.Value); 462 Trace.WriteLine("Path=" + c.Path); 463 Trace.WriteLine("Domain=" + c.Domain); 464 } 465 Trace.WriteLine("**********************END COOKIES*************************"); 466 } 467 468 } 469 } Httphelper 小哥哥 小姐姐们 如果本篇文章对你们有帮助的话 就点点右下角的推荐吧 0.0 现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!! C#/.NetCore技术交流群:608188505欢迎加群交流 如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!

优秀的个人博客,低调大师

(2)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- .NetCore启动配置 和 .NetCoreW...

什么是.Net Core?.Net Core是微软开发的另外一个可以跨Linux、Windows、mac等平台的.Net.Net Core相关知识看文章地步dotnet dllname.dll 运行PostMan 是一个非常专业的调试webapi的工具 Asp.net Core 配置 设置启动端口 CMD -> set ASPNETCORE_URLS=http://127.0.0.1:8080 (程序中读取这个配置很难) 通过代码修改端口 在Program下的CreateWebHostBuilder(.net core 2.1) / CreateDefaultBuilder(.net core 2.0)方法最后.UserUrls("http://127.0.0.1:9999"); (写死的) 通过命令符配置设置启动端口 修改Program下的CreateWebHostBuilder(.net core 2.1) / CreateDefaultBuilder(.net core 2.0)方法 获取cmd命令中传过来的IP跟端口号 var config = new ConfigurationBuilder().AddCommandLine(args).Build(); String ip = config["ip"]; String prot = config["port"]; 在最后.UseUrls($"http://{ip}:{port}"); 调用cmd -> dotnet name.dll --ip 127.0.0.1 --port 8888 ----------WebApi就是restful风格,请求响应最好是json格式 .NetCore中 webapi 跟 MVC 变成了同一个Controller的父类 其他的跟.Net中使用上没有太大的区别 通过状态码而非响应体报错,是restful风格 .Net Core相关资料https://windsting.github.io/little-aspnetcore-book/book/ https://docs.microsoft.com/zh-cn/aspnet/core/.Net Core WebApihttps://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-2.1 小哥哥 小姐姐们 如果本篇文章对你们有帮助的话 就点点右下角的推荐吧 0.0 现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!! C#/.NetCore技术交流群:608188505欢迎加群交流 如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!

优秀的个人博客,低调大师

(1)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型

开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点: 1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块; 2)系统耦合性强,一旦其中一个模块有问题,整个系统就瘫痪了;一旦升级其中一个模块,整个系统就停机了; 3)要上线必须一起上线,互相等待,无法快速响应需求; 4)集群只能是复制整个系统,即使只是其中一个模块压力大; 微服务:不同模块放到不同的进程/服务器上,模块之间通过网络通讯进行协作。适用于:模块比较多,访问量比较大的互联网类系统,并不是所有项目都适合微服务 优点: 1)可以用不同的语言或者语言不同版本开发不同模块; 2)系统耦合性弱,其中一个模块有问题,可以通过“降级熔断”等手段来保证系统不血崩; 3)可以独立上线,能够迅速响应需求; 4)可以对不同模块用不同的集群策略,哪里慢集群哪里。 缺点: 1)开发难度大,系统结构更复杂; 2)运行效率低;(网络通讯没有进程通讯快) 微服务架构要处理哪些问题?服务间通讯;服务治理与服务发现;网关和安全认证;限流与容错;监视等第一代微服务:Dubbo(Java)、Orleans(.Net)等 和语言绑定紧密;第二代微服务:Spring Cloud等 适合混合开发第二代微服务:Service Mesh(Service Fabric(微软内部使用的、开源的)、lstio、Conduit等)目前:第三方微服务还在快速发展中,更新迭代比较快(还不完善) .NetCore 微服务选型为什么是.net Core?虽然.Net Framework也可以实现微服务,但是.Net Core是为云而生,用来实现微服务更方便,而且.Net Core可以跨平台。(.Net Framewordk不会再有.Net5.x 下一代就是.Net Core) 第二代微服务架构:Consul+Ocelot+.Net Core+Polly+.....;腾讯.net大队长张善友把腾讯内部的架构实战整理出一个开源项目NanoFabric(Github上可以搜到),NanoFabric不是一个独立的技术,它只是帮我们搭建好了,配置好了的一个脚手架,只是一个胶水项目,文档不全,仅供我们参考。 在SpringCloud中:EurekaServer做服务治理和服务发现、Hystrix做熔断降级、Zuul做网关;在NanoFabric中:Consul做服务治理和服务发现、Polly做熔断降级、Ocelot做网关; 在微服务中,服务之间的通讯有俩种主要形式:1)Restful,也就是传输Json格式数据。.net中就是对应的webapi技术 效率比较低,数据冗余,短连接2)二进制RPC:二进制传输协议,比restful用的http通讯效率更高,但是耦合性更强。技术有Thrift、gRPC等 此文档是根据杨中科老师的.net Core微服务视频和课件做的记录 小哥哥 小姐姐们 如果本篇文章对你们有帮助的话 就点点右下角的推荐吧 0.0 现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!! C#/.NetCore技术交流群:608188505欢迎加群交流 如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!

优秀的个人博客,低调大师

(7)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 利用Polly+AOP+依赖注入封装的降级框架

创建简单的熔断降级框架 要达到的目标是:参与降级的方法参数要一样,当HelloAsync执行出错的时候执行HelloFallBackAsync方法。 public class Person { [HystrixCommand("HelloFallBackAsync")] public virtual async Task<string> HelloAsync(string name) { Console.WriteLine("hello"+name); return "ok"; } public async Task<string> HelloFallBackAsync(string name) { Console.WriteLine("执行失败"+name); return "fail"; }} 1、编写 HystrixCommandAttribute using AspectCore.DynamicProxy; using System; using System.Threading.Tasks; namespace hystrixtest1 { //限制这个特性只能标注到方法上 [AttributeUsage(AttributeTargets.Method)] public class HystrixCommandAttribute : AbstractInterceptorAttribute { public HystrixCommandAttribute(string fallBackMethod) { this.FallBackMethod = fallBackMethod; } public string FallBackMethod { get; set; } public override async Task Invoke(AspectContext context, AspectDelegate next) { try { await next(context);//执行被拦截的方法 } catch (Exception ex) { //context.ServiceMethod被拦截的方法。context.ServiceMethod.DeclaringType被拦截方法所在的类 //context.Implementation实际执行的对象p //context.Parameters方法参数值 //如果执行失败,则执行FallBackMethod //调用降级方法 //1.调用降级的方法(根据对象获取类,从类获取方法) var fallBackMethod = context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod); //2.调用降级的方法 Object fallBackResult = fallBackMethod.Invoke(context.Implementation, context.Parameters); //3.把降级方法的返回值返回 context.ReturnValue = fallBackResult; } } } } 2、编写类 public class Person//需要public类 { [HystrixCommand(nameof(HelloFallBackAsync))] public virtual async Task<string> HelloAsync(string name)//需要是虚方法 { Console.WriteLine("hello"+name); String s = null; // s.ToString(); return "ok"; } public async Task<string> HelloFallBackAsync(string name) { Console.WriteLine("执行失败"+name); return "fail"; } [HystrixCommand(nameof(AddFall))] public virtual int Add(int i,int j) { String s = null; //s.ToArray(); return i + j; } public int AddFall(int i, int j) { return 0; } } 3、创建代理对象 ProxyGeneratorBuilder proxyGeneratorBuilder = new ProxyGeneratorBuilder(); using (IProxyGenerator proxyGenerator = proxyGeneratorBuilder.Build()) { Person p = proxyGenerator.CreateClassProxy<Person>(); Console.WriteLine(p.HelloAsync("yzk").Result); Console.WriteLine(p.Add(1, 2)); } 上面的代码还支持多次降级,方法上标注[HystrixCommand]并且virtual即可: public class Person//需要public类 { [HystrixCommand(nameof(Hello1FallBackAsync))] public virtual async Task<string> HelloAsync(string name)//需要是虚方法 { Console.WriteLine("hello" + name); String s = null; s.ToString(); return "ok"; } [HystrixCommand(nameof(Hello2FallBackAsync))] public virtual async Task<string> Hello1FallBackAsync(string name) { Console.WriteLine("Hello降级1" + name); String s = null; s.ToString(); return "fail_1"; } public virtual async Task<string> Hello2FallBackAsync(string name) { Console.WriteLine("Hello降级2" + name); return "fail_2"; } [HystrixCommand(nameof(AddFall))] public virtual int Add(int i, int j) { String s = null; s.ToString(); return i + j; } public int AddFall(int i, int j) { return 0; } } 细化框架 上面明白了了原理,然后直接展示写好的更复杂的HystrixCommandAttribute,讲解代码。 这是杨中科老师维护的开源项目 github最新地址 https://github.com/yangzhongke/RuPeng.HystrixCore Nuget地址:https://www.nuget.org/packages/RuPeng.HystrixCore 重试:MaxRetryTimes表示最多重试几次,如果为0则不重试,RetryIntervalMilliseconds 表示重试间隔的毫秒数; 熔断:EnableCircuitBreaker是否启用熔断,ExceptionsAllowedBeforeBreaking表示熔断前出现允许错误几次,MillisecondsOfBreak表示熔断多长时间(毫秒); 超时:TimeOutMilliseconds执行超过多少毫秒则认为超时(0表示不检测超时) 缓存:CacheTTLMilliseconds 缓存多少毫秒(0 表示不缓存),用“类名+方法名+所有参数值ToString拼接”做缓存Key(唯一的要求就是参数的类型ToString对于不同对象一定要不一样)。 用到了缓存组件:Install-Package Microsoft.Extensions.Caching.Memory using System; using AspectCore.DynamicProxy; using System.Threading.Tasks; using Polly; namespace RuPeng.HystrixCore { [AttributeUsage(AttributeTargets.Method)] public class HystrixCommandAttribute : AbstractInterceptorAttribute { /// <summary> /// 最多重试几次,如果为0则不重试 /// </summary> public int MaxRetryTimes { get; set; } = 0; /// <summary> /// 重试间隔的毫秒数 /// </summary> public int RetryIntervalMilliseconds { get; set; } = 100; /// <summary> /// 是否启用熔断 /// </summary> public bool EnableCircuitBreaker { get; set; } = false; /// <summary> /// 熔断前出现允许错误几次 /// </summary> public int ExceptionsAllowedBeforeBreaking { get; set; } = 3; /// <summary> /// 熔断多长时间(毫秒) /// </summary> public int MillisecondsOfBreak { get; set; } = 1000; /// <summary> /// 执行超过多少毫秒则认为超时(0表示不检测超时) /// </summary> public int TimeOutMilliseconds { get; set; } = 0; /// <summary> /// 缓存多少毫秒(0表示不缓存),用“类名+方法名+所有参数ToString拼接”做缓存Key /// </summary> public int CacheTTLMilliseconds { get; set; } = 0; //由于CircuitBreaker要求同一段代码必须共享同一个Policy对象。 //而方法上标注的Attribute 对于这个方法来讲就是唯一的对象,一个方法对应一个方法上标注的Attribute对象。 //一般我们熔断控制是针对一个方法,一个方法无论是通过几个 Person 对象调用,无论是谁调用,只要全局出现ExceptionsAllowedBeforeBreaking次错误,就会熔断,这是框架的实现,你如果认为不合理,自己改去。 //我们在Attribute上声明一个Policy的成员变量,这样一个方法就对应一个Policy对象。 private Policy policy; private static readonly Microsoft.Extensions.Caching.Memory.IMemoryCache memoryCache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new Microsoft.Extensions.Caching.Memory.MemoryCacheOptions()); /// <summary> /// /// </summary> /// <param name="fallBackMethod">降级的方法名</param> public HystrixCommandAttribute(string fallBackMethod) { this.FallBackMethod = fallBackMethod; } public string FallBackMethod { get; set; } public override async Task Invoke(AspectContext context, AspectDelegate next) { //一个HystrixCommand中保持一个policy对象即可 //其实主要是CircuitBreaker要求对于同一段代码要共享一个policy对象 //根据反射原理,同一个方法就对应一个HystrixCommandAttribute,无论几次调用, //而不同方法对应不同的HystrixCommandAttribute对象,天然的一个policy对象共享 //因为同一个方法共享一个policy,因此这个CircuitBreaker是针对所有请求的。 //Attribute也不会在运行时再去改变属性的值,共享同一个policy对象也没问题 lock (this)//因为Invoke可能是并发调用,因此要确保policy赋值的线程安全 { if (policy == null) { policy = Policy.NoOpAsync();//创建一个空的Policy if (EnableCircuitBreaker) //先保证熔断 { policy = policy.WrapAsync(Policy.Handle<Exception>().CircuitBreakerAsync(ExceptionsAllowedBeforeBreaking, TimeSpan.FromMilliseconds(MillisecondsOfBreak))); } if (TimeOutMilliseconds > 0) //控制是否超时 { policy = policy.WrapAsync(Policy.TimeoutAsync(() => TimeSpan.FromMilliseconds(TimeOutMilliseconds), Polly.Timeout.TimeoutStrategy.Pessimistic)); } if (MaxRetryTimes > 0) //如果出错等待MaxRetryTimes时间在执行 { policy = policy.WrapAsync(Policy.Handle<Exception>().WaitAndRetryAsync(MaxRetryTimes, i => TimeSpan.FromMilliseconds(RetryIntervalMilliseconds))); } Policy policyFallBack = Policy .Handle<Exception>() //出错了报错 如果出错就尝试调用降级方法 .FallbackAsync(async (ctx, t) => { //这里拿到的就是ExecuteAsync(ctx => next(context), pollyCtx);这里传的 pollyCtx AspectContext aspectContext = (AspectContext)ctx["aspectContext"]; var fallBackMethod = context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod); Object fallBackResult = fallBackMethod.Invoke(context.Implementation, context.Parameters); //不能如下这样,因为这是闭包相关,如果这样写第二次调用Invoke的时候context指向的 //还是第一次的对象,所以要通过Polly的上下文来传递AspectContext //context.ReturnValue = fallBackResult; aspectContext.ReturnValue = fallBackResult; }, async (ex, t) => { }); policy = policyFallBack.WrapAsync(policy); } } //把本地调用的AspectContext传递给Polly,主要给FallbackAsync中使用,避免闭包的坑 Context pollyCtx = new Context();//Context是polly中通过Execute给FallBack、Execute等回调方法传上下文对象使用的 pollyCtx["aspectContext"] = context;//context是aspectCore的上下文 //Install-Package Microsoft.Extensions.Caching.Memory if (CacheTTLMilliseconds > 0) { //用类名+方法名+参数的下划线连接起来作为缓存key string cacheKey = "HystrixMethodCacheManager_Key_" + context.ServiceMethod.DeclaringType + "." + context.ServiceMethod + string.Join("_", context.Parameters); //尝试去缓存中获取。如果找到了,则直接用缓存中的值做返回值 if (memoryCache.TryGetValue(cacheKey, out var cacheValue)) { context.ReturnValue = cacheValue; } else { //如果缓存中没有,则执行实际被拦截的方法 await policy.ExecuteAsync(ctx => next(context), pollyCtx); //存入缓存中 using (var cacheEntry = memoryCache.CreateEntry(cacheKey)) { cacheEntry.Value = context.ReturnValue;//返回值放入缓存 cacheEntry.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMilliseconds(CacheTTLMilliseconds); } } } else//如果没有启用缓存,就直接执行业务方法 { await policy.ExecuteAsync(ctx => next(context), pollyCtx); } } } } 框架不是万能的,不用过度框架,过度框架带来的复杂度陡增,从人人喜欢变成人人恐惧。 结合asp.net core依赖注入 在asp.net core项目中,可以借助于asp.net core的依赖注入,简化代理类对象的注入,不用再自己调用ProxyGeneratorBuilder 进行代理类对象的注入了。 Install-Package AspectCore.Extensions.DependencyInjection 修改Startup.cs的ConfigureServices方法,把返回值从void改为IServiceProvider using AspectCore.Extensions.DependencyInjection; public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSingleton<Person>(); return services.BuildAspectCoreServiceProvider(); } 其 中 services.AddSingleton<Person>(); 表 示 把Person注 入 。 BuildAspectCoreServiceProvider是让aspectcore接管注入。 在Controller中就可以通过构造函数进行依赖注入了: public class ValuesController : Controller { private Person p; public ValuesController(Person p) { this.p = p; } } 通过反射扫描所有Service类,只要类中有标记了CustomInterceptorAttribute的方法都算作服务实现类。为了避免一下子扫描所有类,所以 RegisterServices 还是手动指定从哪个程序集中加载。 public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); RegisterServices(this.GetType().Assembly, services); return services.BuildAspectCoreServiceProvider(); } private static void RegisterServices(Assembly asm, IServiceCollection services) { //遍历程序集中的所有public类型 foreach (Type type in asm.GetExportedTypes()) { //判断类中是否有标注了CustomInterceptorAttribute的方法 bool hasCustomInterceptorAttr = type.GetMethods().Any(m => m.GetCustomAttribute(typeof(CustomInterceptorAttribute)) != null); if (hasCustomInterceptorAttr) { services.AddSingleton(type); } } } 注:此文章是我看杨中科老师的.Net Core微服务第二版和.Net Core微服务第二版课件整理出来的 现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!! C#/.NetCore技术交流群:608188505欢迎加群交流 如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!

优秀的个人博客,低调大师

Micro-One-API v0.30.0 发布:分组 v2 生产启用收口 —— 部署接线、可观测性、迁移预检与操作解释

v0.30.0 是 v0.29.0 之后的 MINOR 启用与稳定性收口版本,包含 6 个提交、105 个文件、+5.8k/-0.3k 行。v0.29.0 交付的分组重设计 v2(阶段 A–F)全部能力默认关闭、未经真实链路验收;本版按 v0.30 阶段路线图 完成 P0 P1 P2 三个优先级共 7 项任务:接通部署开关并完成 MySQL SQLite 真实服务验收,补齐路由 outbox 可观测性与告警,收口管理台汇总性能与降级语义,为迁移 runner 增加旧元数据表预检,并补齐分组日常操作与账务解释链路。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册