首页 文章 精选 留言 我的

精选列表

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

ASP.NET Core on K8S深入学习(6)Health Check

本篇已加入《.NET Core on K8S学习实践系列文章索引》,可以点击查看更多容器化技术相关系列文章。 一、关于K8S中的Health Check 所谓Health Check,就是健康检查,即防微杜渐。K8S是一个编排引擎可以帮助我们快捷地部署容器集群,如果部署上错误的容器导致服务崩溃,通常情况下我们都会通过一些高可用机制进行故障转移。但是,前提条件是有健康检查。 K8S自然帮我们考虑到了这个问题,健康检查是K8S的重要特性之一,默认有健康检查机制,此外还可以主动设置一些自定义的健康检查。 默认情况下,每个容器启动时都会执行一个进程,由Dockerfile中的CMD或ENTRYPOINT指定。如果进程退出时的返回码不为0,则认为容器发生了故障,K8S会根据重启策略(restartPolicy)重启容器。 例如下面这个例子,它模拟了容器发生故障的场景,注意下面配置文件中的args选项的定义: apiVersion: v1 kind: Pod metadata: name: edc-healthcheck-demo labels: test: healthcheck spec: restartPolicy: OnFailure containers: - name: healthcheck image: busybox imagePullPolicy: IfNotPresent args: - /bin/sh - -c - sleep 10; exit 1 其中 sleep 10; exit 1代表启动10秒之后就非正常退出(返回码不为0),然后通过kubectl创建Pod: kubectl apply -f health-check.yaml 过一段时间后查看Pod的状态,如下图所示: 可以看到,该容器已经重启了2次。也可以看出,restartPolicy简单直接暴力有效,不由感叹重启大法好! 但是,也要正视一个问题:必须等到进程退出后的返回值是非零才会触发重启策略,不能直接监测容器是否是健康。 那么,K8S中有没有更好的机制能够实现智能一点的健康检查呢?答案就是使用Liveness与Readinesss。 二、Liveness探测 2.1 Liveness初体验 一句话Liveness:如果检测有问题(如果健康检查失败),重启pod!至于怎么检测,你说了算(自定义判断容器是否健康的条件)! Liveness提供了一些重要的参数: initialDelaySeconds:容器启动后第一次执行探测是需要等待多少秒,看运行的服务而定。 periodSeconds:执行探测的频率,默认是10秒,最小1秒。 timeoutSeconds:探测超时时间,默认1秒,最小1秒。 successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功,默认是1,对于liveness必须是1,最小值是1。 failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是3。最小值是1. 下面实践一个小例子创建一个Pod: #command自己定义,例子为 /tmp/healthy 不存在则认为pod有问题,大家根据实际业务来自定义。 apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-demo spec: containers: - name: liveness image: busybox args: - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf/tmp/healthy; sleep 10 livenessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 10 periodSeconds: 5 这里启动pod后会创建文件夹 /tmp/healthy,30秒后删除,在我们的设置中,如果 /tmp/healthy 存在,则认为容器处于正常状态,否则认为发生故障。 需要注意的就是livenessProbe部分的定义了: (1)探测方法:通过cat命令查看/tmp/healthy是否存在;如果返回值为0,则探测成功;否则,探测失败; (2)initialDelaySeconds: 10 => 容器启动10秒之后开始执行liveness探测; (3)periodSeconds: 5 => 每5秒执行一次liveness探测;如果连续执行3次探测都失败,那么就会杀掉并重启容器; 下面快速地验证一下: (1)kubectl创建demo kubectl apply -f liveness-demo.yaml (2)查看pod日志 kubectl describe pod liveness-demo 结果如下图所示: 30秒之后,/tmp/healthy 被删除了,liveness探测失败,又过了几十秒,重复探测均失败后,开启了重启容器。 2.2 Liveness探针 上面的例子使用的是Liveness的exec探针,此外K8S还有几种其他类型的探针: exec:在容器中执行一个命令,如果命令退出码返回0则表示探测成功,否则表示失败 tcpSocket:对指定的容IP及端口执行一个TCP检查,如果端口是开放的则表示探测成功,否则表示失败 httpGet:对指定的容器IP、端口及路径执行一个HTTP Get请求,如果返回的状态码在 [200,400)之间则表示探测成功,否则表示失败 针对tcpSocket的例子:这里会检测80端口是否可以正常访问; #检测80端口是否联通 apiVersion: v1 kind: Pod metadata: labels: test: readiness name: readiness-tcp spec: containers: - name: readiness image: nginx readinessProbe: failureThreshold: 3 tcpSocket: port: 80 initialDelaySeconds: 10 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10 针对httpGet的例子:这里会检测index.html文件是否可以正常访问; #访问80端口的index.html文件是否存在 apiVersion: v1 kind: Pod metadata: labels: test: readiness name: readiness-httpget spec: containers: - name: readiness image: nginx readinessProbe: failureThreshold: 3 httpGet: path: /index.html port: 80 scheme: HTTP initialDelaySeconds: 10 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10 三、Readiness探测 3.1 Readiness初体验 一句话Readiness:如果检查失败,K8S会将该Pod从服务代理的分发后端去除,不再让其接客(分发请求给该Pod)。如果检测成功,那么K8S就会将容器加入到分发后端,重新对外接客(对外提供服务)。 下面继续以上面Liveness的例子来实践一下: apiVersion: v1 kind: Pod metadata: labels: test: readiness name: readiness-demo spec: containers: - name: readiness image: busybox args: - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf/tmp/healthy; sleep 10 readinessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 10 periodSeconds: 5 readinessProbe的配置语法与livenessProbe完全一致,但执行后的效果却不一样,见下图所示: 可以看出: (1)刚被创建时,其READY状态为不可用; (2)15秒(initialDelaySeconds + periodSeconds = 10 + 5 = 15)之后,第一次进行Readiness探测成功,其READY状态变为可用。 (3)30秒之后,/tmp/healthy被删除,连续3次Readiness探测均失败后,其READY状态又变为了不可用。 此外,我们也可以通过kubectl describe pod readiness-demo 查看到更想起的日志信息。 3.2 与Liveness的对比 Liveness与Readiness都是K8S的Health Check机制,Liveness探测是重启容器,而Readiness探测则是将容器设置为不可用,不让其再接受Service转发的请求。 Liveness与Readiness是独立执行的,二者无依赖,可以单独使用也可以同时使用。 四、Health Check在K8S中的应用 4.1 在Scale Up中的应用 对于多副本应用,当执行Scale Up操作时,新的副本会作为后端服务加入到Service的负载均衡列表中。但是,很多时候应用的启动都需要一定的时间做准备(比如加载缓存、连接数据库等等),这时我们可以通过Readiness探测判断容器是否真正就绪,从而避免将请求发送到还未真正就绪的后端服务。 下面一个示例YAML配置文件定义了Readiness探测,重点关注readinessProbe部分: apiVersion: apps/v1 kind: Deployment metadata: name: edc-webapi-deployment namespace: aspnetcore spec: replicas: 2 selector: matchLabels: name: edc-webapi template: metadata: labels: name: edc-webapi spec: containers: - name: edc-webapi-container image: edisonsaonian/k8s-demo:1.2 ports: - containerPort: 80 imagePullPolicy: IfNotPresent readinessProbe: httpGet: scheme: HTTP path: /api/health port: 80 initialDelaySeconds: 10 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: edc-webapi-service namespace: aspnetcore spec: type: NodePort ports: - nodePort: 31000 port: 8080 targetPort: 80 selector: name: edc-webapi 对于readinessProbe部分: (1)schema指定了协议,这里是HTTP协议,也可以是HTTPS协议; (2)path指定访问路径,这里是我们自定义的一个Controller中的接口:简单地返回一个状态码为200的响应; [Produces("application/json")] [Route("api/Health")] public class HealthController : Controller { [HttpGet] public IActionResult Get() => Ok("ok"); } (3)port指定端口,这里是容器的端口80; (4)initialDelaySeconds和periodSeconds指定了容器启动10秒之后开始探测,然后每隔5秒执行探测,如果发生3次以上探测失败,则该容器会从Service的负载均衡中移除,直到下次探测成功后才会重新加入。 4.2 在Rolling Update中的应用 假设现在有一个正常运行的多副本应用,我们要对其进行滚动更新即Rolling Update,K8S会逐步用新Pod替换旧Pod,结果就有可能发生这样的一个场景:当所有旧副本被替换之后,而新的Pod由于人为配置错误一直无法启动,因此整个应用将无法处理请求,无法对外提供服务,后果很严重! 因此,Readiness探测还提供了用于避免滚动更新中出现这种情况的一些解决办法,比如maxSurge和maxUnavailable两个参数,用来控制副本替换的数量。 继续以上面的YAML配置文件为例,重点关注strategy部分: apiVersion: apps/v1 kind: Deployment metadata: name: edc-webapi-deployment namespace: aspnetcore spec: strategy: rollingupdate: maxSurge: 25% maxUnavailable: 25% replicas: 10 selector: matchLabels: name: edc-webapi template: metadata: labels: name: edc-webapi spec: containers: - name: edc-webapi-container image: edisonsaonian/k8s-demo:1.2 ports: - containerPort: 80 imagePullPolicy: IfNotPresent readinessProbe: httpGet: scheme: HTTP path: /api/health port: 80 initialDelaySeconds: 10 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: edc-webapi-service namespace: aspnetcore spec: type: NodePort ports: - nodePort: 31000 port: 8080 targetPort: 80 selector: name: edc-webapi (1)maxSurge : 25% => 控制滚动更新过程中副本总数超过预期(这里预期是10个副本 replicas: 10)的上限,可以是数值也可以是百分比,然后向上取整。这里写的百分比,默认值是25%; 如果预期副本数为10,那么副本总数的最大值为RoundUp(10 + 10 * 25%)=13个。 (2)maxUnavailable : 25% => 控制滚动更新过程中不可用的副本(这里预期是10个副本 replicas: 10)占预期的最大比例,可以是数值也可以是百分比,然后向下取整,同样地默认值也是25%; 如果预期副本总数为10,那么可用的副本数至少要为10-roundDown(10 * 25%)=10-2=8个。 综上看来,maxSurge的值越大,初始创建的新副本数量就越多;maxUnavaliable值越大,初始销毁的旧副本数量就越多; 五、小结 本文探索了K8S中的默认健康检查机制以及Liveness和Readiness两种各有特点的探测机制,并通过一些小例子进行了说明。不过由于笔者也是初学,对于这一块没有过多实践经验,因此也是讲的比较粗浅,也希望以后能够有更多的实际经验分享与各位。 参考资料 (1)CloudMan,《每天5分钟玩转Kubernetes》 (2)李振良,《一天入门Kubernets教程》 (3)马哥(马永亮),《Kubernetes快速入门》 (4)华仔,《[[译]Kubernetes最佳实践:使用Readiness和Liveness探测做Health Check](https://blog.csdn.net/cainiaofly/article/details/84324321)》 (5)benjanmin杨,《K8S中的Health Check》 (6)条子在洗澡,《K8S健康性检查-探测》

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

