写在前面

  Api网关我们之前是用 .netcore写的 Ocelot的,使用后并没有完全达到我们的预期,花了些时间了解后觉得kong可能是个更合适的选择。

简单说下kong对比ocelot打动我的:

1、kong可以直接代替Nginx/OpenRestry做前端服务器。

2、kong的功能强大,性能不俗,生态不错,操作面板,插件丰富,社区活跃;

本文目的

1、对kong和consul做个基本介绍;

2、kong集成consul 做服务发现;

3、Asp.net core WebApi 服务自动注册到Consul;

4、Asp.net core WebApi 自动注册路由规则到kong,实现程序启动即部署;

运行环境

172.16.1.30 CentOS Linux release 7.6.1810 (Core) (虚拟机单核2g)

Docker version 18.09.3, build 774a1f4

kong apigateway(enterprise) 2.3.x (docker安装)

kong

kong的简介

我们熟悉Nginx;

有个一个加强版的Nginx叫做OpenRestry,OpenRestry ≈ lua脚本+Nginx;

那么Kong 网关就是满血版的 OpenRestry,它有许许多多的的插件和各种丰富的功能,且提供对应的Rest Api,让你轻松打造你所能想象到的 网关+ web前端服务器的功能;

特点(翻译)

  • 云原生:平台无关,kong支持任意平台,裸机容器或云平台;

  • k8s原生:原生支持k8s,有kong-ingress,支持l4+l7协议;

  • 动态负载均衡:负载均衡到多个upstream;

  • Hash-based的负载均衡:根据cookie、session,ip等hash负载均衡;

  • 断路器:自动剔除不健康的服务;

  • 心跳检测:主动和被动心跳检测;

  • 服务发现:通过第三方dns解析做服务发现,如consul;

  • Serverless:调用和保护 AWS Lambda or OpenWhisk functions directly ;

  • WebSockets:支持ws、wss协议;

  • gRPC:支持gRPC协议,并通过日志和插件监控流量;

  • OAuth2.0:轻松添加OAuth2.0支持;

  • 日志:轻松记录请求和响应,通过HTTP, TCP, UDP, 或 直接到硬盘;

  • 安全性:访问控制,爬虫检测、ip黑白名单等等;

  • Syslog:记录到系统日志;

  • SSL: 安装不同的SSL证书到服务;

  • 监控:实时监控,提供关机负责负载均衡和性能指标;

  • 整箱代理:kong可以作为正向代理服务器;

  • 身份认证:HMAC, JWT, Basic, 各种奇奇怪怪的规则都支持.

  • 限制器:流量限制功能;

  • 传输转换:新增、删掉、或者修改你的请求或者响应;

  • 缓存:请求缓存;

  • CLI:命令行控制支持;

  • Rest Api:Rest Api控制支持;

  • Geo-Replicated:夸时区请求支持;

  • 故障检测与恢复:数据库(Cassandra /postgres)节点挂掉不影响kong的服务;

  • 集群:所有kong节点都自动加入集群保持配置同步;

  • 拓展性:分布式拓展原生支持,水平伸缩加减节点就行;

  • 高性能:使用Nginx作为核心负载均衡组件,高性能可伸缩;

  • 插件:高拓展性,插件式添加功能;

详细请看

github: https://github.com/Kong/kong

官方文档: https://docs.konghq.com

kong的安装

拉取镜像

  1. docker pull kong/kong-gateway:2.3.3.2-alpine

给镜像改个名

  1. docker tag <IMAGE_ID> kong-ee

创建一个网络

  1. docker network create kong-ee-net

运行一个postgresSql 9.6,用来存取kong的配置

  1. docker run -d --name kong-ee-database \
  2. --network=kong-ee-net \
  3. -p 5432:5432 \
  4. -e "POSTGRES_USER=kong" \
  5. -e "POSTGRES_DB=kong" \
  6. -e "POSTGRES_PASSWORD=kong" \
  7. postgres:9.6

