.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.如果某个地址的请求连续多次失败了,那么就移除这个地址,下次就不会访问到它了。

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

然而:

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/293356.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis 连接 命令

目录 1.Redis Echo 命令 - 打印字符串简介语法可用版本: > 1.0.0返回值: 返回字符串本身。 示例 2.Redis Select 命令 - 切换到指定的数据库简介语法可用版本: > 1.0.0返回值: 总是返回 OK 。 示例 3.Redis Ping 命令 - 查看服务是否运行简介语法可用版本: > 1.0.0返回…

【Proteus仿真】【Arduino单片机】超声波测距系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用动态数码管、按键、HCSR04超声波、蜂鸣器模块等。 主要功能&#xff1a; 系统运行后&#xff0c;数码管显示超声波检测距离&#xff0c;当检…

Apache网页优化

本章主要介绍如何对Apache网页进行优化 Apache 网页压缩Apache 网页缓存Apache 隐藏版本信息Apache 网页防盗链 目录 1、网页压缩与缓存 1.1、网页压缩 &#xff08;1&#xff09;gzip介绍 &#xff08;2&#xff09;HTTP压缩的过程 &#xff08;3&#xff09;Apache的…

Scikit-Learn线性回归(五)

Scikit-Learn线性回归五&#xff1a;岭回归与Lasso回归 1、误差与模型复杂度2、正则化3、Scikit-Learn岭&#xff08;Ridge&#xff09;回归4、Scikit-Learn Lasso回归 1、误差与模型复杂度 在第二篇文章 Scikit-Learn线性回归(二) 中&#xff0c;我们已经给出了过拟合与模型泛…

均匀与准均匀 B样条算法

B 样条曲线的定义 p ( t ) ∑ i 0 n P i F i , k ( t ) p(t) \sum_{i0}{n} P_i F_{i, k}(t) p(t)i0∑​nPi​Fi,k​(t) 方程中 n 1 n1 n1 个控制点&#xff0c; P i P_i Pi​, i 0 , 1 , ⋯ n i0, 1, \cdots n i0,1,⋯n 要用到 n 1 n1 n1 个 k k k 次 B 样条基函数 …

手游开发项目经验简单总结

这是我最近一个完整的手游开发项目的总结信息&#xff0c;请大家指点 目录 引擎 语言 编辑器 项目开发模块规划分 主项目工程&#xff0c;UI资源项目工程&#xff0c;模型场景资源项目工程 热更框架 前后端协议 UI 图集 多语言适配 SLG场景和其他场景 战斗 美术模型资源 人物…

微信怎么删除聊天记录?进来Get常用的操作方法!

微信是国内使用最广泛的社交应用程序之一。在我们使用微信的过程中&#xff0c;删除聊天记录是一个常见的操作。人们会删除一些不需要的聊天记录&#xff0c;以此减少存储空间的占用。微信怎么删除聊天记录&#xff1f;本文将介绍删除聊天记录的常用方法&#xff0c;非常简单&a…

Git保姆级安装教程

Git保姆级安装教程 一、去哪下载二、安装2.1 具体安装步骤2.2 设置全局用户签名 一、去哪下载 1、官网&#xff08;有最新版本&#xff09;&#xff1a;https://git-for-windows.github.io/ 2、本人学习时安装的版本&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1uAo…

被替换的文件怎么找回?3个高效方法分享!

“我在对电脑进行清理时&#xff0c;删除了一些比较重要的文件&#xff0c;为了恢复它们&#xff0c;我重新复制了一些文件&#xff0c;这导致我原先的文件被替换了&#xff0c;这怎么办呢&#xff1f;被替换的文件还能恢复吗&#xff1f;” 在信息化时代&#xff0c;用户的电脑…

Spark四:Spark Streaming和Structured Streaming

简介 Spark Streaming整体流程和DStream介绍 Structured Streaming发展历史和Dataflow模型介绍 Spark Streaming 是一个基于 Spark Core 之上的实时计算框架&#xff0c;从很多数据源消费数据并对数据进行实时的处理&#xff0c;具有高吞吐量和容错能力强等特点。 Spark Stre…

