.NetCore部署微服务(一)

news/2024/5/19 23:43:38 标签: .netcore, 微服务

目录

前言

什么是微服务

微服务的优势

微服务的原则

创建项目

在Docker中运行服务

 客户端调用

简单的集群服务


前言

写这篇文章旨在用最简单的代码阐述一下微服务

什么是微服务

微服务描述了从单独可部署的服务构建分布式应用程序的体系结构流程,同时这些服务会执行特定业务功能并通过 Web 接口进行通信,DevOps 团队通过将微服务(如构建块)组合在一起,从而将单个功能纳入微服务中以及构建更大的系统。

微服务的优势

微服务采用了某一开放/封闭原则:

  • 它们会开放以便进行扩展(使用它们公开的接口)
  • 它们会关闭以便进行修改(每个修改都会独立执行并进行版本控制)

微服务为整体体系结构提供了众多优势:

  • 它们可以通过确保一个服务中的问题不会崩溃或影响应用程序的其他部分来移除单一故障点 (SPOF)
  • 可独立扩展单个微服务,以提供额外的可用性和容量。
  • DevOps 团队可通过添加新微服务来扩展功能,而无需不必要的影响应用程序的其他部分。

使用微服务可提高团队速度。微服务通过允许软件开发团队利用事件驱动的编程和自动缩放等场景,很好地补充基于云的应用程序体系结构。 微服务组件通常会通过 REST 协议公开 API(应用程序编程接口),以便与其他服务通信。

微服务的原则

        顾名思义,微服务体系结构是一种将服务器应用程序生成为一组小型服务的方法。 这意味着微服务体系结构主要面向后端,虽然该方法也会用于前端。 每个服务都在自己的进程中运行,并使用 HTTP/HTTPS、WebSocket 或 AMQP 等协议与其他进程进行通信。 每个微服务在特定的上下文边界内实现特定的端到端域或业务功能,每个微服务都必须自主开发,并且可以独立部署。 最后,每个微服务都应拥有其自己的相关域数据模型和域逻辑(主权和分散式数据管理),并且可以基于不同的数据存储技术(SQL、NoSQL)和不同的编程语言。

        微服务应该有多大? 在开发微服务时,大小不应成为重点。 相反,重点应该是创建松散耦合的服务以便自主地为每个服务进行开发、部署和缩放。 当然,在标识和设计微服务时,只要与其他微服务不存在过多的直接依赖项,就应尝试让它们尽可能地小。 比微服务的大小更重要的是,它必须具有内部内聚,并且独立于其他服务。

创建项目

 我们在项目中新建两个文件夹,Client跟Service。Client文件夹用于管理我们的客户端,Service文件夹用于管理我们的Api.

项目结构目录如下:

三个项目都是Asp.Net Core Web API。

我们为ForumProductApi以及ForumOrderApi添加一些基础代码,我们返回接口名称以及当前时间,服务的IP地址,端口等信息,以让我们更好的区分接口。

我们修改OrderApi的代码如下:

[ApiController]
[Route("order")]
public class OrderController : ControllerBase
{
    private readonly ILogger<OrderController> _logger;

    public OrderController(ILogger<OrderController> logger)
    {
        _logger = logger;
    }

    [HttpGet(Name = "GetOrder")]
    public Task<OrderEntity> GetOrder()
    {
        return Task.FromResult(new OrderEntity()
        {
            date_time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
            ip_address = Request.HttpContext.Connection.LocalIpAddress?.ToString(),
            ip_port = Request.HttpContext.Connection.LocalPort.ToString(),
            service_name = "订单服务"
        });

    }
}


public class OrderEntity
{
    /// <summary>
    /// 当前时间
    /// </summary>
    public string? date_time { get; set; }

    /// <summary>
    /// Ip地址
    /// </summary>
    public string? ip_address { get; set; }


    /// <summary>
    /// Ip端口
    /// </summary>
    public string? ip_port { get; set; }


    /// <summary>
    /// 服务名称
    /// </summary>
    public string? service_name { get; set; }


}

同理我们修改ProductApi的代码如下

[ApiController]
[Route("product")]
public class ProductController : ControllerBase
{
    private readonly ILogger<ProductController> _logger;

    public ProductController(ILogger<ProductController> logger)
    {
        _logger = logger;
    }

    [HttpGet(Name = "GetOrder")]
    public Task<ProductEntity> GetOrder()
    {
        return Task.FromResult(new ProductEntity()
        {
            date_time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
            ip_address = Request.HttpContext.Connection.LocalIpAddress?.ToString(),
            ip_port = Request.HttpContext.Connection.LocalPort.ToString(),
            service_name = "产品服务"
        });

    }
}