启动kong

  1. docker run -d --name kong-ee2
  2. --network=kong-ee-net \
  3. -e "KONG_DATABASE=postgres" \
  4. -e "KONG_PG_HOST=172.16.1.30" \
  5. -e "KONG_PG_PASSWORD=kong" \
  6. -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
  7. -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
  8. -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
  9. -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
  10. -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
  11. -e "KONG_ADMIN_GUI_URL=http://172.16.1.30:8002" \
  12. -e "KONG_DNS_RESOLVER=172.16.1.30:8600" \ #注意按需使用,consul的才配
  13. -p 8000:8000 \
  14. -p 8443:8443 \
  15. -p 8001:8001 \
  16. -p 8444:8444 \
  17. -p 8002:8002 \
  18. -p 8445:8445 \
  19. -p 8003:8003 \
  20. -p 8004:8004 \
  21. kong-ee
  22. //-e "KONG_DNS_RESOLVER=172.16.1.30:8600" 注意这个配置,这是我需要用的consul的dns配置,如果不想用consul做服务发现,删掉这行

这里说明一下,kong的配置是用postgres(或者Cassandra )来存配置,但每一次请求都不需要去读取数据库的。修改的配置会直接 reload 到内存中,不影响性能;

另外说说kong的集群;

因为kong 网关其实最终 表现为一个超级前端服务器+网关,所以每个连接到同个数据库的kong实例配置一样,连接同个数据库的kong作为一个集群;

一般在kong的前面是直接做dns解析就行,如果dns不支持多ip的话做keepalive + vip就行;

验证

  1. #admin api 获取所有服务
  2. curl -i -X GET --url http://127.0.0.1:8001/services
  3. #admin 管理后台
  4. curl -i -X GET --url http://127.0.0.1:8002

管理后台

consul

consul简介

  Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,比如 Airbnb的SmartStack等相比,Consul的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等),使用起来也较 为简单。

其实就是做服务治理的。

github: https://github.com/hashicorp/consul

官方文档: https://www.consul.io/

consul的安装

直接docker安装

*这是作为开发节点安装

  1. docker run -d --name=dev-consul1 --network=host -e CONSUL_BIND_INTERFACE=eth0 consul:1.8

安装成功

运行一个WebApi服务

先在服务运行一个Asp.net Core WebApi (就是是新建的一个包含),我的版本是3.1的,我给服务命名:DemoApi31,监听端口5002

将服务注册到Consul

  1. curl --location --request PUT 'http://172.16.1.30:8500/v1/agent/service/register' \
  2. --header 'Content-Type: application/json' \
  3. --data-raw '{
  4. "ID": "DemoApi31_172.16.1.30:5002",
  5. "Name": "DemoApi31",
  6. "Address": "172.18.1.30",
  7. "Port": 5002,
  8. "EnableTagOverride": false,
  9. "Weights": {
  10. "Passing": 10,
  11. "Warning": 1
  12. }
  13. }'

注册成功:

Dns解析验证

  1. # 如果没安装dig 安装:yum install bind-utils
  2. dig @172.16.1.30 -p 8600 Demoapi31.service.consul SRV

ok,我们这里已经把服务注册到consul,且能通过dns常解析到了,我们做跟kong的集成吧。

consul提供内置Dns解析和Rest Api 两种方式集成做服务发现,我们这里跟kong的集成选用的Dns方式。

kong集成consul做服务发现

因为consul的角色是dns服务器,所以非常简单,我们已注册好的 DemoApi31为例:

1、创建一个名为consul的服务

DemoApi31.service.consul 是consu要求的格式

2、创建一个名为consul的路由

验证

访问我们配置的kong路由:http://172.16.1.30:8000/consul/api/values

ok

到目前为止我们只完成了本文目的1、2

3,和4三请往下看;

