庐山真面目之三微服务架构Consul版本实现

一、简介

          在上一篇文章《庐山真面目之二微服务架构NGINX版本实现》中,我们已经探讨了如何搭建基于Nginx 网关的微服务架构,可能很多人感觉很简单。没错,的确很简单,因为我的每篇文章只会解决一个问题,这样更单纯、更精准,微服务架构也是这样演化的。如果我们把所有的东西都放在一篇文章里,太多了,不利于学习和查看。我们看完上一篇文章后,知道了基于Nginx网关实现的微服务架构是有缺点的,而且这个缺点也是很致命的,它不能做到服务的注册和发现。我们为了解决这个问题,今天我们引入另外一个概念,服务注册发现组件:Consul,当然,ETCD、Zookeeper也可以做到,但是他们不再我们讨论范围之内。



  1、说明

      我先说明一下,这个实现的版本只过是测试版本,以后会把相关的技术点都增加上去,我们一步一步的演化而来,如果是大牛,就可以直接跳过,因为这些东西相对于您来说,这个太简单了。特别说明,这里的所有代码都经过测试,所以大家可以放心使用。



  2、开发环境

      
以下就是开发环境,不用多说,都很简单,一看就知道。

     (1)、开发工具:Visual
Studio 2019

     (2)、开发语言:C#

     (3)、开发平台:Net
Core3.1,跨平台。

     (4)、服务注册:Consul,服务注册、发现中心



  3、我们的目标

      今天我们要解决的问题是如何做到服务的注册和发现,其他问题,比如:Consul 本身的集群的问题暂时就不考虑,我们会一步一步的演变下去。

            

二、Consul注册、发现服务组件的简介。
             

           在这里我们只是做简单的介绍,不会详细讨论它的所有技术点,没有那么大的篇幅,再说这样做也就跑题了。如果大家想去学习Consul的有关内容,地址我已经贴出来了,大家可以自行恶补。

           Consul是一个服务网格(微服务间的 TCP/IP,负责服务之间的网络调用、限流、熔断和监控)解决方案,它是一个一个分布式的,高度可用的系统,而且开发使用都很简便。它提供了一个功能齐全的控制平面,主要特点是:服务发现、健康检查、键值存储、安全服务通信、多数据中心。

         上面的一幅图就说明了Consul的工作原理。S-A是使用服务的客户端,他不用关系有多少个服务,只要它需要的时候,他就会向Consul服务中心发出请求,Consul中心就会把和服务名称(在Consul中的名称,也可以说是服务的类别名称)匹配的、在此注册的所有服务实例的地址信息全部返回给S-A客户端,然后客户端,可以根据自己的情况,决定调用的具体策略,可以轮训、可以权重、可以随机,当然其他方式也可以,但是这些工作必须由使用服务的客户端S-A自行解决。

          S-B-1、S-B-2、S-B-3等是在Consul中心注册的服务实例,包括服务实例的地址、端口号、ID名称、服务类别名称等信息。这些服务实例,每个服务实例都有自己的ID名称,多个服务实例可以取一个相同的名称,其实,这个名称是用于分类的。Consul中心查找的时候就是通过这个名称来查找的。这些服务实例,在应用程序一开始的时候就开始注册,并且只注册一次。

          所有的服务实例都注册成功了,时间一分一秒的过去,一个小时之后或者一段时候之后服务还存在吗?我们如何判断呢?这就涉及到我们要谈的第三个方面,健康监测,英文叫:Health Check。Consul中心为我们提供了现成的接口,可以定义针对服务的健康检查,这个健康检查是一个完整的策略,包括:如果检查失败,多久注销服务;服务检查的时间间隔;还有调用检查的超时时间等。这样就能为我们提供一个比较完备的服务注册、发现和检测的解决方案。

      总结如下:

          1
、服务自动注册,可以发现新服务。

          2
、健康检测,可以过滤掉线服务。

          3
、客户端要完成服务调用逻辑,比较麻烦,没有Nginx
方便。


  
Consul文档:https://www.consul.io/docs
  
Consul官网:https://www.consul.io