ASP.NET Core微服务之服务间的调用方式(REST and RPC)

Tip: 此篇已加入.NET Core微服务基础系列文章索引 一、REST or RPC ? 1.1 REST & RPC 微服务之间的接口调用通常包含两个部分,序列化和通信协议。常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等;通信比较流行的是http、soap、websockect,RPC通常基于TCP实现,常用框架例如dubbo,netty、mina、thrift。 REST:严格意义上说接口很规范,操作对象即为资源,对资源的四种操作(post、get、put、delete),并且参数都放在URL上,但是不严格的说Http+json、Http+xml,常见的http api都可以称为Rest接口。 RPC:即我们常说的远程过程调用,就是像调用本地方法一样调用远程方法,通信协议大多采用二进制方式。 1.2 HTTP vs 高性能二进制协议 HTTP相对更规范,更标准,更通用,无论哪种语言都支持HTTP协议。如果你是对外开放API,例如开放平台,外部的编程语言多种多样,你无法拒绝对每种语言的支持,相应的,如果采用HTTP,无疑在你实现SDK之前,支持了所有语言,所以,现在开源中间件,基本最先支持的几个协议都包含RESTful。 RPC协议性能要高的多,例如Protobuf、Thrift、Kyro等,(如果算上序列化)吞吐量大概能达到http的二倍。响应时间也更为出色。千万不要小看这点性能损耗,公认的,微服务做的比较好的,例如,netflix、阿里,曾经都传出过为了提升性能而合并服务。如果是交付型的项目,性能更为重要,因为你卖给客户往往靠的就是性能上微弱的优势。 所以,最佳实践一般是对外REST,对内RPC,但是追求极致的性能会消耗很多额外的成本,所以一般情况下对内一般也REST,但对于个别性能要求较高的接口使用RPC。 二、案例结构 这里假设有两个服务,一个ClinetService和一个PaymentService,其中PaymentService有两部分,一部分是基于REST风格的WebApi部分,它主要是负责一些对性能没有要求的查询服务,另一部分是基于TCP的RPC Server,它主要是负责一些对性能要求高的服务,比如支付和支出等涉及到钱的接口。假设User在消费ClientService时需要调用PaymentService根据客户账户获取Payment History(走REST)以及进行交易事务操作(走RPC)。 三、REST调用 3.1 一个好用的REST Client : WebApiClient 使用过Java Feign Client的人都知道,一个好的声明式REST客户端可以帮我们省不少力。在.NET下,园子里的大大老九就写了一款类似于Feign Client的REST Client:WebApiClient。WebApiClient是开源在github上的一个httpClient客户端库,内部基于HttpClient开发,是一个只需要定义C#接口(interface),并打上相关特性,即可异步调用http-api的框架 ,支持.net framework4.5+、netcoreapp2.0和netstandard2.0。它的GitHub地址是:https://github.com/dotnetcore/WebApiClient 如何安装? NuGet>Install-Package WebApiClient-JIT 3.2 使用实例:走API Gateway Step1.定义HTTP接口 [HttpHost("http://yourgateway:5000")] public interface IPaymentWebApi: IHttpApi { // GET api/paymentservice/history/edisonzhou // Return 原始string内容 [HttpGet("/api/paymentservice/history/{account}")] ITask<IList<string>> GetPaymentHistoryByAccountAsync(string account); } 这里需要注意的是,由于我们要走API网关,所以这里定义的HttpHost地址是一个假的,后面具体调用时会覆盖掉,当然你也可以直接把地址写在这里,不过我更倾向于写到配置文件中,然后把这里的HttpHost设置注释掉。 Step2.在Controller中即可异步调用: [Route("api/[controller]")] public class PaymentController : Controller { private readonly string gatewayUrl;public PaymentController(IConfiguration _configuration) { gatewayUrl = _configuration["Gateway:Uri"]; } [HttpGet("{account}")] public async Task<IList<string>> Get(string account) { using (var client = HttpApiClient.Create<IPaymentWebApi>(gatewayUrl)) { var historyList = await client.GetPaymentHistoryByAccountAsync(account); // other business logic code here // ...... return historyList; } } } 当然你也可以在Service启动时注入一个单例的IPaymentServiceWebApi实例,然后直接在各个Controller中直接使用,这样更加类似于Feign Client的用法: (1)StartUp类注入 public void ConfigureServices(IServiceCollection services) { // IoC - WebApiClient services.AddSingleton(HttpApiClient.Create<IPaymentServiceWebApi>(Configuration["PaymentService:Url"])); } (2)Controller中直接使用 [HttpPost] public async Task<string> Post([FromBody]ModelType model, [FromServices]IPaymentServiceWebApi restClient) { ...... var result = await restClient.Save(model); ...... } 这里PaymentService的实现很简单,就是返回了一个String集合: // GET api/history/{account} [HttpGet("{account}")] public IList<string> Get(string account) { // some database logic // ...... IList<string> historyList = new List<string> { "2018-06-10,10000RMB,Chengdu", "2018-06-11,11000RMB,Chengdu", "2018-06-12,12000RMB,Beijing", "2018-06-13,10030RMB,Chengdu", "2018-06-20,10400RMB,HongKong" }; return historyList; } 最终调用结果如下: 3.3 使用实例:直接访问具体服务 在服务众多,且单个服务就部署了多个实例的情况下,我们可以通过API网关进行中转,但是当部分场景我们不需要通过API网关进行中转的时候,比如:性能要求较高,负载压力较小单个实例足够等,我们可以直接与要通信的服务进行联接,也就不用从API网关绕一圈。 Step1.改一下HTTP接口: [HttpHost("http://paymentservice:8880")] public interface IPaymentDirectWebApi: IHttpApi { // GET api/paymentservice/history/edisonzhou // Return 原始string内容 [HttpGet("/api/history/{account}")] ITask<IList<string>> GetPaymentHistoryByAccountAsync(string account); } 同理,这里的HttpHost也是后面需要被覆盖的,原因是我们将其配置到了配置文件中。 Step2.改一下调用代码: [Route("api/[controller]")] public class PaymentController : Controller { private readonly string gatewayUrl; private readonly string paymentServiceUrl; public PaymentController(IConfiguration _configuration) { gatewayUrl = _configuration["Gateway:Uri"]; paymentServiceUrl = _configuration["PaymentService:Uri"]; } [HttpGet("{account}")] public async Task<IList<string>> Get(string account) { #region v2 directly call PaymentService using (var client = HttpApiClient.Create<IPaymentDirectWebApi>(paymentServiceUrl)) { var historyList = await client.GetPaymentHistoryByAccountAsync(account); // other business logic code here // ...... return historyList; } #endregion } 最终调用结果如下: 四、RPC调用 4.1 Thrift简介 Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。 当然,还有gRPC也可以选择,不过从网上的性能测试来看,Thrift性能应该优于gRPC 2倍以上,但是gRPC的文档方面要比Thrift友好很多。 4.2 Thrift的使用 (1)下载Thrift (这里选择Windows版) 下载完成后解压,这里我将其改名为thrift.exe(去掉了版本号),一会在命令行敲起来更方便一点。 (2)编写一个PaymentService.thrift,这是一个IDL中间语言 namespace csharp Manulife.DNC.MSAD.Contracts service PaymentService { TrxnResult Save(1:TrxnRecord trxn) } enum TrxnResult { SUCCESS = 0, FAILED = 1, } struct TrxnRecord { 1: required i64 TrxnId; 2: required string TrxnName; 3: required i32 TrxnAmount; 4: required string TrxnType; 5: optional string Remark; } (3)根据thrift语法规则生成C#代码 cmd>thrift.exe -gen csharp PaymentService.thrift (4)创建一个Contracts类库项目,将生成的C#代码放进去 4.3 增加RPC Server (1)新增一个控制台项目,作为我们的Payment Service RPC Server,并引用Contracts类库项目 (2)引入thrift-netcore包: NuGet>Install-Package apache-thrift-netcore (3)加入一个新增的PaymentService实现类 public class PaymentServiceImpl : Manulife.DNC.MSAD.Contracts.PaymentService.Iface { public TrxnResult Save(TrxnRecord trxn) { // some business logic here //Thread.Sleep(1000 * 1); Console.WriteLine("Log : TrxnName:{0}, TrxnAmount:{1}, Remark:{2}", trxn.TrxnName, trxn.TrxnAmount, trxn.Remark); return TrxnResult.SUCCESS; } } 这里输出日志仅仅是为了测试。 (4)编写启动RPC Server的主程序 public class Program { private const int port = 8885; public static void Main(string[] args) { Console.WriteLine("[Welcome] PaymentService RPC Server is lanuched..."); TServerTransport transport = new TServerSocket(port); var processor = new Manulife.DNC.MSAD.Contracts.PaymentService.Processor(new PaymentServiceImpl()); TServer server = new TThreadedServer(processor, transport); // lanuch server.Serve(); } } (5)如果是多个服务实现的话,也可以如下这样启动: public static void Main(string[] args) { Console.WriteLine("[Welcome] PaymentService RPC Server is lanuched..."); TServerTransport transport = new TServerSocket(port); var processor1 = new Manulife.DNC.MSAD.Contracts.PaymentService.Processor(new PaymentServiceImpl()); var processor2 = new Manulife.DNC.MSAD.Contracts.PayoutService.Processor(new PayoutServiceImpl()); var processorMulti = new Thrift.Protocol.TMultiplexedProcessor(); processorMulti.RegisterProcessor("Service1", processor1); processorMulti.RegisterProcessor("Service2", processor2); TServer server = new TThreadedServer(processorMulti, transport); // lanuch server.Serve(); } 4.4 调用RPC 在ClientService中也引入apache-thrift-netcore包,然后在调用的地方修改如下: [HttpPost] public string Post([FromBody]TrxnRecordDTO trxnRecordDto) { // RPC - use Thrift using (TTransport transport = new TSocket( configuration["PaymentService:RpcIP"], Convert.ToInt32(configuration["PaymentService:RpcPort"]))) { using (TProtocol protocol = new TBinaryProtocol(transport)) { using (var serviceClient = new PaymentService.Client(protocol)) { transport.Open(); TrxnRecord record = new TrxnRecord { TrxnId = GenerateTrxnId(), TrxnName = trxnRecordDto.TrxnName, TrxnAmount = trxnRecordDto.TrxnAmount, TrxnType = trxnRecordDto.TrxnType, Remark = trxnRecordDto.Remark }; var result = serviceClient.Save(record); return Convert.ToInt32(result) == 0 ? "Trxn Success" : "Trxn Failed"; } } } } private long GenerateTrxnId() { return 10000001; } 最终测试结果如下: 五、小结 本篇简单的介绍了下微服务架构下服务之间调用的两种常用方式:REST与RPC,另外前面介绍的基于消息队列的发布/订阅模式也是服务通信的方式之一。本篇基于WebApiClient这个开源库介绍了如何进行声明式的REST调用,以及Thrift这个RPC框架介绍了如何进行RPC的通信,最后通过一个小例子来结尾。最后,服务调用的最佳实践一般是对外REST,对内RPC,但是追求极致的性能会消耗很多额外的成本,所以一般情况下对内一般也REST,但对于个别性能要求较高的接口使用RPC。 参考资料 远方的行者,《微服务 RPC和REST》 杨中科,《.NET Core微服务课程:Thrift高效通讯》 醉眼识朦胧,《Thrift入门初探--thrift安装及java入门实例》 focus-lei,《.net core下使用Thrift》 宝哥在路上,《Thrift性能测试与分析》

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