在Asp.net Core中的使用

  以之前的DemoApi31为例,换成5003端口,我需要达到的效果是,程序启动的时候就把服务注册到Consul 做好心跳检测,并同时部署到网关Kong,直接对外服务。

Asp.net Core 服务自动注册到Consul

安装nuget包

  1. Install-Package Passport.Infrastructure -Version 0.1.4.7-preview-1

**加入配置appsettings.json**

大家主要各服务器要改成自己的

  1. "ServiceDiscovery": {
  2. "ServiceName": "DemoApi31",
  3. "Consul": {
  4. "HttpEndpoint": "http://172.16.1.30:8500",
  5. "HttpHeathCheck": {
  6. "Path": "/healthcheck",
  7. "TimeOunt": 10,
  8. "Interval": 10
  9. },
  10. "Tags": [
  11. "NetCore",
  12. "DemoApi",
  13. "v1.0"
  14. ]
  15. }
  16. }

StartUp.cs ConfigureServices方法

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. //第一行
  4. PassportConfig.InitPassportConfig(Configuration, Environment);
  5. ......
  6. services.AddHealthChecks();
  7. services.AddConsul();
  8. }

StartUp.cs Configure方法

  1. app.UseHealthChecks("/healthcheck");

启动程序

  1. dotnet DemoApi.Core3.1.dll --healthhost 172.16.1.30 --urls http://*:5003

源码解析

  1. /// <summary>
  2. /// 加入consul做服务管理
  3. /// </summary>
  4. /// <param name="services"></param>
  5. /// <returns></returns>
  6. public static IServiceCollection AddConsul(this IServiceCollection services)
  7. {
  8. var options = PassportConfig.GetSection("ServiceDiscovery").Get<ServiceDiscoveryOptions>();
  9. if (options?.Disable != true)
  10. {
  11. var healthHost = PassportConfig.GetHealthHost();
  12. if (string.IsNullOrWhiteSpace(options?.ServiceName) || string.IsNullOrWhiteSpace(options?.Consul?.HttpEndPoint))
  13. {
  14. throw new ArgumentNullException("ServiceDiscovery.ServiceName/Consul.HttpEndpoint cannot be null or empty!");
  15. }
  16. //实例化kongclient
  17. var consulClient = new ConsulClient(x => x.Address = new Uri(options.Consul.HttpEndPoint));
  18. services.AddSingleton(consulClient);
  19. services.Configure(new Action<ConsulOptions>(op =>
  20. {
  21. op.HttpEndPoint = options.Consul.HttpEndPoint;
  22. op.Token = options.Consul.Token;
  23. op.TcpEndPoint = options.Consul.TcpEndPoint;
  24. }));
  25. var checkOptions = options.Consul.HttpHeathCheck;
  26. var checkUrl = $"http://{healthHost}:{PassportConfig.GetCurrentPort()}{checkOptions.Path}";
  27. new ConsulBuilder(consulClient)
  28. .AddHttpHealthCheck(checkUrl, checkOptions.TimeOunt, checkOptions.Interval)
  29. .RegisterService(options.ServiceName, healthHost, PassportConfig.GetCurrentPort(), options.Consul.Tags)
  30. .Wait();
  31. }
  32. return services;
  33. }