三、微服务架构Consul版本实现(简约版)

      这个系列是有前后关系的,每篇文章都会解决一个特定的问题,Nginx网关的问题是可以解决负载均衡,可以做到服务掉线的检测,但是没有办法做到新增服务的自动发现功能。今天要解决的问题就是如何做到服务的注册和发现,相关组件的高可用和伸缩性暂时就不考了,还有一点,需要说明,每个项目都很简单,代码也做到尽量的最少,因为我们关注的是微服务的架构,所以业务逻辑就很简单。废话少说,我们开始吧。



      1、下载 Consul 服务组件。

            官网下载:https://www.consul.io/downloads

                       

            Consul下载成功后,放在没有包含中文的目录里面,这是我的个人的习惯。

            我的运行目录:D:\Programs\MicroServices\Consul_1.8.4_windows_amd64
              

           

      2、配置 Consul 服务组件。

            切换到当前目录,执行如下命令:Consul
agent –dev

              

            我们也可以通过浏览器查看 Consul服务中心是否启动成功,地址:http://localhost:8500,如果成功,效果如下:

                  



      3、建立项目,并为项目引入 Consul包。

            (1)、PatrickLiu.MicroService.Client(ASPNETCORE
MVC),客户端项目。


                     这个项目很简单,都是建立项目后,几乎没有修改任何代码,有修改的地方都会贴出来。
          
          1)、HomeController类型的代码,我只会贴出必须的代码。
            

  1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Net.Http;
5 using Consul;
6 using Microsoft.AspNetCore.Mvc;
7 using Microsoft.Extensions.Logging;
8 using PatrickLiu.MicroService.Interfaces;
9 using PatrickLiu.MicroService.Models;
10
11 namespace PatrickLiu.MicroService.Client.Controllers
12 {
13 public class HomeController : Controller
14 {
15 private readonly ILogger<HomeController> _logger;
16 private readonly IUserService _userService;
17 private static int index;
18
19 /// <summary>
20 /// 初始化该类型的新实例。
21 /// </summary>
22 /// <param name="logger">注入日志对象。</param>
23 /// <param name="userService">注入用户服务对象。</param>
24 public HomeController(ILogger<HomeController> logger, IUserService userService)
25 {
26 _logger = logger;
27 _userService = userService;
28 }
29
30 /// <summary>
31 /// 首页。
32 /// </summary>
33 /// <returns></returns>
34 public IActionResult Index()
35 {
36 #region 分布式架构
37
38 #region 通过 Consul 服务发现来执行服务实例。
39
40 //发现服务
41 string url = "http://PatrickLiuService/api/users/all";
42
43 ConsulClient client = new ConsulClient(config =>
44 {
45 config.Address = new Uri("http://localhost:8500/");
46 config.Datacenter = "dc1";
47 });
48 var response = client.Agent.Services().Result.Response;
49 foreach (var item in response)
50 {
51 Console.WriteLine("*************************************************************");
52 Console.WriteLine(item.Key);
53 var service = item.Value;
54 Console.WriteLine($"{service.Address}--{service.Port}--{service.Service}");
55 Console.WriteLine("*************************************************************");
56 }
57 Uri uri = new Uri(url);
58 string groupName = uri.Host;
59 AgentService agentService = null;
60
61 var serviceDirectory = response.Where(s => s.Value.Service.Equals(groupName, StringComparison.OrdinalIgnoreCase)).ToArray();
62 //{//1、 写死的
63 // agentService = serviceDirectory[0].Value;
64 //}
65 {
66 //2、轮询
67 if (index >= 10000)
68 {
69 index = 0;
70 }
71 agentService = serviceDirectory[index++ % serviceDirectory.Length].Value;
72 }
73 // {//3、随机
74 // var indexResult = new Random(index++).Next(0, serviceDirectory.Length);
75 // agentService = serviceDirectory[indexResult].Value;
76 //}
77 url = $"{uri.Scheme}://{agentService.Address}:{agentService.Port}{uri.PathAndQuery}";
78
79 #endregion
80
81 string content = InvokeAPI(url);
82 this.ViewBag.Users = Newtonsoft.Json.JsonConvert.DeserializeObject<IEnumerable<User>>(content);
83 Console.WriteLine($"This is {url} Invoke.");
84
85 #endregion
86
87 return View();
88 }
89
90 /// <summary>
91 /// 封装 HttpClient 实例,提供 Http 调用。
92 /// </summary>
93 /// <param name="url">http url 的地址。</param>
94 /// <returns>返回结束数据,格式:JSON。</returns>
95 public static string InvokeAPI(string url)
96 {
97 using (HttpClient client = new HttpClient())
98 {
99 HttpRequestMessage message = new HttpRequestMessage();
100 message.Method = HttpMethod.Get;
101 message.RequestUri = new Uri(url);
102 var result = client.SendAsync(message).Result;
103 string conent = result.Content.ReadAsStringAsync().Result;
104 return conent;
105 }
106 }
107 }
108 }

          2)、Index.cshtml 视图的代码。

 1 @{
2 ViewData["Title"] = "Home Page";
3 }
4
5 <div class="text-center">
6 <h1 class="display-4">Welcome</h1>
7 <ul>
8 @{
9
10 foreach (var item in ViewBag.Users)
11 {
12 <li>@item.ID --@item.Name --@item.Role </li>
13 }
14 }
15 </ul>
16 <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
17 </div>

          3)、为 Startup 类型ConfigurationServices 方法提供带类型注册。