public class ProductEntity
{
    /// <summary>
    /// 当前时间
    /// </summary>
    public string? date_time { get; set; }

    /// <summary>
    /// Ip地址
    /// </summary>
    public string? ip_address { get; set; }


    /// <summary>
    /// Ip端口
    /// </summary>
    public string? ip_port { get; set; }


    /// <summary>
    /// 服务名称
    /// </summary>
    public string? service_name { get; set; }


}

在Docker中运行服务

发布Product服务。

修改Dockerfile如下

#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.

#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed.
#For more information, please see https://aka.ms/containercompat

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

COPY ./ ./

ENTRYPOINT ["dotnet", "ForumProductApi.dll"]

 打开prowershell,使用Docker编译项目并发布,

进入发布目录,build Api

 docker build -t productcontainer:1.0 .

这就表示编译成功。

然后我们运行容器

 docker run -d -p 8050:80 --name productapi productcontainer:1.0

 然后我们在浏览器中访问该项目

http://localhost:8050/swagger/index.html

访问结果如下:

我们以同样的方式部署Order服务。

同样我们也需要修改Dockerfile如下:

#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.

#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed.
#For more information, please see https://aka.ms/containercompat

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

COPY ./ ./

ENTRYPOINT ["dotnet", "ForumOrderApi.dll"]

 打开prowershell,使用Docker编译项目并发布,

进入发布目录,build Api

 docker build -t ordercontainer:1.0 .

运行项目

 docker run -d -p 8060:80 --name ordertapi ordercontainer:1.0

  然后我们在浏览器中访问该项目

http://localhost:8060/swagger/index.html

访问结果如下: 

 客户端调用

客户端我们需要http请求服务端接口,所以我们需要http请求,这里我使用了HttpClientFactory,代码很简单,可用于参考

Program代码

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();


#region 注册IHttpClientFactory


builder.Services.AddHttpClient("local", config =>
{
    config.BaseAddress = new Uri("http://localhost");
});
builder.Services.AddHttpClient();

#endregion

var app = builder.Build();

// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
    app.UseSwagger();
    app.UseSwaggerUI();
//}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

ClientController代码如下:

[ApiController]
[Route("client")]
public class ClientController:ControllerBase
{
    private readonly ILogger<ClientController> _logger;

    private readonly IHttpClientFactory _httpClientFactory;

    public ClientController(IHttpClientFactory httpClientFactory, ILogger<ClientController> logger)
    {
        _httpClientFactory = httpClientFactory;

        _logger = logger;

    }

    [HttpGet(Name = "GetData")]
    public async Task<string> GetData()
    {
        var client = _httpClientFactory.CreateClient("local"); //

        string order_url = "http://localhost:8060/order";

        string product_url = "http://localhost:8050/product";

        var order_result = await client.GetStringAsync(order_url);

        var product_result = await client.GetStringAsync(product_url);

        return $"订单服务:{order_result}========================产品服务:{product_result}";
    }
}

直接运行看结果:

 我们的接口都可以请求成功。

一切正常,进行到这里,各个服务都可以独立运行,客户端也可以正常调用,貌似我们已经完成一个简易的微服务了,但是微服务架构最重要的原则是,高可用,以上的做法并不能满足高可用性,因为我么的服务一旦挂掉,所有依赖这个服务的业务系统就会受到影响。

如,我们现在停止订单服务。

docker stop orderapi

我们再次使用客户端请求获取数据的接口:

出现如下结果:

要解决这个问题,我们很容易想到的解决方案就是,集群。

简单的集群服务

既然单个服务有挂掉的风险,那么部署多个服务实例就好了,只要大家不同时挂掉我们的请求就没有问题。

ok,我们使用docker运行多个服务实例

 我们的Order服务运行三个实例,端口从60到62

 docker run -d -p 8060:80 --name orderapi1 ordercontainer:1.0
 docker run -d -p 8061:80 --name orderapi2 ordercontainer:1.0
 docker run -d -p 8062:80 --name orderapi3 ordercontainer:1.0

同样的我们的product服务也运行三个实例,端口从50到52

 docker run -d -p 8050:80 --name productapi1 productcontainer:1.0
 docker run -d -p 8051:80 --name productapi2 productcontainer:1.0
 docker run -d -p 8052:80 --name productapi3 productcontainer:1.0

那么也稍稍的改动一下我们的Client代码吧

[ApiController]
[Route("api/[controller]/[action]")]
public class ClientController:ControllerBase
{
    private readonly ILogger<ClientController> _logger;

