廬山真面目之二微服務架構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、  結束語           好了,今天就寫到這裏了,這個是介於分佈式和微服務之間的一個很簡單的架構實現,雖然很簡單,可是咱們所要表達的思想和所要獲取到的東西咱們已經獲得了。什麼東西都是由簡入繁的,下一期,繼續開始咱們的有關微服務的進化吧。努力吧,天天進步一點點。

相關文章
相關標籤/搜索