1         /// <summary>
2 /// 注册服务到容器中。
3 /// </summary>
4 /// <param name="services"></param>
5 public void ConfigureServices(IServiceCollection services)
6 {
7 services.AddSingleton<IUserService, UserService>();
8 }


            (2)、PatrickLiu.MicroService.Interfaces(NETCORE 类库),定义接口。

                     这个项目很简单,都是建立项目后,几乎没有修改任何代码,有修改的地方都会贴出来。

                        

          IUserService.cs 类型的代码。
            

 1 using PatrickLiu.MicroService.Models;
2 using System.Collections.Generic;
3
4 namespace PatrickLiu.MicroService.Interfaces
5 {
6 /// <summary>
7 /// 用户服务的接口定义。
8 /// </summary>
9 public interface IUserService
10 {
11 /// <summary>
12 /// 查找指定主键的用户实例对象。
13 /// </summary>
14 /// <param name="id">用户的主键。</param>
15 /// <returns>返回查找到的用户实例对象。</returns>
16 User FindUser(int id);
17
18 /// <summary>
19 /// 获取所有用户的实例集合。
20 /// </summary>
21 /// <returns>返回所有的用户实例。</returns>
22 IEnumerable<User> UserAll();
23 }
24 }


            (3)、PatrickLiu.MicroService.Models(NETCORE 类库),定义实例类型。

                     这个项目很简单,都是建立项目后,几乎没有修改任何代码,有修改的地方都会贴出来。
                

                       

          User.cs 类型的代码。
            

 1 using System;
2
3 namespace PatrickLiu.MicroService.Models
4 {
5 /// <summary>
6 /// 用户模型。
7 /// </summary>
8 public class User
9 {
10 /// <summary>
11 /// 获取或者设置用户主键。
12 /// </summary>
13 public int ID { get; set; }
14
15 /// <summary>
16 /// 获取或者设置用户姓名。
17 /// </summary>
18 public string Name { get; set; }
19
20 /// <summary>
21 /// 获取或者设置用户账号名称。
22 /// </summary>
23 public string Account { get; set; }
24
25 /// <summary>
26 /// 获取或者设置用户密码。
27 /// </summary>
28 public string Password { get; set; }
29
30 /// <summary>
31 /// 获取或者设置用户的电子邮箱地址。
32 /// </summary>
33 public string Email { get; set; }
34
35 /// <summary>
36 /// 获取或者设置用户角色。
37 /// </summary>
38 public string Role { get; set; }
39
40 /// <summary>
41 /// 获取或者设置用户的登录时间。
42 /// </summary>
43 public DateTime LoginTime { get; set; }
44 }
45 }


            (4)、PatrickLiu.MicroService.Services(NETCORE 类库),定义服务实现。

                     这个项目很简单,都是建立项目后,几乎没有修改任何代码,有修改的地方都会贴出来。

                      

          UserService.cs 类型的代码。
            

 1 using PatrickLiu.MicroService.Interfaces;