    private readonly IHttpClientFactory _httpClientFactory;

    public ClientController(IHttpClientFactory httpClientFactory, ILogger<ClientController> logger)
    {
        _httpClientFactory = httpClientFactory;

        _logger = logger;

    }

 
    public async Task<string> GetProduct()
    {
        var client = _httpClientFactory.CreateClient("local"); //

        string[] arr_product_url = { "http://localhost:8050/product", "http://localhost:8051/product", "http://localhost:8052/product" } ;

        var product_result = await client.GetStringAsync(arr_product_url[new Random().Next(0, 3)]);

        return $"产品服务:{product_result}";
    }


    public async Task<string> GetOrder() 
    {
        var client = _httpClientFactory.CreateClient("local"); //

        string[] arr_order_url = { "http://localhost:8060/order", "http://localhost:8061/order", "http://localhost:8062/order" };

        var order_result = await client.GetStringAsync(arr_order_url[new Random().Next(0, 3)]);

        return $"订单服务:{order_result}";
    }
}

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

我们尝试多次调用该接口,发现我们已经实现了随机的效果。

但是,这种做法依然不安全,如果随机访问到的实例刚好挂掉,那么业务依然会出现问题,简单处理思路是什么呢?

1.如果某个地址请求失败了,那么换一个地址接着执行。
2.如果某个地址的请求连续多次失败了,那么就移除这个地址,下次就不会访问到它了。

业务系统实现以上逻辑,基本上风险就很低了,也算是大大增加了系统可用性了。

然而:

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

这时候就需要引入一个问题,服务注册与发现。


http://www.niftyadmin.cn/n/5304437.html

相关文章

Flappy Bird QDN PyTorch博客 - 代码解读

Flappy Bird QDN PyTorch博客 - 代码解读 介绍环境配置项目目录结构QDN算法重要函数解读preprocess(observation)DeepNetWork(nn.Module)BirdDQN类主程序部分 介绍 在本博客中&#xff0c;我们将介绍如何使用QDN&#xff08;Quantile Dueling Network&#xff09;算法&#xf…

基于SpringBoot的旅游网站281

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的旅游网站281,java项目。…

FBL刷写

刷写 1、刷写需求的理解2、刷写流程2.1、预编程阶段&#xff1a;保证在编程阶段的动作能够正常操作&#xff0c;控制器给响应。整车功能不会出现问题 刷写某一控制器时&#xff0c;避免其他控制器集DTC,85控制DTC&#xff1b; 28 通信控制.保证总线负载率不要过高&#xff08;下…

第10课 利用windows API捕获桌面图像并通过FFmpeg分享

在上一章&#xff0c;我们已经实现了一对一音视频对话功能。在实际应用中&#xff0c;我们常需要把自己的电脑桌面分享给他人以实现桌面共享功能&#xff0c;这种功能在视频会议、在线教学等场景中很常见&#xff0c;这种功能如何实现呢&#xff1f;这节课我们就来解决这个问题…

【PostgreSQL】约束-外键

【PostgreSQL】约束链接 检查 唯一 主键 外键 排他 外键 外键是关系数据库中的一个概念&#xff0c;用来建立表与表之间的关系。它是指一个表中的字段&#xff08;或字段组合&#xff09;&#xff0c;用来关联另一个表的主键&#xff08;或唯一标识字段&#xff09;。通过外…

Zuul相关面试题及到案(2024)

1、什么是Zuul&#xff1f;它在微服务架构中有什么作用&#xff1f; Zuul是Netflix开源的一种提供API网关服务的应用程序&#xff0c;它在微服务架构中扮演着流量的前门角色。主要功能包括以下几点&#xff1a; 路由转发&#xff1a;Zuul网关将外部请求转发到具体的微服务实例…

技术发展驱动编程语言走向

未来编程语言的走向可能会受到多种因素的影响&#xff0c;包括技术进步、市场需求、开发人员的偏好和生态系统的演变等。以下是一些可能的发展趋势&#xff1a; 简洁性和易用性 随着技术的进步&#xff0c;编程语言可能会变得越来越简洁和易于使用。一些语言可能会引入更高级的…

【大数据】安装 Zookeeper 单机版

安装 Zookeeper 单机版 下面安装 Zookeeper&#xff0c;由于它是 Apache 的一个顶级项目&#xff0c;所以域名是 zookeeper.apache.org&#xff0c;所有 Apache 的顶级项目的官网都是以项目名 .apache.org 来命名的。 点击 Download 即可下载&#xff0c;这里我们选择的版本是 …