ConsulBuilder.cs 参考晓晨大佬

  1. public class ConsulBuilder
  2. {
  3. private readonly ConsulClient _client;
  4. private readonly List<AgentServiceCheck> _checks = new List<AgentServiceCheck>();
  5. public ConsulBuilder(ConsulClient client)
  6. {
  7. _client = client;
  8. }
  9. public ConsulBuilder AddHealthCheck(AgentServiceCheck check)
  10. {
  11. _checks.Add(check);
  12. return this;
  13. }
  14. /// <summary>
  15. ///
  16. /// </summary>
  17. /// <param name="url"></param>
  18. /// <param name="timeout">unit: second</param>
  19. /// <param name="interval">check interval. unit: second</param>
  20. /// <returns></returns>
  21. public ConsulBuilder AddHttpHealthCheck(string url, int timeout = 10, int interval = 10)
  22. {
  23. _checks.Add(new AgentServiceCheck()
  24. {
  25. DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(timeout * 3),
  26. Interval = TimeSpan.FromSeconds(interval),
  27. HTTP = url,
  28. Timeout = TimeSpan.FromSeconds(timeout)
  29. });
  30. PassportConsole.Success($"[Consul]Add Http Healthcheck Success! CheckUrl:{url}");
  31. return this;
  32. }
  33. /// <summary>
  34. ///
  35. /// </summary>
  36. /// <param name="endpoint">GPRC service address.</param>
  37. /// <param name="grpcUseTls"></param>
  38. /// <param name="timeout">unit: second</param>
  39. /// <param name="interval">check interval. unit: second</param>
  40. /// <returns></returns>
  41. public ConsulBuilder AddGRPCHealthCheck(string endpoint, bool grpcUseTls = false, int timeout = 10, int interval = 10)
  42. {
  43. _checks.Add(new AgentServiceCheck()
  44. {
  45. DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20),
  46. Interval = TimeSpan.FromSeconds(interval),
  47. GRPC = endpoint,
  48. GRPCUseTLS = grpcUseTls,
  49. Timeout = TimeSpan.FromSeconds(timeout)
  50. });
  51. PassportConsole.Success($"[Consul]Add GRPC HealthCheck Success! Endpoint:{endpoint}");
  52. return this;
  53. }
  54. public async Task RegisterService(string name, string host, int port, string[] tags)
  55. {
  56. var registration = new AgentServiceRegistration()
  57. {
  58. Checks = _checks.ToArray(),
  59. ID = $"{name}_{host}:{port}",
  60. Name = name,
  61. Address = host,
  62. Port = port,
  63. Tags = tags
  64. };
  65. await _client.Agent.ServiceRegister(registration);
  66. PassportConsole.Success($"[Consul]Register Service Success! Name:{name} ID:{registration.ID}");
  67. AppDomain.CurrentDomain.ProcessExit += async (sender, e) =>
  68. {
  69. PassportConsole.Information($"[Consul] Service Deregisting .... ID:{registration.ID}");
  70. await _client.Agent.ServiceDeregister(registration.ID);
  71. };
  72. }
  73. /// <summary>
  74. /// 移除服务
  75. /// </summary>
  76. /// <param name="serviceId"></param>
  77. public async Task Deregister(string serviceId)
  78. {
  79. await _client?.Agent?.ServiceDeregister(serviceId);
  80. }
  81. }

逻辑简单,确定自己需要用的是注册服务功能,调Consul Api 注册,然后程序退出的时候注销consul的服务就行;

Asp.net core WebApi 自动注册路由规则到kong

通过Consul

安装nuget包

  1. #已安装跳过
  2. Install-Package Passport.Infrastructure -Version 0.1.4.7-preview-1

**加入配置appsettings.json**

guid顺便去https://www.guidgen.com/ 生成一个

  1. "Kong": {
  2. //"Disable": false, //true=禁用
  3. "Host": "http://172.16.1.30:8001",
  4. "Services": [
  5. {
  6. "Id": "72e21af8-283f-44c4-a766-53de8bb35c21", //guid
  7. "Name": "service-autoapi",
  8. "Retries": 5,
  9. "Protocol": "http",
  10. "Host": "DemoApi31.service.consul",
  11. "Port": 0,
  12. "Path": null,
  13. "Connect_timeout": 60000, //毫秒
  14. "Write_timeout": 60000,
  15. "Read_timeout": 60000,
  16. "Tags": null
  17. }
  18. ],
  19. "Routes": [
  20. {
  21. "Id": "5370e1b7-6c43-442d-9a44-23c249f958f7",
  22. "Name": "route-autoapi",
  23. "Protocols": [ "http" ],
  24. "Methods": null,
  25. "Hosts": null,
  26. "Paths": [ "/autoapi" ],
  27. "Https_redirect_status_code": 307,
  28. "Regex_priority": 0,
  29. "Strip_path": true,
  30. "Preserve_host": false,
  31. "Tags": null,
  32. "Service": {
  33. "Id": "72e21af8-283f-44c4-a766-53de8bb35c21" //这个id跟关联的Services的id一致
  34. }
  35. }
  36. ]
  37. }