2 using PatrickLiu.MicroService.Models;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6
7 namespace PatrickLiu.MicroService.Services
8 {
9 /// <summary>
10 /// 实现用户服务接口的实现类型。
11 /// </summary>
12 public class UserService : IUserService
13 {
14 private IList<User> dataList;
15
16 /// <summary>
17 /// 初始化类型的实例
18 /// </summary>
19 public UserService()
20 {
21 dataList = new List<User>()
22 { new User {ID=1,Name="黄飞鸿",Account="HuangFeiHong",Password="HuangFeiHong123456",Email="huangFeiHong@sina.com", Role="Admin", LoginTime=DateTime.Now },
23 new User {ID=2,Name="洪熙官",Account="HongXiGuan",Password="HongXiGuan54667",Email="HongXiGuan@sina.com", Role="Admin", LoginTime=DateTime.Now.AddDays(-5) },
24 new User {ID=3,Name="方世玉",Account="FangShiYu",Password="FangShiYu112233",Email="fangShiYu@163.com", Role="Admin", LoginTime=DateTime.Now.AddDays(-30) },
25 new User {ID=4,Name="苗翠花",Account="MiaoCuiHua",Password="MiaoCuiHua887766",Email="miaoCuiHua@sohu.com", Role="Admin", LoginTime=DateTime.Now.AddDays(-90) },
26 new User {ID=5,Name="严咏春",Account="YanYongChun",Password="YanYongChun09392",Email="yanYongChun@263.com", Role="Admin", LoginTime=DateTime.Now.AddMinutes(-50) }};
27 }
28
29 /// <summary>
30 /// 查找指定主键的用户实例对象。
31 /// </summary>
32 /// <param name="id">用户的主键。</param>
33 /// <returns>返回查找到的用户实例对象。</returns>
34 public User FindUser(int id)
35 {
36 return dataList.FirstOrDefault(user => user.ID == id);
37 }
38
39 /// <summary>
40 /// 获取所有用户的实例集合。
41 /// </summary>
42 /// <returns>返回所有的用户实例。</returns>
43 public IEnumerable<User> UserAll()
44 {
45 return dataList;
46 }
47 }
48 }


            (5)、PatrickLiu.MicroService.ServiceInstance(ASPNETCORE
WEBAPI),接口服务。

                     这个项目很简单,都是建立项目后,几乎没有修改任何代码,有修改的地方都会贴出来。
         

                   1)、我们要在这个项目里面使用Consul服务,所以,我们通过Nuget安装。也可以执行命令安装:Install-Package
Consul。

                         Install-Package Consul


                   2)、我们在Startup类里面的Configure方法里增加以下代码:

                         Startup.cs 类型的代码。

 1 using Consul;
2 using Microsoft.AspNetCore.Builder;
3 using Microsoft.AspNetCore.Hosting;
4 using Microsoft.Extensions.Configuration;
5 using Microsoft.Extensions.DependencyInjection;
6 using Microsoft.Extensions.Hosting;
7 using PatrickLiu.MicroService.Interfaces;
8 using PatrickLiu.MicroService.ServiceInstance.Utilities;
9 using PatrickLiu.MicroService.Services;
10 using System;
11 using System.Security.Policy;
12
13 namespace PatrickLiu.MicroService.ServiceInstance
14 {
15 /// <summary>
16 /// 应用程序启动配置
17 /// </summary>
18 public class Startup
19 {
20 /// <summary>
21 /// 构造函数注入配置对象
22 /// </summary>
23 /// <param name="configuration">配置实例</param>
24 public Startup(IConfiguration configuration)
25 {
26 Configuration = configuration;
27 }
28
29 /// <summary>
30 /// 获取配置实例。
31 /// </summary>
32 public IConfiguration Configuration { get; }
33
34 /// <summary>
35 /// 配置注入对象。
36 /// </summary>
37 /// <param name="services"></param>
38 public void ConfigureServices(IServiceCollection services)
39 {
40 services.AddControllers();
41 //需要增加的
42 services.AddSingleton<IUserService, UserService>();
43 }
44
45 /// <summary>
46 /// 配置 HTTP 处理管道。
47 /// </summary>
48 /// <param name="app">应用程序对象。</param>
49 /// <param name="env">环境对象。</param>
50 /// <param name="applicationLifetime">应用程序生命周期的对象。</param>
51 public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IHostApplicationLifetime applicationLifetime)
52 {
53 if (env.IsDevelopment())
54 {
55 app.UseDeveloperExceptionPage();
56 }
57
58 //app.UseHttpsRedirection();
59
60 app.UseRouting();
61
62 app.UseAuthorization();//授权
63
64 app.UseEndpoints(endpoints =>
65 {
66 endpoints.MapControllers();
67 });
68
69 #region Consul 注册,需要增加的
70
71 string serviceID = $"Service:{Configuration["ip"]}:{Configuration["port"]}---{Guid.NewGuid()}";
72 string consuleServiceName = "PatrickLiuService";
73
74 //注册服务。
75 Configuration.ConsulRegist(consuleServiceName,serviceID);
76
77 //注销服务
78 applicationLifetime.ConsulDown(serviceID);
79
80 #endregion
81 }
82 }
83 }

        3)、UsersController.cs 类型的代码。
            

  1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Threading;
