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