2.5配置的框架浅析「深入浅出ASP.NET Core系列」

希望给你3-5分钟的碎片化学习,可能是坐地铁、等公交,积少成多,水滴石穿,谢谢关注。 配置的使用流程 //第一步.初始化Builder var builder = new ConfigurationBuilder(); //第二步.将Source添加到Builder builder.AddJsonFile("student.json", false, true); //builder.AddInMemoryCollection(dict) //builder.AddXmlFile("/path/tmp.xml") //第三步.调用Build var configuration = builder.Build(); //第四步.使用 configurationRoot["key"] 第二步,在将Source添加到Builder的时候,内部做了哪些事情呢? 初始化对应的Source对象,比如Json文件配置源对象: JsonConfigurationSource sr=new JsonConfigurationSource() { Path = "settings.json", } 第三步,Build时候在内部,生成Provider对象,一个Source对应一个Provider,最后返回ConfigurationRoot,该Root包含所有的Provider。 foreach(var source in sources) { var provider = source.Build(); providers.add(provider); } return new ConfigurationRoot(providers); 第四步,在使用的时候,通过Provider去找到相应的key,返回key值。 foreach(var provider in providers.Reverse()) { string value; provider.TryGet(key,out value); return value; } 通过以上步骤,我们可以看到配置Source和配置Provider是关键的两个要点。 内部类关系图 如果所示,如果要自己定义配置,必须实现接口IConfigurationSource,并在内部实现一个对应的Provider,该Provider必须继承ConfigurationProvider抽象类,并在Provider读取配置,对配置进行维护、同步、热更新。具体如何定制,在后续进阶和高级进行讲解。 我是.NET架构师张飞洪,入行10年有余,人不堪其忧,吾不改其乐,谢谢您关注我的头条号。

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