5 using Microsoft.AspNetCore.Authorization;
6 using Microsoft.AspNetCore.Mvc;
7 using Microsoft.Extensions.Configuration;
8 using Microsoft.Extensions.Logging;
9 using PatrickLiu.MicroService.Interfaces;
10 using PatrickLiu.MicroService.Models;
11
12 namespace PatrickLiu.MicroService.ServiceInstance.Controllers
13 {
14 /// <summary>
15 /// 用户的 API 类型。
16 /// </summary>
17 [Route("api/[controller]")]
18 [ApiController]
19 public class UsersController : ControllerBase
20 {
21 #region 私有字段
22
23 private readonly ILogger<UsersController> _logger;
24 private readonly IUserService _userService;
25 private IConfiguration _configuration;
26
27 #endregion
28
29 #region 构造函数
30
31 /// <summary>
32 /// 初始化该类型的新实例。
33 /// </summary>
34 /// <param name="logger">日志记录器。</param>
35 /// <param name="userService">用户服务接口。</param>
36 /// <param name="configuration">配置服务。</param>
37 public UsersController(ILogger<UsersController> logger, IUserService userService, IConfiguration configuration)
38 {
39 _logger = logger;
40 _userService = userService;
41 _configuration = configuration;
42 }
43
44 #endregion
45
46 #region 实例方法
47
48 /// <summary>
49 /// 获取一条记录
50 /// </summary>
51 /// <param name="id"></param>
52 /// <returns></returns>
53 [HttpGet]
54 [Route("Get")]
55 public User Get(int id)
56 {
57 return _userService.FindUser(id);
58 }
59
60 /// <summary>
61 /// 获取所有记录。
62 /// </summary>
63 /// <returns></returns>
64 [HttpGet]
65 [Route("All")]
66 //[Authorize]
67 public IEnumerable<User> Get()
68 {
69 Console.WriteLine($"This is UsersController {this._configuration["port"]} Invoke");
70
71 return this._userService.UserAll().Select((user => new User
72 {
73 ID = user.ID,
74 Name = user.Name,
75 Account = user.Account,
76 Password = user.Password,
77 Email = user.Email,
78 Role = $"{this._configuration["ip"]}:{this._configuration["port"]}",
79 LoginTime = user.LoginTime
80 })); ;
81 }
82
83 /// <summary>
84 /// 超时处理
85 /// </summary>
86 /// <returns></returns>
87 [HttpGet]
88 [Route("Timeout")]
89 public IEnumerable<User> Timeout()
90 {
91 Console.WriteLine($"This is Timeout Start");
92 //超时设置。
93 Thread.Sleep(3000);
94
95 Console.WriteLine($"This is Timeout End");
96
97 return this._userService.UserAll().Select((user => new User
98 {
99 ID = user.ID,
100 Name = user.Name,
101 Account = user.Account,
102 Password = user.Password,
103 Email = user.Email,
104 Role = $"{this._configuration["ip"]}:{this._configuration["port"]}",
105 LoginTime = user.LoginTime
106 })); ;
107 }
108
109 #endregion
110 }
111 }

        4)、HealthController.cs 类型的代码,该代码用于健康检查。            

 1 using System;
