.Net Core使用Consul+Ocelot搭建简易微服务项目

时代在变,技术也在更新迭代。从传统的单体应用架构到现在的分布式集群架构,在技术的学习上真的是一点都不能松懈。

网上关于微服务与Consul的话题太多了,我在这里不做过多描述。

其实就是在微服务中我们可以利用Consul可以实现服务的发现、治理、健康检查等...

用它先下载它:

我此番在windows下操作,打开下载的Consul所在文件夹,输入 consul.exe agent -dev

Consul的默认启动端口为8500,如果能正常显示页面则启动成功。

 新建解决方案,建立一个.Net Core MVC的项目和一个.Net Core WebApi的项目。 安装NuGet包Consul

首先Api端服务实例启动时需到Consul中进行服务注册,Web Client直接与Consul进行连接,从Consul中拿到服务实例并配置策略及发送http请求等。

如图所示:

 Consul每隔一段时间就会调用一次注册的服务实例进行健康检查。

在Api项目中新建一个IConfiguration的扩展方法:

public static void ConsulExtend(this IConfiguration configuration)
{
    ConsulClient client = new ConsulClient(m =>
    {
        m.Address = new Uri("http://localhost:8500/");
        m.Datacenter = "dc1";
    });
    //启动的时候在consul中注册实例服务
    //在consul中注册的ip,port
    string ip = configuration["ip"];
    int port = int.Parse(configuration["port"]);
    int weight = string.IsNullOrWhiteSpace(configuration["weight"]) ? 1 : int.Parse(configuration["weight"]);
    client.Agent.ServiceRegister(new AgentServiceRegistration()
    {
        ID = "service" + Guid.NewGuid(),//唯一的
        Name = "MicroserviceAttempt",//组(服务)名称
        Address = ip,
        Port = port,//不同的端口=>不同的实例
        Tags = new string[] { weight.ToString() },//标签
        Check = new AgentServiceCheck()//服务健康检查
        {
            Interval = TimeSpan.FromSeconds(12),//间隔12s一次 检查
            HTTP = $"http://{ip}:{port}/Api/Health/Index",
            Timeout = TimeSpan.FromSeconds(5),//检测等待时间
            DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20)//失败后多久移除
        }
    });
    Console.WriteLine($"{ip}:{port}--weight:{weight}");
}

心跳检查的接口:

[ApiController]
[Route("api/[controller]/[action]")]
public class HealthController : Controller
{
    readonly IConfiguration _configuration;
    public HealthController(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    [HttpGet]
    public IActionResult Index()
    {
        //心跳,consul会每隔几秒调一次
        Console.WriteLine($"{ _configuration["port"]} Invoke");
        return Ok();
    }
}

在Startup类中的Configure方法加入:

//启动时注册,且注册一次
this.Configuration.ConsulExtend();

将api项目启动(三个端口)

dotnet ServicesInstances.dll --urls="http://*:5726" --ip="127.0.0.1" --port=5726
dotnet ServicesInstances.dll --urls="http://*:5727" --ip="127.0.0.1" --port=5727
dotnet ServicesInstances.dll --urls="http://*:5728" --ip="127.0.0.1" --port=5728

接着是web端,新建控制器:

public class UserController : Controller
{readonly HttpSender _httpSender;
    public UserController(HttpSender httpSender)
    {
        _httpSender = httpSender;
    }
    //暂不考虑线程安全
    private static int index = 0;
    public async Task<IActionResult> Index()
    {
        #region nginx版 只知道nginx地址就行了
        //var str = await _httpSender.InvokeApi("http://localhost:8088/api/User/GetCustomerUser");
        #endregion

        #region consul
        //new一个consul实例
        ConsulClient client = new ConsulClient(m =>
        {
            new Uri("http://localhost:8500/");
            m.Datacenter = "dc1";
        });
        //与consul进行通信(连接),得到consul中所有的服务实例
        var response = client.Agent.Services().Result.Response;
        string url = "http://MicroserviceAttempt/api/User/GetCustomerUser";
        Uri uri = new Uri(url);
        string groupName = uri.Host;
        AgentService agentService = null;//服务实例
        var serviceDictionary = response.Where(m => m.Value.Service.Equals(groupName, StringComparison.OrdinalIgnoreCase)).ToArray();//找到的全部服务实例
        //{
        //    agentService = serviceDictionary[0].Value;
        //}
        {
            //轮询策略=>达到负载均衡的目的
            agentService = serviceDictionary[index++ % 3].Value;
        }
        {
            //平均策略(随机获取索引--相对平均)=>达到负载均衡的目的
            agentService = serviceDictionary[new Random(index++).Next(0, serviceDictionary.Length)].Value;
        }
        {
            //权重策略,给不同的实例分配不同的压力,注册时提供权重
            List<KeyValuePair<string, AgentService>> keyValuePairs = new List<KeyValuePair<string, AgentService>>();
            foreach (var item in keyValuePairs)
            {
                int count = int.Parse(item.Value.Tags?[0]);//在服务注册的时候给定权重数量
                for (int i = 0; i < count; i++)
                {
                    keyValuePairs.Add(item);
                }
            }
            agentService = keyValuePairs.ToArray()[new Random(index++).Next(0, keyValuePairs.Count())].Value;
        }
        url = $"{uri.Scheme}://{agentService.Address}:{agentService.Port}{uri.PathAndQuery}";
        string content = await _httpSender.InvokeApi(url);
        #endregion
        return View(JsonConvert.DeserializeObject<CustomerUser>(content));
    }
}
public class HttpSender
{
    public async Task<string> InvokeApi(string url)
    {
        using (HttpClient client = new HttpClient())
        {
            HttpRequestMessage message = new HttpRequestMessage();
            message.Method = HttpMethod.Get;
            message.RequestUri = new Uri(url);
            var result = client.SendAsync(message).Result;
            string content = result.Content.ReadAsStringAsync().Result;
            return content;
        }
    }
}

启动web项目,访问User-Index这个视图,会轮询不同的服务实例。

但是这样做不好,客户端都需要和Consul进行连接,拿到所有的服务实例,直接和服务实例进行交互,服务实例就暴露了--所以需要网关。

网关将服务实例与客户端进行隔离,是所有Api请求的入口。因此可以统一鉴权。当然微服务网关的作用有很多,大家可自行百度了解。

新建一个网关的项目,请求先到达网关,再由网关分发请求到不同的实例。如图:

