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

1、简介
         在上一篇文章《庐山真面目之一微服务的简介和技术栈》中,咱们已经探讨了微服务的前因后果,也说了想要实现微服务架构所须要的技术栈,今天咱们开始实现一个微服务,固然这个实现是简化版本的,在这个版本里面也不涉及 Docker、K8S等的东西,咱们逐步演化,今天这一期只是实现一个NGINX版本的微服务的功能。

       1、说明
             有关微服务架构所涉及的技术太多,没法再一篇文章内讨论彻底,因此,咱们就分多期来讲,每期都递进相关的技术,而后,一步一步的演化而来。若是您是大牛,就能够直接跳过,由于这些东西相对于您来讲,这个太简单了。特别说明,这里的全部代码都通过测试,因此你们能够放心使用。

       2、开发环境
            
如下就是开发环境,不用多说,都很简单,一看就知道。
            (1)、开发工具:Visual Studio 2019
            (2)、开发语言:C#
            (3)、开发平台:Net Core3.1,跨平台。
            (4)、网关服务:NGINX,专一于高性能网关。
            (5)、操做系统:Windows 10,64 bit。

        三、今天的目标
              今天咱们的目标就是创建一个基于Nginx网关实现的,服务实例没有任何容器包容,只是独立进程承载的这么一个架构实现。
             

html