StartUp.cs ConfigureServices方法

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. ......
  4. services.AddConsul();
  5. services.RouteRegistToKong();
  6. }

启动程序

  1. dotnet DemoApi.Core3.1.dll --healthhost 172.16.1.30 --urls http://*:5003

验证

查看kong管理后台:

访问 http://172.16.1.30:8000/auto/api/values

大功告成。

不通过Consul,直接配置路由到kong

StartUp.cs ConfigureServices方法

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. ......
  4. //删掉这行services.AddConsul();
  5. services.RouteRegistToKong();
  6. }

配置变为

  1. "Kong": {
  2. //"Disable": false, //true=禁用
  3. "Host": "http://172.16.1.30:8001",
  4. "Services": [
  5. {
  6. "Id": "0f86015b-b170-4ada-b045-740ae7d77ed6", //guid
  7. "Name": "configupapi",
  8. "Retries": 5,
  9. "Protocol": "http",
  10. "Host": "configupapi",
  11. "Port": 0,
  12. "Path": null,
  13. "Connect_timeout": 60000, //毫秒
  14. "Write_timeout": 60000,
  15. "Read_timeout": 60000,
  16. "Tags": null
  17. }
  18. ],
  19. "Routes": [
  20. {
  21. "Id": "1be79a57-af87-43b0-a0a0-b7a6cc0c5ade",
  22. "Name": "configupapi",
  23. "Protocols": [ "http" ],
  24. "Methods": null,
  25. "Hosts": null,
  26. "Paths": [ "/configupapi" ],
  27. "Https_redirect_status_code": 307,
  28. "Regex_priority": 0,
  29. "Strip_path": true,
  30. "Preserve_host": false,
  31. "Tags": null,
  32. "Service": {
  33. "Id": "0f86015b-b170-4ada-b045-740ae7d77ed6" //这个id跟Services的id一致
  34. }
  35. }
  36. ],
  37. "Upstream": {
  38. "Id": "8efd15af-df78-422f-97a0-9072fa7e7431",
  39. "Tags": [ "exampleapi", "v1.0" ],
  40. "Name": "configupapi",
  41. "Hash_on": "none",
  42. "Healthchecks": {
  43. "Active": {
  44. "Unhealthy": {
  45. "Http_statuses": [ 429, 500, 501, 502, 503, 504, 505 ],
  46. "Tcp_failures": 1,
  47. "Timeouts": 2,
  48. "Http_failures": 1,
  49. "Interval": 5
  50. },
  51. "Type": "http",
  52. "Http_path": "/healthcheck",
  53. "Timeout": 1,
  54. "Healthy": {
  55. "Successes": 1,
  56. "Interval": 20,
  57. "Http_statuses": [ 200, 302 ]
  58. },
  59. "Https_verify_certificate": true,
  60. "Concurrency": 1
  61. },
  62. "Passive": {
  63. "Unhealthy": {
  64. "Http_statuses": [ 429, 500, 501, 502, 503, 504, 505 ]
  65. },
  66. "Healthy": {
  67. "Http_statuses": [ 200, 201, 302 ]
  68. },
  69. "Type": "http"
  70. }
  71. },
  72. "Hash_on_cookie_path": "/",
  73. "Hash_fallback": "none",
  74. "Slots": 10000
  75. },
  76. "Target": {
  77. "Tags": [ "exampleapi", "v1.0" ],
  78. "Weight": 100
  79. }
  80. }