2 using Microsoft.AspNetCore.Mvc;
3 using Microsoft.Extensions.Configuration;
4
5 namespace PatrickLiu.MicroService.ServiceInstance.Controllers
6 {
7 /// <summary>
8 /// 健康检查的控制器。
9 /// </summary>
10 [ApiController]
11 [Route("api/[controller]")]
12 public class HealthController : ControllerBase
13 {
14 private IConfiguration _configuration;
15
16 /// <summary>
17 /// 初始化该类型的新实例。
18 /// </summary>
19 /// <param name="configuration">配置接口。</param>
20 public HealthController(IConfiguration configuration)
21 {
22 _configuration = configuration;
23 }
24
25 /// <summary>
26 /// 要调用的接口。
27 /// </summary>
28 [HttpGet]
29 [Route("Index")]
30 public IActionResult Index()
31 {
32 Console.WriteLine($"This is HealhController {_configuration["port"]} Invoke");
33 return Ok();
34 }
35 }
36 }

  4、编写注册Consul 的核心代码。

            我们增加了一个扩展类,类的名称:ConsulExtension.cs,实现代码如下:

      

 1 using Consul;
2 using Microsoft.Extensions.Configuration;
3 using Microsoft.Extensions.Hosting;
4 using System;
5
6 namespace PatrickLiu.MicroService.ServiceInstance.Utilities
7 {
8 /// <summary>
9 /// Consul 静态扩展类。
10 /// </summary>
11 public static class ConsulExtension
12 {
13 /// <summary>
14 ///类型初始化器,初始化 Consul 网址和数据中心。
15 /// </summary>
16 static ConsulExtension()
17 {
18 Uri = new Uri("http://localhost:8500");
19 DataCenter = "dc1";
20 }
21
22 /// <summary>
23 /// 获取或者设置 Consul 的网址。
24 /// </summary>
25 public static Uri Uri { get; set; }
26
27 /// <summary>
28 /// 获取或者设置数据中心。
29 /// </summary>
30 public static string DataCenter { get; set; }
31
32 /// <summary>
33 /// 向 Consul 服务中心注册服务实例。
34 /// </summary>
35 /// <param name="configuration">配置对象。</param>
36 /// <param name="consulServiceName">在 Consul 服务中心注册的服务类别名称,多个实例的 ID 可以属于一个服务类别名称。</param>
37 /// <param name="serviceID">服务实例的主键值,必须唯一。</param>
38 public static void ConsulRegist(this IConfiguration configuration,string consulServiceName, string serviceID)
39 {
40 if (string.IsNullOrEmpty(consulServiceName) || string.IsNullOrWhiteSpace(consulServiceName))
41 {
42 throw new ArgumentNullException("consulServiceName is null");
43 }
44 if (string.IsNullOrEmpty(serviceID) || string.IsNullOrWhiteSpace(serviceID))
45 {
46 throw new ArgumentNullException("serviceID is null.");
47 }
48
49 using (ConsulClient client = new ConsulClient(config =>
50 {
51 config.Address = Uri;
52 config.Datacenter = DataCenter;
53 }))
54 {
55 string ip = configuration["ip"];
56 int port = int.Parse(configuration["port"]);
57 int weight = string.IsNullOrWhiteSpace(configuration["weight"]) ? 1 : int.Parse(configuration["weight"]);
58
59 client.Agent.ServiceRegister(new AgentServiceRegistration()
60 {
61 ID = serviceID,
62 Name = consulServiceName,
63 Address = ip,
64 Port = port,
65 Tags = new string[] { weight.ToString() },
66 Check = new AgentServiceCheck()
67 {
68 Interval = TimeSpan.FromSeconds(12),
69 HTTP = $"http://{ip}:{port}/API/Health/Index",
70 Timeout = TimeSpan.FromSeconds(5),
71 DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20)
72 }
73 }).Wait();
74 Console.WriteLine($"注册服务:{ip}:{port}--Weight:{weight}");
75 };
76 }
77
78 /// <summary>
79 /// 向 Consul 服务中心注销服务实例。
80 /// </summary>
81 /// <param name="applicationLifetime">配置对象。</param>
82 /// <param name="serviceID">服务实例的主键值,必须唯一。</param>
83 public static void ConsulDown(this IHostApplicationLifetime applicationLifetime, string serviceID)
84 {
85 if (string.IsNullOrEmpty(serviceID) || string.IsNullOrWhiteSpace(serviceID))
86 {
87 throw new ArgumentNullException("serviceID is null");
88 }
89 applicationLifetime.ApplicationStopped.Register(() =>
90 {
91 using (var consulClient = new ConsulClient(config => { config.Address = Uri; config.Datacenter = DataCenter; }))
92 {
93 Console.WriteLine("服务已经退出");
94 consulClient.Agent.ServiceDeregister(serviceID);
95 }
96 });
97 }
98 }
99 }

  
    5、编译项目,发布4个服务实例,独立进程承载。

             