 引入NuGet包:Ocelot、Ocelot.Provider.Consul

修改Startup类:

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(IServiceCollection services)
    {
        services.AddOcelot().AddConsul();
        //services.AddControllers();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        //将默认的请求管道全部丢掉
        app.UseOcelot();
        //if (env.IsDevelopment())
        //{
        //    app.UseDeveloperExceptionPage();
        //}

        //app.UseHttpsRedirection();

        //app.UseRouting();

        //app.UseAuthorization();

        //app.UseEndpoints(endpoints =>
        //{
        //    endpoints.MapControllers();
        //});
    }
}

新建一个json文件,用于配置策略等...

{//*************************单地址多实例负载均衡+Consul*****************************
  "Routes": [
    {
      //GeteWay转发=>Downstream
      "DownstreamPathTemplate": "/api/{url}", //服务地址--url变量
      "DownstreamScheme": "http",
      //http://localhost:6299/T5/User/GetCustomerUser
      "UpstreamPathTemplate": "/T5/{url}", //网关地址--url变量 冲突的还可以加权重Priority
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "UseServiceDiscovery": true, //使用服务发现
      "ServiceName": "MicroserviceAttempt", //Consul服务名称
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //轮询  //"LeastConnection":最少连接数服务器   "NoloadBalance":不负载均衡     "CookieStickySession":会话粘滞
      }
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://127.0.0.1:6299",
    "ServiceDiscoveryProvider": {
      "Host": "localhost",
      "Port": 8500,
      "Type": "Consul"//由Consul提供服务发现,每次请求去Consul
    }
    //"ServiceDiscoveryProvider": {
    //  "Host": "localhost",
    //  "Port": 8500,
    //  "Type": "PollConsul", //由Consul提供服务发现,每次请求去Consul
    //  "PollingInterval": 1000//轮询Consul,评率毫秒--down是不知道的
    //}
  }
  //*************************单地址多实例负载均衡+Consul*****************************
}

启动网关(项目)服务:

dotnet GateWay-Ocelot.dll --urls="http://*:6299" --ip="127.0.0.1" --port=6299

调用服务接口:

每请求一次就轮询不同的服务实例,达到负载均衡。

到此就结尾了。如有不当的地方,请谅解,希望能帮到大家。

代码已上传至我的github:

Smiling Face with Smiling Eyes on Microsoft Windows 10 May 2019 Update

猜你喜欢

转载自www.cnblogs.com/zhangnever/p/13192070.html