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


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

           1、说明
                 有关微服务架构所涉及的技术太多,无法再一篇文章内讨论完全,所以,我们就分多期来说,每期都递进相关的技术,然后,一步一步的演化而来。如果您是大牛,就可以直接跳过,因为这些东西相对于您来说,这个太简单了。特别说明,这里的所有代码都经过测试,所以大家可以放心使用。

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

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

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

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

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

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

                               1)、startup.cs 类中唯一增加的代码

     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 类的代码                                

     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 1、单体架构
    25 
    26             //this.ViewBag.Users = _userService.UserAll();
    27 
    28             #endregion
    29 
    30             #region 2、分布式架构
    31 
    32             #region 2.1、直接访问服务实例
    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 视图的代码

     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 的代码

     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 2019ProjectSourceCodePatrickLiu.MicroServicePatrickLiu.MicroService.ServiceInstanceinDebug etcoreapp3.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:ProgramsMicroServices
                       
             5、修改 NGINX.CONF 文件。               
          
                     修改该目录D:ProgramsMicroServicesNginx-1.18.0conf 下的配置文件。文件名: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
                    效果如图:

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

                   如图:5727端口
                       

                   如图:5728端口
                        

                   如图:5729端口
                        

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

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

  • 相关阅读:
    iOS:Objective-C中Self和Super详解
    调试工具Instruments----Core Animation
    iOS开发之复制字符串到剪贴板
    Copy 和 mutableCopy
    TCP/IP,Http,Socket,XMPP的区别
    iOS程序中的内存分配 栈区堆区全局区(转)
    iOS常见算法(二分法 冒泡 选择 快排)
    老司机带你走进Core Animation
    C# 爬虫小程序
    C# 房贷计算器
  • 原文地址:https://www.cnblogs.com/PatrickLiu/p/13939135.html
Copyright © 2020-2023  润新知