微服务之:从零搭建ocelot网关和consul集群

  微服务中有关键的几项技术,其中网关和服务服务发现,服务注册相辅相成。
  
  首先解释几个本次教程中需要的术语
  
  网关 Gateway(API GW / API 网关),顾名思义,是企业 IT 在系统边界上提供给外部访问内部接口服务的统一入口,简化了外部由于多服务协同完成任务时的繁琐配置。网关组件有Kong,ocelot,
  
  服务发现:通过网关访问内部各个微服务,网关要找到所需服务的过程称为服务发现
  
  服务注册:既然有服务发现,前提是要把所需服务提前“录入”,这个录入的过程称为服务注册。服务注册可配置文件(人肉方式不推荐),也可用服务注册组件如Consul或者Eureka等等(推荐)
  
  搭建Consul集群(Windows
  
  下载下来就是一个可执行文件Consul.exe
  
  Consul有两种代理模式,一种server,一种client,官方建议Server端达到3台以上才可高可用,但不要太多,太多会给集群间数据同步造成压力,client数量不限。
  
  多个server端之间会选择出一个leader,当一个server的leader宕机则会从其他server端”投票“选择新的leader
  
  实践
  
  这里server我们用2台实验
  
  192.168.74.55
  
  192.168.74.54
  
  1台Client
  
  192.168.74.161
  
  consul启动有两种方式一种是命令行,一种是配置文件的方式。
  
  命令行方式启动一个consul的server端
  
  consul agent -server -ui -bootstrap-expect 2 -data-dir opt/consul/data -node ServerMaster -bind 192.168.74.55 -client 192.168.74.55
  
  关键参数说明
  
  -server:server模式启动
  
  -ui :开启ui界面(consul.exe内部带了GUI图形界面操作)
  
  -bootstrap-expect 2:server端到2个时集群生效
  
  -data-dir:consul产生的文件路径(consul自己会产生一下数据存储的位置)
  
  -node:此节点名称
  
  -bind:集群内部通信地址,默认0.0.0.0
  
  -client:此节点绑定的通讯地址
  
  以上只是关键参数,以下是完整参数说明:
  
  但是命令启动很繁琐,所以推荐下面的配置文件的方式启动
  
  在consul同文件夹下建立一个server.json的配置文件
  
  {
  
  "datacenter": "dc1",
  
  "data_dir": "opt/consul/data",
  
  "node_name": "consul-server01",
  
  "server": true,
  
  "bootstrap_expect": 2,
  
  "bind_addr": "192.168.74.55",
  
  "client_addr": "192.168.74.55",
  
  "ui":true
  
  }
  
  为了快速启动,再建立一个bat批处理文件runconsul.bat
  
  consul agent -config-dir server.json
  
  pause
  
  双击runconsul.bat启动consul
  
  在192.168.74.54服务器开启一个server端继续以上操作。
  
  命令方式启动
  
  consul agent -server -ui -data-dir opt/consul/data -node Server01 -bind 192.168.74.54 -client 192.168.74.54 -join=192.168.74.55
  
  -join将192.168.74.54加入到192.168.74.55服务器
  
  配置文件方式:
  
  {
  
  "datacenter": "dc1",
  
  "data_dir": "opt/consul/data",
  
  "node_name": "consul-server2",
  
  "server": true,
  
  "bind_addr": "192.168.74.54",
  
  "client_addr": "192.168.74.54",
  
  "ui":true,
  
  "retry_join": ["192.168.74.55"],
  
  "retry_interval": "30s",
  
  "rejoin_after_leave": true,
  
  "start_join":["192.168.74.55"]
  
  }
  
  在192.168.74.161服务器开启一个consul的client端
  
  命令方式:
  
  consul agent -ui -data-dir opt/consul/data -node ServerSlave  -bind 192.168.74.161 -client 192.168.74.161 -join 192.168.74.55
  
  配置文件方式:
  
  {
  
  "datacenter": "dc1",
  
  "data_dir": "opt/consul/data",
  
  "node_name": "consul-client01",
  
  "server": false,
  
  "bind_addr": "192.168.74.161",
  
  "client_addr": "192.168.74.161",
  
  "ui":true,
  
  "retry_join": ["192.168.74.55"],
  
  "retry_interval": "30s",
  
  "rejoin_after_leave": true,
  
  "start_join":["192.168.74.55"]
  
  }
  
  效果
  
  简单Consul集群到这里就搭建成功,只要访问三台服务器任意一个都可数据同步,演示:
  
  netcore集成Consul服务注册
  
  服务注册关键代码
  
  首先新建一个ConsulClient的类库
  
  ConsulRegister.csproj所需组件如下:
  
  
  
  
  
  netstandard2.0
  
  

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

  
  

  
  ServiceDiscoveryOptions.cs
  
  using System;
  
  using System.Collections.Generic;
  
  using System.Text;
  
  namespace ConsulRegister
  
  {
  
  ///


  
  /// 服务治理第三方组件Consul相关配置参数
  
  ///

  
  public class ServiceDiscoveryOptions
  
  {
  
  public string ServiceName { get; set; }
  
  public ConsulOptions Consul { get; set; }
  
  }
  
  public class ConsulOptions
  
  {
  
  public string HttpEndPoint { get; set; }
  
  }
  
  }
  
  RegisterToConsulExtension.cs
  
  using Consul;
  
  using Microsoft.AspNetCore.Builder;
  
  using Microsoft.AspNetCore.Hosting;
  
  using Microsoft.AspNetCore.Hosting.Server.Features;
  
  using Microsoft.AspNetCore.Http;
  
  using Microsoft.AspNetCore.Http.Features;
  
  using Microsoft.Extensions.Configuration;
  
  using Microsoft.Extensions.DependencyInjection;
  
  using Microsoft.Extensions.Options;
  
  using System;
  
  using System.Linq;
  
  using System.Net;
  
  using System.Net.NetworkInformation;
  
  using System.Net.Sockets;
  
  namespace ConsulRegister
  
  {
  
  public static class RegisterToConsulExtension
  
  {
  
  ///
  
  /// Add Consul
  
  /// 添加consul
  
  ///

  
  ///
  
  ///
  
  ///
  
  public static IServiceCollection AddConsul(this IServiceCollection services, IConfiguration configuration)
  
  {
  
  // configuration Consul register address
  
  //配置consul注册地址
  
  services.Configure(configuration.GetSection("ServiceDiscovery"));
  
  //configuration Consul client
  
  //配置consul客户端
  
  services.AddSingleton(sp => new Consul.ConsulClient(config =>
  
  {
  
  var consulOptions = sp.GetRequiredService>().Value;
  
  if (!string.IsNullOrWhiteSpace(consulOptions.Consul.HttpEndPoint))
  
  {
  
  config.Address = new Uri(consulOptions.Consul.HttpEndPoint);
  
  }
  
  }));
  
  return services;
  
  }
  
  ///
  
  /// use Consul
  
  /// 使用consul
  
  /// The default health check interface format is http://host:port/HealthCheck
  
  /// 默认的健康检查接口格式是 http://host:port/HealthCheck
  
  ///

  
  ///
  
  ///
  
  public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
  
  {
  
  IConsulClient consul = app.ApplicationServices.GetRequiredService();
  
  IApplicationLifetime appLife = app.ApplicationServices.GetRequiredService();
  
  IOptions serviceOptions = app.ApplicationServices.GetRequiredService>();
  
  var features = app.Properties["server.Features"] as FeatureCollection;
  
  var port = new Uri(features.Get()
  
  .Addresses
  
  .FirstOrDefault()).Port;
  
  Console.ForegroundColor = ConsoleColor.Blue;
  
  Console.WriteLine($"application port is :{port}");
  
  var addressIpv4Hosts = NetworkInterface.GetAllNetworkInterfaces()
  
  .OrderByDescending(c => c.Speed)
  
  .Where(c => c.NetworkInterfaceType != NetworkInterfaceType.Loopback && c.OperationalStatus == OperationalStatus.Up);
  
  foreach (var item in addressIpv4Hosts)
  
  {
  
  var props = item.GetIPProperties();
  
  //this is ip for ipv4
  
  //这是ipv4的ip地址
  
  var firstIpV4Address = props.UnicastAddresses
  
  .Where(c => c.Address.AddressFamily == AddressFamily.InterNetwork)
  
  .Select(c => c.Address)
  
  .FirstOrDefault().ToString();
  
  var serviceId = $"{serviceOptions.Value.ServiceName}_{firstIpV4Address}:{port}";
  
  var httpCheck = new AgentServiceCheck()
  
  {
  
  DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),
  
  Interval = TimeSpan.FromSeconds(30),
  
  //this is default health check interface
  
  //这个是默认健康检查接口
  
  HTTP = $"{Uri.UriSchemeHttp}://{firstIpV4Address}:{port}/HealthCheck",
  
  };
  
  var registration = new AgentServiceRegistration()
  
  {
  
  Checks = new[] { httpCheck },
  
  Address = firstIpV4Address.ToString(),
  
  ID = serviceId,
  
  Name = serviceOptions.Value.ServiceName,
  
  Port = port
  
  };
  
  consul.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
  
  //send consul request after service stop
  
  //当服务停止后想consul发送的请求
  
  appLife.ApplicationStopping.Register(() =>
  
  {
  
  consul.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();
  
  });
  
  Console.ForegroundColor = ConsoleColor.Blue;
  
  Console.WriteLine($"health check service:{httpCheck.HTTP}");
  
  }
  
  //register localhost address
  
  //注册本地地址
  
  var localhostregistration = new AgentServiceRegistration()
  
  {
  
  Checks = new[] { new AgentServiceCheck()
  
  {
  
  DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),
  
  Interval = TimeSpan.FromSeconds(30),
  
  HTTP = $"{Uri.UriSchemeHttp}://localhost:{port}/HealthCheck",
  
  } },
  
  Address = "localhost",
  
  ID = $"{serviceOptions.Value.ServiceName}_localhost:{port}",
  
  Name = serviceOptions.Value.ServiceName,
  
  Port = port
  
  };
  
  consul.Agent.ServiceRegister(localhostregistration).GetAwaiter().GetResult();
  
  //send consul request after service stop
  
  //当服务停止后想consul发送的请求
  
  appLife.ApplicationStopping.Register(() =>
  
  {
  
  consul.Agent.ServiceDeregister(localhostregistration.ID).GetAwaiter().GetResult();
  
  });
  
  app.Map("/HealthCheck", s =>
  
  {
  
  s.Run(async context =>
  
  {
  
  await context.Response.WriteAsync("ok");
  
  });
  
  });
  
  return app;
  
  }
  
  }
  
  }
  
  再新建一个.netcore的webapi项目WebA,并且引用ConsulRegister项目
  
  在WebA项目中的Startup.cs文件中加入Consul服务
  
  public void ConfigureServices(IServiceCollection services)
  
  {
  
  services.AddConsul(Configuration);
  
  services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
  
  }
  
  // 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();
  
  }
  
  app.UseConsul();
  
  app.UseMvc();
  
  }
  
  在WebA项目的appsettings.json配置文件中加入以下Consul服务端配置
  
  {
  
  "Logging": {
  
  "LogLevel": {
  
  "Default": "Warning"
  
  }
  
  },
  
  "AllowedHosts": "*",
  
  "ServiceDiscovery": {
  
  "ServiceName": "A",
  
  "Consul": {
  
  "HttpEndpoint": "http://192.168.74.161:8500"
  
  }
  
  }
  
  }
  
  这里服务注册就算完成
  
  Ocelot网关搭建
  
  接下来继续Ocelot借助于Consul实现服务发现
  
  新建项目Ocelot.Gateway
  
  将以下依赖加入Ocelot.Gateway.csproj中:
  
  
  
  
  
  netcoreapp2.1
  
  

  
  
  
  
  
  
  
  
  
  

  
  
  
  
  
  PreserveNewest
  
  

  
  

  
  

  
  新建ocelot.json文件
  
  {
  
  "ReRoutes": [
  
  {
  
  "UseServiceDiscovery": true,
  
  "DownstreamPathTemplate": "/{url}",
  
  "DownstreamScheme": "http",
  
  "ServiceName": "A",
  
  "LoadBalancerOptions": {
  
  "Type": "RoundRobin"
  
  },
  
  "UpstreamPathTemplate": "/a/{url}",
  
  "UpstreamHttpMethod": [ "Get", "Post" ],
  
  "ReRoutesCaseSensitive": false
  
  }
  
  ],
  
  "GlobalConfiguration": {
  
  // 使用Consul服务治理
  
  "ServiceDiscoveryProvider": {
  
  "Host": "192.168.74.161",
  
  "Port": 8500,
  
  "ConfigurationKey": "Oceolot_A" //存储在Consul上的Key
  
  }
  
  }
  
  }
  
  修改Startup.www.gcyl159.com/  cs文件如下:
  
  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(www.michenggw.com IServiceCollection services)
  
  {
  
  services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
  
  services.AddOcelot(
  
  new ConfigurationBuilder()
  
  .AddJsonFile("ocelot.json", optional: false, reloadOnChange: true).Build())
  
  .AddConsul()
  
  .AddConfigStoredInConsul(www.dasheng178.com);
  
  }
  
  // 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.UseHsts();
  
  }
  
  app.UseHttpsRedirection();
  
  app.UseOcelot(www.haom178.com).Wait();
  
  }
  
  }
  
  发布WebA后复制两份分别启动
  
  dotnet WebA.dll --urls="http://0.0.0.0:2001"
  
  dotnet WebA.dll --urls="http://0.0.0.0:2002"
  
  到这里相当于2001和2002程序简单集群了一下
  
  可以发现日志中有 http://192.168.74.161:2002/HealthCheck调用信息:
  
  这其实是consul进行健康检查进行的调用。
  
  启动多个程序后,打开浏览器打开Consuld界面会发现注册了两个服务
  
  这里ocelot网关和consul的服务注册和发现就算初步集成。
  
  如果生成环境是windows的情况,将consul做成windwos服务即可
  
  sc create "ConsulServer" binPath="F:\XXX\consul.exe agent -config-dir XXX.json"
  
  生产环境是linux则借助systemd做成守护进程即可
  
  目前集群搭建成功,但是连接的话如果指定某个端点的ip进行连接,端点宕机,就会导致网关一样无法连接consul进行服务发现。所以还需进行配置暴露一个端点让客户端连接,配置详情:https://www.consul.io/docs/connect/configuration.html
  
  不过也可以做成虚拟ip进行多台consul的负载。客户端连接虚拟ip即可

你可能感兴趣的:(微服务之:从零搭建ocelot网关和consul集群)