1.2环境安装「深入浅出ASP.NET Core系列」

官网 在介绍安装环境之前,先介绍周边信息,比如微软net官网。 https://www.microsoft.com/net 这个网站是学习微软技术栈比较权威的地方,包括环境下载,学习,架构,文档,社区等等非常有价值的内容。 1.1下载.NET Core 下载网址:https://www.microsoft.com/net/download 微信支付 在逛这个网站的时候,偶然发现微信支付用的微服务就是基于.NET Core技术栈,视频里你还可以看到张善友本人(张善友是微软MVP,.NET Core社区推广大使),可以想见,未来的.NET Core的应用前景有多大…… 安装 接下来就是详细的安装过程了,官网以英文为主,如果你不想阅读二手的信息,请学好英文。 https://www.microsoft.com/net/learn/dotnet/hello-world-tutorial 这里我们看到跨平台的下载和安装方法,注意下载包默认是最新的版本,目前最新版本是2.1.403 我是.NET架构师张飞洪,入行10年有余,人不堪其忧,吾不改其乐,谢谢您关注我的头条号。

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

(9)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- JWT算法

一、 JWT 简介 内部 Restful 接口可以“我家大门常打开”,但是如果要给 app 等使用的接口,则需要做权限校验,不能谁都随便调用。 Restful 接口不是 web 网站,App 中很难直接处理 SessionId,而且 Cookie 有跨域访问的限制,所以一般不能直接用后端 Web 框架内置的 Session 机制。但是可以用类似 Session 的机制,用户登录之后返回一个类似 SessionId 的东西,服务器端把 SessionId 和用户的信息对应关系保存到 Redis 等地方,客户端把 SessionId 保存起来,以后每次请求的时候都带着这个SessionId。 用类似 Session 这种机制的坏处:需要集中的 Session 机制服务器;不可以在 nginx、CDN 等静态文件处理服务器上校验权限;每次都要根据 SessionId 去 Redis 服务器获取用户信息,效率低; JWT(Json Web Token)是现在流行的一种对 Restful 接口进行验证的机制的基础。 JWT 的特点:把用户信息放到一个 JWT 字符串中,用户信息部分是明文的,再加上一部分签名区域,签名部分是服务器对于“明文部分+秘钥”加密的,这个加密信息只有服务器端才能解析。用户端只是存储、转发这个 JWT 字符串。如果客户端篡改了明文部分,那么服务器端解密时候会报错。 JWT 由三块组成,可以把用户名、用户 Id 等保存到 Payload 部分 注意 Payload和 Header部分都是 Base64编码,可以轻松的 Base64解码回来。因此 Payload 部分约等于是明文的,因此不能在 Payload 中保存不能让别人看到的机密信息。虽然说 Payload 部分约等于是明文的,但是不用担心 Payload 被篡改,因为 Signature 部分是根据 header+payload+secretKey 进行加密算出来的,如果 Payload 被篡改,就可以根据 Signature 解密时候校验。 用 JWT 做权限验证的好处:无状态,更有利于分布式系统,不需要集中的 Session 机制服务器;可以在 nginx、CDN 等静态文件处理服务器上校验权限;获取用户信息直接从 JWT 中就可以读取,效率高; 二、.Net 中使用 JWT 算法 1) 加密 Nuget -> Install-Package JWT var payload = new Dictionary<string, object> { { "UserId", 123 }, { "UserName", "admin" } }; var secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";//不要泄露 IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); var token = encoder.Encode(payload, secret); Console.WriteLine(token); 2) 解密 var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiJ9.Qj w1epD5P6p4Yy2yju3-fkq28PddznqRj3ESfALQy_U"; var secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk"; try { IJsonSerializer serializer = new JsonNetSerializer(); IDateTimeProvider provider = new UtcDateTimeProvider(); IJwtValidator validator = new JwtValidator(serializer, provider); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); var json = decoder.Decode(token, secret, verify: true); Console.WriteLine(json); } catch (FormatException) { Console.WriteLine("Token format invalid"); } catch (TokenExpiredException) { Console.WriteLine("Token has expired"); } catch (SignatureVerificationException) { Console.WriteLine("Token has invalid signature"); } 试着篡改一下 Payload 部分。 3) 过期时间 在 payload 中增加一个名字为 exp 的值,值为过期时间和 1970/1/1 00:00:00 相差的秒数 double exp = (DateTime.UtcNow.AddSeconds(10) - new DateTime(1970, 1, 1)).TotalSeconds; 4) 不用秘钥解析数据payload 因为 payload 部分是明文的,所以在不知道秘钥的时候也可以用 Decode、DecodeToObject 等不需要秘钥的方法把payload部分解析出来。 var token ="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiJ9.Qjw1 epD5P6p4Yy2yju3-fkq28PddznqRj3ESfALQy_U"; try { IJsonSerializer serializer = new JsonNetSerializer(); IDateTimeProvider provider = new UtcDateTimeProvider(); IJwtValidator validator = new JwtValidator(serializer, provider); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); var json = decoder.Decode(token); Console.WriteLine(json); } catch (FormatException) { Console.WriteLine("Token format invalid"); } catch (TokenExpiredException) { Console.WriteLine("Token has expired"); } 现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!! C#/.NetCore技术交流群:608188505欢迎加群交流 如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!

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

