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

可均可可 2020-11-07 20:37:39
真面目 面目 庐山真面目 庐山真面 庐山


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

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

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

        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 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
                效果如图:

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

               如图:5727端口
                   

               如图:5728端口
                    

               如图:5729端口
                    

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

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

版权声明
本文为[可均可可]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/PatrickLiu/p/13939135.html

  1. [front end -- JavaScript] knowledge point (IV) -- memory leakage in the project (I)
  2. This mechanism in JS
  3. Vue 3.0 source code learning 1 --- rendering process of components
  4. Learning the realization of canvas and simple drawing
  5. gin里获取http请求过来的参数
  6. vue3的新特性
  7. Get the parameters from HTTP request in gin
  8. New features of vue3
  9. vue-cli 引入腾讯地图(最新 api,rocketmq原理面试
  10. Vue 学习笔记(3,免费Java高级工程师学习资源
  11. Vue 学习笔记(2,Java编程视频教程
  12. Vue cli introduces Tencent maps (the latest API, rocketmq)
  13. Vue learning notes (3, free Java senior engineer learning resources)
  14. Vue learning notes (2, Java programming video tutorial)
  15. 【Vue】—props属性
  16. 【Vue】—创建组件
  17. [Vue] - props attribute
  18. [Vue] - create component
  19. 浅谈vue响应式原理及发布订阅模式和观察者模式
  20. On Vue responsive principle, publish subscribe mode and observer mode
  21. 浅谈vue响应式原理及发布订阅模式和观察者模式
  22. On Vue responsive principle, publish subscribe mode and observer mode
  23. Xiaobai can understand it. It only takes 4 steps to solve the problem of Vue keep alive cache component
  24. Publish, subscribe and observer of design patterns
  25. Summary of common content added in ES6 + (II)
  26. No.8 Vue element admin learning (III) vuex learning and login method analysis
  27. Write a mini webpack project construction tool
  28. Shopping cart (front-end static page preparation)
  29. Introduction to the fluent platform
  30. Webpack5 cache
  31. The difference between drop-down box select option and datalist
  32. CSS review (III)
  33. Node.js学习笔记【七】
  34. Node.js learning notes [VII]
  35. Vue Router根据后台数据加载不同的组件(思考-&gt;实现-&gt;不止于实现)
  36. Vue router loads different components according to background data (thinking - & gt; Implementation - & gt; (more than implementation)
  37. 【JQuery框架,Java编程教程视频下载
  38. [jQuery framework, Java programming tutorial video download
  39. Vue Router根据后台数据加载不同的组件(思考-&gt;实现-&gt;不止于实现)
  40. Vue router loads different components according to background data (thinking - & gt; Implementation - & gt; (more than implementation)
  41. 【Vue,阿里P8大佬亲自教你
  42. 【Vue基础知识总结 5,字节跳动算法工程师面试经验
  43. [Vue, Ali P8 teaches you personally
  44. [Vue basic knowledge summary 5. Interview experience of byte beating Algorithm Engineer
  45. 【问题记录】- 谷歌浏览器 Html生成PDF
  46. [problem record] - PDF generated by Google browser HTML
  47. 【问题记录】- 谷歌浏览器 Html生成PDF
  48. [problem record] - PDF generated by Google browser HTML
  49. 【JavaScript】查漏补缺 —数组中reduce()方法
  50. [JavaScript] leak checking and defect filling - reduce() method in array
  51. 【重识 HTML (3),350道Java面试真题分享
  52. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  53. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  54. [re recognize HTML (3) and share 350 real Java interview questions
  55. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  56. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  57. 【重识 HTML ,nginx面试题阿里
  58. 【重识 HTML (4),ELK原来这么简单
  59. [re recognize HTML, nginx interview questions]
  60. [re recognize HTML (4). Elk is so simple