【完整流程】实现STM32+ESP8266+MQTT+阿里云+APP——【第二节-编写STM32程序初步实现ESP8266上云发布订阅消息】

&#x1f31f;博主领域&#xff1a;嵌入式领域&人工智能&软件开发 前言&#xff1a;本节实现&#xff0c;硬件连接STM32与ESP8266&#xff0c;编写STM32程序通过at命令方式实现STM32ESP8266与阿里云物联网平台发布订阅消息&#xff0c;本节最终实现初步的发布订阅消息…

2024年阿里云服务器按年与按带宽活动价格出炉,最低仅需61元/1年

2024年阿里云服务器活动价格出炉了&#xff0c;新版活动价格表分为按年与按带宽两个价格表&#xff0c;按年最高可选择5年&#xff0c;按带宽可选择按量付费带宽和按固定带宽模式&#xff0c;按年活动价格最低61元/1年&#xff0c;按照带宽模式最低365.33元/1年&#xff0c;不同…

深情回忆VB编程之路

深情回忆VB编程之路 在上世纪90年代末&#xff0c;我与编程世界的初次邂逅始于QBasic这一古老而亲切的语言。那时的编程环境虽然简陋&#xff0c;但却蕴藏着无穷的可能性&#xff0c;每个字符仿佛跳跃着生命&#xff0c;在黑色屏幕中编织出我对编程世界最初的憧憬和梦想。 时光…

计算机网络学习笔记(5)——运输层

本文继续整理计算机网络体系架构知识内容。今日主讲——运输层。 网络层只把分组发送到目的主机&#xff0c;但是真正通信的并不是主机而是主机中的进程。 运输层提供了应用进程间的逻辑通信。运输层向高层用户屏蔽了下面网络层的核心细节&#xff0c;使应用程序看 见的好像在两…

JavaScript基本使用方法

JavaScript 是一种脚本语言&#xff0c;常用于 Web 开发。这里是一些基本的使用方法&#xff1a; 声明变量&#xff1a; 在 JavaScript 中&#xff0c;可以使用 var、let 或 const 关键字来声明变量。例如&#xff1a; var age 25; let name "John"; const PI …

【51单片机】点亮第一个LED灯(含创建文件等基础操作)

51单片机现在不仅是电子信息专业学生的必修课&#xff0c;也是进入嵌入式领域的踏脚石。 本系列将会按照江科大的视频进行&#xff0c;也算是相当于一个笔记&#xff0c;进行巩固 实现第一个LED灯的点亮其实并不复杂&#xff0c;重要的是有一些准备工作比较繁琐&#xff0c;就…

Android RecyleView 使用 Gilde 加载图片引发的卡顿问题

Glide 是一个用于 Android 的图片加载和缓存库。它可以帮助开发者快速、高效地加载网络图片、本地文件和视频帧&#xff0c;并且能够自动缓存图片数据&#xff0c;减少网络请求。Glide 具有良好的性能和易用的 API&#xff0c;支持常见的图片加载需求&#xff0c;例如图片压缩、…

局部与整体的关联特性,如图所示

局部与整体的关联特性是指事物的局部部分与整体之间存在一定的关联关系。它强调整体是由局部构成&#xff0c;局部又反向影响整体。具体包括以下几个方面的特性&#xff1a; 互依性&#xff1a;局部与整体相互依赖&#xff0c;一个的变动会影响另一个的变动。局部的变化会对整体…

bootstrap5实现蛋糕店网页Bakery设计模板

一、需求分析 蛋糕店的网页通常是指蛋糕店的官方网站。这些网页的功能可以因店铺而异&#xff0c;但一般会包含以下内容&#xff1a; 主页&#xff1a;主页通常是网站的起点&#xff0c;展示店铺的品牌形象、特色蛋糕和推广信息。主页通常会设计成吸引人眼球、易于导航的页面。…

vue3 vuedraggable draggable element must have an item slot

vue3vite 看官网使用这种<template #item“{ element }”> <draggablev-model"myArray"start"onStart"end"onEnd":sort"false"item-key"id"draggable".item"handle".mover" ><template…