(6)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- AOP框架

AOP 框架基础 要求懂的知识:AOP、Filter、反射(Attribute)。 如果直接使用 Polly,那么就会造成业务代码中混杂大量的业务无关代码。我们使用 AOP (如果不了解 AOP,请自行参考网上资料)的方式封装一个简单的框架,模仿 Spring cloud 中的 Hystrix。 需要先引入一个支持.Net Core 的 AOP,我们用.Net Core 下的 AOP 框架是AspectCore(国产,动态织入),其他要不就是不支持.Net Core,要不就是不支持对异步方法进行拦截 MVC Filter。 GitHub:https://github.com/dotnetcore/AspectCore-Framework Install-Package AspectCore.Core -Version 0.5.0 这里只介绍和我们相关的用法: 1、编写拦截器CustomInterceptorAttribute 一般继承自AbstractInterceptorAttribute public class CustomInterceptorAttribute:AbstractInterceptorAttribute { //每个被拦截的方法中执行 public async override Task Invoke(AspectContext context, AspectDelegate next) { try { Console.WriteLine("执行之前"); await next(context);//执行被拦截的方法 } catch (Exception) { Console.WriteLine("被拦截的方法出现异常"); throw; } finally { Console.WriteLine("执行之后"); } } } 2、编写需要被代理拦截的类 在要被拦截的方法上标注CustomInterceptorAttribute 。类需要是public类,方法如果需要拦截就是虚方法,支持异步方法,因为动态代理是动态生成被代理的类的动态子类实现的。 public class Person { [CustomInterceptor] public virtual void Say(string msg) { Console.WriteLine("service calling..."+msg); } } 3、通过AspectCore创建代理对象 ProxyGeneratorBuilder proxyGeneratorBuilder = new ProxyGeneratorBuilder(); using (IProxyGenerator proxyGenerator = proxyGeneratorBuilder.Build()) { Person p = proxyGenerator.CreateClassProxy<Person>(); p.Say("rupeng.com"); } Console.ReadKey(); 注意p指向的对象是AspectCore生成的Person的动态子类的对象,直接new Person是无法被拦截的。 研究AOP细节 拦截器中Invoke方法下的参数AspectContext的属性的含义: Implementation 实际动态创建的Person子类的对象。 ImplementationMethod就是Person子类的Say方法 Parameters 方法的参数值。 Proxy==Implementation:当前场景下 ProxyMethod==ImplementationMethod:当前场景下 ReturnValue返回值 ServiceMethod是Person的Say方法 注:此文章是我看杨中科老师的.Net Core微服务第二版和.Net Core微服务第二版课件整理出来的 现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!! C#/.NetCore技术交流群:608188505欢迎加群交流 如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

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

Nacos

Nacos

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

WebStorm

WebStorm

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

用户登录
用户注册