2、微服务架构之NGINX 版本实现
           
          在文章开始以前,咱们仍是要简要说明一下。上一篇文件《庐山真面目之一微服务的简介和技术栈》中咱们说过,微服务有三个版本,分别是:单体架构、垂直拆分设计和微服务,基于SOA的咱们暂时不讨论。其实,第二版本和第一个没有本质区别,都是单体架构而已,因此,咱们今天就简单实现一下微服务的版本,因为里面涉及的技术不少,微服务这个版本我又分了多个版原本写,今天是最简单。

          今天咱们主要讨论基于NGINX实现的分布式、微服务架构的优缺点,每一个项目的代码都独立贴出来,逻辑很简单,由于咱们侧重架构嘛,咱们开始吧。

        1、咱们解决方案,包含5个项目。

             (1)、PatrickLiu.MicroService.Client(ASP.NET CORE MVC),客户端应用程序。
                         这个项目很简单,几乎没有任何修改,只是在这里访问服务实例而已。
                           

                           1)、startup.cs 类中惟一增长的代码nginx

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

                           2)、HomeController.cs 类的代码                                web

 1 public class HomeController : Controller
 2     {
 3         private readonly ILogger<HomeController> _logger;
 4         private readonly IUserService _userService;
 5         private static int index;
 6 
 7         /// <summary>
 8         /// 初始化该类型的新实例。
 9         /// </summary>
10         /// <param name="logger">注入日志对象。</param>
11         /// <param name="userService">注入用户服务对象。</param>
12         public HomeController(ILogger<HomeController> logger, IUserService userService)
13         {
14             _logger = logger;
15             _userService = userService;
16         }
17 
18         /// <summary>
19         /// 首页。
20         /// </summary>
21         /// <returns></returns>
22         public IActionResult Index()
23         {
24             #region 一、单体架构
25 
26             //this.ViewBag.Users = _userService.UserAll();
27 
28             #endregion
29 
30             #region 二、分布式架构
31 
32             #region 2.一、直接访问服务实例
33 
34             //string url = string.Empty;
35             //url = "http://localhost:5726/api/users/all";
36             //url = "http://localhost:5726/api/users/all";
37             //url = "http://localhost:5726/api/users/all";
38 
39             #endregion
40 
41             #region 经过 Ngnix网关访问服务实例,默认轮训。
42 
43             string url = "http://localhost:8080/api/users/all";
44 
45             #endregion
46 
47             string content = InvokeAPI(url);
48             this.ViewBag.Users = Newtonsoft.Json.JsonConvert.DeserializeObject<IEnumerable<User>>(content);
49             Console.WriteLine($"This is {url} Invoke.");
50             
51             #endregion
52 
53             return View();
54         }
55 
56 
57         /// <summary>
58         /// 
59         /// </summary>
60         /// <param name="url"></param>
61         /// <returns></returns>
62         public static string InvokeAPI(string url)
63         {
64             using (HttpClient client = new HttpClient())
65             {
66                 HttpRequestMessage message = new HttpRequestMessage();
67                 message.Method = HttpMethod.Get;
68                 message.RequestUri = new Uri(url);
69                 var result = client.SendAsync(message).Result;
70                 string conent = result.Content.ReadAsStringAsync().Result;
71                 return conent;
72             }
73         }
74     }
75 }

                          3)、Index.cshtml 视图的代码api

 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>


             (2)、PatrickLiu.MicroService.Interfaces(NETCORE类库项目),定义服务接口。
                          这个项目很简单,只定义了一个接口类型,名称:IUserService.cs。
                         

         IUserService的代码浏览器

 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。
                        

                         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 类库项目),实现接口类型。
                         这个项目很简单,只定义了一个类型,实现IUserService接口,类名:UserService.cs。
                        

        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(ASP.NET CORE WEB API项目)。
                          这个项目很简单,引用其余三个项目,制做 Restfull API,可让客户端访问。
                         引用项目:PatrickLiu.MicroService.Interfaces
                                          PatrickLiu.MicroService.Services
                                          PatrickLiu.MicroService.Models
                         

          1)、Startup.cs 的代码app

 1     public class Startup
 2     {
 3 
 4         /// <summary>
 5         /// 
 6         /// </summary>
 7         /// <param name="services"></param>
 8         public void ConfigureServices(IServiceCollection services)
 9         {
10             services.AddControllers();
11             services.AddSingleton<IUserService, UserService>();
12         }
13     }

          2)、Program.cs 的代码负载均衡

 1     public class Program
 2     {
 3         public static void Main(string[] args)
 4         {
 5             new ConfigurationBuilder()
 6                 .SetBasePath(Directory.GetCurrentDirectory())
 7                 .AddCommandLine(args)//支持命令行
 8                 .Build();
 9                 
10             CreateHostBuilder(args).Build().Run();
11         }
12 
13         public static IHostBuilder CreateHostBuilder(string[] args) =>
14             Host.CreateDefaultBuilder(args)
15                 .ConfigureWebHostDefaults(webBuilder =>
16                 {
17                     webBuilder.UseStartup<Startup>();
18                 });
19     }

          3)、UsersController.cs 的代码分布式

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

         

         2、编译项目,启动四个服务实例。
                这四个服务实例是PatrickLiu.MicroService.ServiceInstance项目的实例,执行 dotnet 命令要在当前目录下。
                个人目录:E:\Visual Studio 2019\Project\SourceCode\PatrickLiu.MicroService\PatrickLiu.MicroService.ServiceInstance\bin\Debug\netcoreapp3.1
              (1)、dotnet PatrickLiu.MicroService.ServiceInstance.dll --urls="http://*:5726" --ip="127.0.0.1" --port=5726

                        

                         能够访问WebAPI地址,验证是否成功。
                         地址:http://localhost:5726/api/users/all
                         效果如图:
                   
              
              (2)、dotnet PatrickLiu.MicroService.ServiceInstance.dll --urls="http://*:5727" --ip="127.0.0.1" --port=5727

                         

                         能够访问WebAPI地址,验证是否成功。
                         地址:http://localhost:5727/api/users/all
                         效果如图:
            
             (3)、dotnet PatrickLiu.MicroService.ServiceInstance.dll --urls="http://*:5728" --ip="127.0.0.1" --port=5728

                        

                         能够访问WebAPI地址,验证是否成功。
                         地址:http://localhost:5728/api/users/all
                         效果如图:
          
             (4)、dotnet PatrickLiu.MicroService.ServiceInstance.dll --urls="http://*:5729" --ip="127.0.0.1" --port=5729

                        

                         能够访问WebAPI地址,验证是否成功。
                         地址:http://localhost:5729/api/users/all
                         效果如图:
          
         3、下载NGINX 服务组件,默认下载 Windows 版本。
     
                  
                        官网: http://nginx.org/en/download.html


         4、部署NGINX服务器。
                   将下载的文件拷贝到没有中文的目录下,解压文件就能够。
                   个人存放目录:D:\Programs\MicroServices
                   
         5、修改 NGINX.CONF 文件。               
      
                 修改该目录D:\Programs\MicroServices\Nginx-1.18.0\conf 下的配置文件。文件名:nginx.conf
                  
               增长的配置内容以下:

 1  upstream MicroService{
 2         server localhost:5726;
 3         server localhost:5727;
 4         server localhost:5728;
 5         server localhost:5729;
 6  }
 7 
 8 server {
 9         listen       8080;
10         server_name  localhost;
11 
12          location / {
13              proxy_pass http://MicroService;
14          }
15  }

       

         6、启动 Nginx 服务器
                 注意:我是在Nginx当前目录下边。
                 Start nginx
                 
                NGINX端口默认:80,我改为了8080,没有提示,通常会启动正常。

                能够访问NGINX地址,验证NGINX是否配置而且启动成功。
                地址:http://localhost:8080
                效果如图:

        七、打开浏览器,输入地址:http://localhost:8080/api/users/all,屡次刷新页面,你就会看到变化。咱们已经实现了分布式、负载均衡。
                如图:5726 端口
                    

               如图:5727端口
                   

               如图:5728端口
                    

               如图:5729端口
                    

          八、咱们测试NGINX的高可用和可扩展性,得出如下结论。
         (1)、Nginx 客户端配置很简单,直接访问 Nginx 的网关地址就会路由到注册服务实例。
                   (2)、若是服务实例掉线或者出现异常,能够自动察觉器情况,下次轮训能够跳过,不需人为参与。
                   (3)、若是系统增长了新的服务实例,Nginx 没法自动发现,须要人工修改配置文件,而后重启,才能够。
      因为第三点的缺点,是咱们这个版本的最大的缺点,咱们不可能在庞大的系统中老是经过人力来作这些事。没法自动发现服务,咱们须要继续改进,那就是服务注册发现中心。

3、  结束语           好了,今天就写到这里了,这个是介于分布式和微服务之间的一个很简单的架构实现,虽然很简单,可是咱们所要表达的思想和所要获取到的东西咱们已经获得了。什么东西都是由简入繁的,下一期,继续开始咱们的有关微服务的进化吧。努力吧,天天进步一点点。

相关文章
相关标签/搜索