源码解析

  1. /// <summary>
  2. /// 路由注册到kong;
  3. /// </summary>
  4. /// <param name="services"></param>
  5. /// <returns></returns>
  6. public static IServiceCollection RouteRegistToKong(this IServiceCollection services)
  7. {
  8. if (!PassportConfig.GetBool("Kong:Disable"))
  9. {
  10. var konghost = PassportConfig.Get("Kong:Host") ?? throw new ArgumentNullException("Kong:Host cannot be null or empty!");
  11. var options = new KongClientOptions(HttpClientFactory.Create(), konghost);
  12. var client = new KongClient(options);
  13. services.AddSingleton<KongClient>(client);
  14. var upStream = PassportConfig.GetSection("Kong:Upstream").Get<UpStream>();
  15. var target = PassportConfig.GetSection("Kong:Target").Get<TargetInfo>();
  16. if (upStream != null && target != null)
  17. {
  18. upStream.Created_at = DateTime.Now;
  19. upStream = client.UpStream.UpdateOrCreate(upStream).Result;
  20. target.Target = $"{PassportConfig.GetHealthHost()}:{PassportConfig.GetCurrentPort()}";
  21. target.Id = PassportTools.GuidFromString($"{Dns.GetHostName()}{target.Target}");
  22. target.Created_at = DateTime.Now;
  23. target.UpStream = new TargetInfo.UpStreamId { Id = upStream.Id.Value };
  24. client.Target.Add(target).Wait();
  25. PassportConsole.Success($"[Kong]UpStream registered:{upStream.Name} Target:{target.Target}");
  26. // app.UseKongHealthChecks(upStream, onExecuter);
  27. }
  28. var kongServices = PassportConfig.GetSection("Kong:Services").Get<ServiceInfo[]>();
  29. var kongRoutes = PassportConfig.GetSection("Kong:Routes").Get<RouteInfo[]>();
  30. if (kongServices?.Length > 0 == true)
  31. {
  32. foreach (var item in kongServices)
  33. {
  34. item.Updated_at = DateTime.Now;
  35. item.Path = string.IsNullOrWhiteSpace(item.Path) ? null : item.Path;
  36. client.Service.UpdateOrCreate(item).Wait();
  37. PassportConsole.Success($"[Kong]Service registered:{item.Name}");
  38. }
  39. }
  40. if (kongRoutes?.Length > 0 == true)
  41. {
  42. foreach (var item in kongRoutes)
  43. {
  44. item.Updated_at = DateTime.Now;
  45. client.Route.UpdateOrCreate(item).Wait();
  46. PassportConsole.Success($"[Kong]Route registered:{item.Name}");
  47. }
  48. }
  49. }
  50. return services;
  51. }

  逻辑也简单,也是调用kong配置把本该手工配置的路由,分别调用upstream、service、route Api修改配置。有区别的是程序退出时不会去删对应的路由;

总结

  我在各技术博客都没有看到总结的比较好的kong+consul+asp.net core的集成文章,特此总结。期待您的点赞留意;

[参考]

https://docs.konghq.com/

https://www.cnblogs.com/stulzq/p/11942691.html

https://github.com/lianggx/Kong.Net

https://www.consul.io/docs