再次提醒大家,在开始启动这4个服务实例之前,必须启动Consul服务中心。

            (1)、dotnet
PatrickLiu.MicroService.ServiceInstance.dll --urls="http://*:5726"
--ip="127.0.0.1" --port=5726
          


                                  健康检查如图:

                         

            (2)、dotnet
PatrickLiu.MicroService.ServiceInstance.dll --urls="http://*:5727"
--ip="127.0.0.1" --port=5727
          


                                   健康检查如图:

                          

            (3)、dotnet
PatrickLiu.MicroService.ServiceInstance.dll --urls="http://*:5728"
--ip="127.0.0.1" --port=5728
          


                                   健康检查如图:

          

            (4)、dotnet
PatrickLiu.MicroService.ServiceInstance.dll --urls="http://*:5729"
--ip="127.0.0.1" --port=5729
          


                                   健康检查如图:
          

                                以上是我们成功启动了4个服务实例,并且在Consul
服务中心注册成功,也可以执行健康检查,功能基本完善了。让我们看看4个服务实例在Consul中心的样子吧。

                  


  
    6、客户端访问 Consul服务,最终访问我们服务实例。

           

            客户端我们自己实现的轮训策略,每次刷新,端口号都变,效果如图:

                5726端口的数据:

                     

                5727端口的数据:

                     
  
              5728端口的数据:

                     

                5729端口的数据:

                     
  
    7、我们的结论。

            我们现在虽然可以自由的发现新服务,也可以发现失败的服务,实现了服务的自动注册和发现,但是Consul另外一个问题就是,客户端使用太麻烦,我们需要自己去决定调用服务策略,是轮训、随机、权重,还是其他策略,如果项目很小不是问题,如果项目很大,需求变化很快,我们都需要手动去做这些事,就太浪费时间和精力了。有问题,我们就解决问题,解决方案已有,就是使用Ocelot这个组件。

四、 
结束语

      
好了,今天就写到这里了。内容挺多了,整理起来也挺麻烦的,但是每天这样坚持,每天都有进步,生活过的也很充实。通过今天的学习,我们知道了如果只是单独以后Consul实现服务注册还是有缺点的,那我们就继续改进,既然客户端那么麻烦,我们的解决办法就是引入另外一个概念,Ocelot网关,它可以解决Consul的问题,又可以实现负载均衡,努力吧,每天进步一点点。

