首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

.Net Core微服务入门系列(一)——项目搭建

  • 25-02-15 06:41
  • 4750
  • 5761
blog.csdn.net

系列文章目录

1、.Net Core微服务入门系列(一)——项目搭建
2、.Net Core微服务入门全纪录(二)——Consul-服务注册与发现(上)
3、.Net Core微服务入门全纪录(三)——Consul-服务注册与发现(下)
4、.Net Core微服务入门全纪录(四)——Ocelot-API网关(上)
5、.Net Core微服务入门全纪录(五)——Ocelot-API网关(下)
6、.Net Core微服务入门全纪录(六)——EventBus-事件总线
7、.Net Core微服务入门全纪录(七)——IdentityServer4-授权认证
8、.Net Core微服务入门全纪录(八)——Docker Compose与容器网络


在这里插入图片描述


前言?

关于 微服务 的概念解释网上有很多, 个人理解微服务是一种系统架构模式,它和语言无关,和框架无关,和工具无关,和服务器环境无关。

微服务思想 是将传统的单体系统按照业务拆分成多个职责单一、且可独立运行的接口服务。至于服务如何拆分,没有明确的定义。几乎任何后端语言都能做微服务开发。微服务也并不是完美无缺的,微服务架构会带来更多的问题,增加系统的复杂度,引入更多的技术栈。


一、创建项目

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
一个客户端,一个产品服务,一个订单服务。3个项目都是 asp.net core web 应用程序。创建项目的时候记得启用一下 Docker 支持,或者后面添加也行。

为产品、订单服务添加一些基础代码,就简单的返回一下 服务名称,当前时间,服务的ip、端口。

在这里插入图片描述
在这里插入图片描述

二、在Docker中运行服务

为了方便,我使用 Docker 来运行服务,不用 Docker 也行,关于 docker 的安装及基本使用就不介绍了。

2.1 build镜像

在项目根目录打开 PowerShell 窗口执行:

docker build -t productapi -f ./Product.API/Dockerfile .
  • 1

在这里插入图片描述
在这里插入图片描述
?提示:Successfully代表 build 成功了。

2.2 运行容器

执行:

docker run -d -p 9050:80 --name productservice productapi
  • 1

在这里插入图片描述
执行:docker ps 查看运行的容器:
在这里插入图片描述
没问题,使用浏览器访问一下接口:
在这里插入图片描述
也没问题,其中的 ip 端口是 Docker 容器内部的 ip 端口,所以端口是80,这个无所谓。

产品服务部署好了,下面部署一下订单服务,也是同样的流程,就把指令简单贴一下吧:
build镜像:

docker build -t orderapi -f ./Order.API/Dockerfile .
  • 1

运行容器:

docker run -d -p 9060:80 --name orderservice orderapi
  • 1

在这里插入图片描述
OK,订单服务也部署完成了。

三、客户端调用

客户端我这里只做了一个web客户端,实际可能是各种业务系统、什么PC端、手机端、小程序。。。这个明白就好,为了简单就不搞那么多了。

因为客户端需要 http 请求服务端接口,所以需要一个 http 请求客户端,我个人比较习惯 RestSharp,安利一波:https://github.com/restsharp/RestSharp

在这里插入图片描述
添加基础代码:

在这里插入图片描述
IServiceHelper.cs:

    public interface IServiceHelper
    {
        /// 
        /// 获取产品数据
        /// 
        /// 
        Task<string> GetProduct();

        /// 
        /// 获取订单数据
        /// 
        /// 
        Task<string> GetOrder();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

ServiceHelper.cs:

    public class ServiceHelper : IServiceHelper
    {
        public async Task<string> GetOrder()
        {
            string serviceUrl = "http://localhost:9060";//订单服务的地址,可以放在配置文件或者数据库等等...

            var Client = new RestClient(serviceUrl);
            var request = new RestRequest("/orders", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }

        public async Task<string> GetProduct()
        {
            string serviceUrl = "http://localhost:9050";//产品服务的地址,可以放在配置文件或者数据库等等...

            var Client = new RestClient(serviceUrl);
            var request = new RestRequest("/products", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Startup.cs:

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            
            //注入IServiceHelper
            services.AddSingleton<IServiceHelper, ServiceHelper>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

HomeController.cs:

    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly IServiceHelper _serviceHelper;

        public HomeController(ILogger<HomeController> logger, IServiceHelper serviceHelper)
        {
            _logger = logger;
            _serviceHelper = serviceHelper;
        }

        public async Task<IActionResult> Index()
        {
            ViewBag.OrderData = await _serviceHelper.GetOrder();
            ViewBag.ProductData = await _serviceHelper.GetProduct();

            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

Index.cshtml:

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>
        @ViewBag.OrderData
    </p>
    <p>
        @ViewBag.ProductData
    </p>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

代码比较简单,这里就不用 docker 了,直接控制台启动,使用浏览器访问:

在这里插入图片描述
一切正常。进行到这里,各个服务也独立运行了,客户端也能正常调用了,貌似算是完成一个简易的微服务了。但是,微服务架构最重要的原则就是——“高可用”。以上的做法明显不能满足高可用性,因为任何一个服务挂掉,所有依赖这个服务的业务系统都会受影响。

停止一下订单服务:

docker stop orderservice
  • 1

在这里插入图片描述
在这里插入图片描述
订单服务停止,导致客户端业务系统无法获取订单数据。要解决这个问题,很容易想到:集群。

四、简单的服务集群

既然单个服务实例有挂掉的风险,那么部署多个服务实例就好了嘛,只要大家不同时全挂就行。

使用 docker 运行多个服务实例:

docker run -d -p 9061:80 --name orderservice1 orderapi
docker run -d -p 9062:80 --name orderservice2 orderapi
docker run -d -p 9051:80 --name productservice1 productapi
docker run -d -p 9052:80 --name productservice2 productapi
  • 1
  • 2
  • 3
  • 4

现在订单服务和产品服务都增加到3个服务实例。

那么稍微改造一下客户端代码吧:ServiceHelper.cs:

public class ServiceHelper : IServiceHelper
    {
        public async Task<string> GetOrder()
        {
            string[] serviceUrls = { "http://localhost:9060", "http://localhost:9061", "http://localhost:9062" };//订单服务的地址,可以放在配置文件或者数据库等等...

            //每次随机访问一个服务实例
            var Client = new RestClient(serviceUrls[new Random().Next(0, 3)]);
            var request = new RestRequest("/orders", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }

        public async Task<string> GetProduct()
        {
            string[] serviceUrls = { "http://localhost:9050", "http://localhost:9051", "http://localhost:9052" };//产品服务的地址,可以放在配置文件或者数据库等等...

            //每次随机访问一个服务实例
            var Client = new RestClient(serviceUrls[new Random().Next(0, 3)]);
            var request = new RestRequest("/products", Method.GET);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

当然拿到这些服务地址可以自己做复杂的负载均衡策略,比如轮询,随机,权重等等 都行,甚至在中间弄个 nginx 也可以。这些不是重点,所以就简单做一个随机吧,每次请求来了随便访问一个服务实例。

浏览器测试一下:
在这里插入图片描述
可以看到请求被随机分配了。但是这种做法依然不安全,如果随机访问到的实例刚好挂掉,那么业务系统依然会出问题。简单处理思路是:1.如果某个地址请求失败了,那么换一个地址接着执行。2.如果某个地址的请求连续多次失败了,那么就移除这个地址,下次就不会访问到它了。。。。。。。业务系统实现以上逻辑,基本上风险就很低了,也算是大大增加了系统可用性了。

?然后思考另一个问题:

实际应用中,上层的业务系统可能非常多,为了保证可用性,每个业务系统都去考虑服务实例挂没挂掉吗?而且实际应用中服务实例的数量或者地址大多是不固定的,例如双十一来了,流量大了,增加了一堆服务实例,这时候每个业务系统再去配置文件里配置一下这些地址吗?双十一过了又去把配置删掉吗?显然是不现实的,服务必须要做到可灵活伸缩。

这时候就引入一个名词:服务注册与发现,下一篇介绍。

注:本文转载自blog.csdn.net的Microi风闲的文章"https://lisaisai.blog.csdn.net/article/details/145213818"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top