Api网关Kong集成Consul做服务发现及在Asp.Net Core中的使用的更多相关文章

  1. 使用Consul做服务发现的若干姿势

    从2016年起就开始接触Consul,使用的主要目的就是做服务发现,后来逐步应用于生产环境,并总结了少许使用经验.最开始使用Consul的人不多,为了方便交流创建了一个QQ群,这两年微服务越来越火,使 ...

  2. Consul做服务发现

    使用Consul做服务发现的若干姿势 https://www.cnblogs.com/bossma/p/9756809.html 从2016年起就开始接触Consul,使用的主要目的就是做服务发现,后 ...

  3. Go | Go 使用 consul 做服务发现

    Go 使用 consul 做服务发现 目录 Go 使用 consul 做服务发现 前言 一.目标 二.使用步骤 1. 安装 consul 2. 服务注册 定义接口 具体实现 测试用例 3. 服务发现 ...

  4. go-micro使用Consul做服务发现的方法和原理

    go-micro v4默认使用mdns做服务发现.不过也支持采用其它的服务发现中间件,因为多年来一直使用Consul做服务发现,为了方便和其它服务集成,所以还是选择了Consul.这篇文章将介绍go- ...

  5. .Netcore 2.0 Ocelot Api网关教程(4)- 服务发现

    本文介绍Ocelot中的服务发现(Service Discovery),Ocelot允许指定一个服务发现提供器,之后将从中寻找下游服务的host和port来进行请求路由.关于服务发现的详细介绍请点击. ...

  6. 使用Redis Stream来做消息队列和在Asp.Net Core中的实现

    写在前面 我一直以来使用redis的时候,很多低烈度需求(并发要求不是很高)需要用到消息队列的时候,在项目本身已经使用了Redis的情况下都想直接用Redis来做消息队列,而不想引入新的服务,kafk ...

  7. ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理

    ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...

  8. 如何在 ASP.NET Core 中构建轻量级服务

    在 ASP.NET Core 中处理 Web 应用程序时,我们可能经常希望构建轻量级服务,也就是没有模板或控制器类的服务. 轻量级服务可以降低资源消耗,而且能够提高性能.我们可以在 Startup 或 ...

  9. ASP.NET Core中的依赖注入(3): 服务的注册与提供

    在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...

随机推荐

  1. Mysql之Mycat读写分离及分库分表

    ## 什么是mycat ```basic 1.一个彻底开源的,面向企业应用开发的大数据库集群 2.支持事务.ACID.可以替代MySQL的加强版数据库 3.一个可以视为MySQL集群的企业级数据库,用 ...

  2. Day16_91_通过反射机制获取父类和父接口

    通过反射机制获取父类和父接口 * 代码: import java.net.InterfaceAddress; public class ReflectTest14 { public static vo ...

  3. golang面向对象分析

    说道面向对象(OOP)编程, 就不得不提到下面几个概念: 抽象 封装 继承 多态 其实有个问题Is Go An Object Oriented Language?, 随便谷歌了一下, 你就发现讨论这个 ...

  4. 解决mysql You can't specify target table for update in FROM clause错误

    mysql中You can't specify target table for update in FROM clause错误的意思是说,不能先select出同一表中的某些值,再update这个表( ...

  5. Mysql通配符的使用

    通配符的分类:%百分号通配符: 匹配任意字符,包括0个到多个_下划线通配符:表示只能匹配单个字符,不能多也不能少,就是一个字符.    escape:用来转义特定字符 [字符列]  :字符列中任何一个 ...

  6. 判断请求是否属于Ajax请求

    我们有时候需要根据请求类型来判断返回视图名称还是JSON数据,这里记录一个判断Ajax的工具类方便日后好找 通过传入Request对象获取头信息,根据头信息判断是否属于Ajax请求 public cl ...

  7. B - 抽屉 POJ - 2356 (容斥原理)

    The input contains N natural (i.e. positive integer) numbers ( N <= 10000 ). Each of that numbers ...

  8. Mysql 8.0安装

    1. 下载安装包至/usr/local目录下 下载地址:https://cdn.mysql.com/Downloads/MySQL-8.0/mysql-8.0.16-el7-x86_64.tar.gz ...

  9. hdu4280 最大流DINIC

    题意:       x最小的到x最大的点同一时间的最大运输量. 思路:       裸的最大流,不解释,注意一点,记得加上防爆栈. #pragma comment(linker, "/STA ...

  10. 使用EasySYS搭建驱动开发基本框架

    提供EasySYS的下载地址:http://bbs.pediy.com/showthread.php?p=956643,看雪上有提供下载,自行百度. EasySYS你能够帮我们快速的搭建驱动的开发框架 ...