庐山真面目之三微服务架构Consul版本实现的更多相关文章

  1. 庐山真面目之六微服务架构Consul集群、Ocelot网关集群和Nginx版本实现

    庐山真面目之六微服务架构Consul集群.Ocelot网关集群和Nginx版本实现 一.简介      在上一篇文章<庐山真面目之五微服务架构Consul集群.Ocelot网关和Nginx版本实 ...

  2. 庐山真面目之七微服务架构Consul集群、Ocelot网关集群和IdentityServer4版本实现

    庐山真面目之七微服务架构Consul集群.Ocelot网关集群和IdentityServer4版本实现 一.简介      在上一篇文章<庐山真面目之六微服务架构Consul集群.Ocelot网 ...

  3. 庐山真面目之四微服务架构Consul集群和Nginx版本实现

    庐山真面目之四微服务架构Consul集群和Nginx版本实现 一.简介      在上一篇文章<庐山真面目之三微服务架构Consul版本实现>中,我们已经探讨了如何搭建基于单节点Consu ...

  4. 庐山真面目之四微服务架构Consul和Ocelot简单版本实现

    庐山真面目之四微服务架构Consul和Ocelot简单版本实现 一.简介      在上一篇文章<庐山真面目之三微服务架构Consul简单版本实现>中,我们已经探讨了如何搭建基于Consu ...

  5. 庐山真面目之八微服务架构 NetCore 基于 Dockerfile 文件部署

    庐山真面目之八微服务架构 NetCore 基于 Dockerfile 文件部署 一.简介      从今天开始,不出意外的话,以后所写的文章中所介绍项目的部署环境都应该会迁移到Linux环境上,而且是 ...

  6. 庐山真面目之二微服务架构NGINX版本实现

    一.简介          在上一篇文章<庐山真面目之微服务的简介和技术栈>中,我们已经探讨了微服务的来龙去脉,也说了想要实现微服务架构所需要的技术栈,今天我们开始实现一个微服务,当然这个 ...

  7. 庐山真面目之十二微服务架构基于Docker搭建Consul集群、Ocelot网关集群和IdentityServer版本实现

    庐山真面目之十二微服务架构基于Docker搭建Consul集群.Ocelot网关集群和IdentityServer版本实现 一.简介      在第七篇文章<庐山真面目之七微服务架构Consul ...

  8. 庐山真面目之九微服务架构 NetCore 基于 Docker 基础镜像和挂载文件部署

    庐山真面目之九微服务架构 NetCore 基于 Docker 基础镜像和挂载文件部署 一.简介      我们在上一篇文章<庐山真面目之八微服务架构 NetCore 基于 Dockerfile ...

  9. 庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群

    庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群 一.简介      前面的两篇文章,我们已经介绍了Net Core项目基于Docker容器部署在Linux服 ...

随机推荐

  1. 【题解】NOIP2018 赛道修建

    题目戳我 \(\text{Solution:}\) 根据题目信息简化题意,是让你在树上找出\(m\)条路径使得路径长度最小值最大. 看到题第一感先二分一个答案,问题转化为如何选择一些路径使得它们最小值 ...

  2. java进阶(26)--ForEach

    JDK5.0后新特性 一.普通for循环

  3. 在Linux下如何根据域名自签发OpenSSL证书与常用证书转换

    在Linux下如何根据域名自签发各种SSL证书,这里我们以Apache.Tomcat.Nginx为例. openssl自签发泛域名(通配符)证书 首先要有openssl工具,如果没有那么使用如下命令安 ...

  4. 基于空镜像scratch创建一个新的Docker镜像

    我们在使用Dockerfile构建docker镜像时,一种方式是使用官方预先配置好的容器镜像.优点是我们不用从头开始构建,节省了很多工作量,但付出的代价是需要下载很大的镜像包. 比如我机器上docke ...

  5. activiti 流程部署 保存流程图到数据库 保存二进制图片 存储失败

    activiti 流程部署 保存流程图到数据库  保存二进制图片 存储失败 具体错误如下 具体 junit测试 结果 :提示如下: 解决方法: 数据库版本不同 无法保存二进制文件到数据库表中!5.5. ...

  6. javascript里面的this指向问题

    1:一般情况下this最终指向调用它的那个对象. 2:全局作用域或者普通函数中的this都会指向window. 例1:console.log(this); //  在控制台输出的是BOM顶级对象 wi ...

  7. 多测师讲解selenium _enter弹框_高级讲师肖sir

    enter # from selenium import webdriver# from time import sleep# drvier=webdriver.Chrome()# url='file ...

  8. 一入Java深似海

    Java的基础语法 一个java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来写协同工作.下面简要介绍下类.对象.方法和实例变量的概念. 对象: 对象是一个类的实例,有状态和行为.例如 ...

  9. Verilog基础入门——简单的语句块编写(一)

    [题干] [代码] module top_module ( input in, output out ); assign out = ~in; endmodule 简单的实现一个非门

  10. zookeeper-(单机,伪集群)

    安装zookeeper(单机,伪集群):    1.下载 登陆zookeeper官网下载 https://zookeeper.apache.org/  或者  https://mirror.bit.e ...