.NetCore部署微服务(一)

news2024/9/28 13:26:43

目录

前言

什么是微服务

微服务的优势

微服务的原则

创建项目

在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.coloradmin.cn/o/1363793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】之搭建 PostgreSQL 环境

前言 在 Linux 系统下安装 PostgreSQL&#xff0c;可以选择快捷方便的 Docker 安装&#xff0c;但正常的服务器都是直接原生安装的&#xff0c;所以&#xff0c;这里我将讲解如何正常安装 PostgreSQL 以及安装之后的一些配置。如果想了解 Docker 安装的话&#xff0c;可以查看我…

二叉树的经典算法(算法村第八关青铜挑战)

二叉树里的双指针 所谓的双指针就是定义了两个变量&#xff0c;在二叉树中有需要至少定义两个变量才能解决问题。这两个指针可能针对一棵树&#xff0c;也可能针对两棵树&#xff0c;姑且也称之为“双指针”。这些问题一般与对称、反转和合并等类型题相关。 判断两棵树是否相…

004-Zynq实现SD卡存储灰度图片(彩色图片存储正点已开源)

文章目录 前言一、为什么参考ov7725照相机实验存储不了灰度&#xff1f;二、SD卡实现步骤1.配置Zynq核中的SD卡接口2.PS端勾选xilffs3.PS端代码4.读卡器读取SD卡结果呈现 总结 前言 最近在弄SD卡存储灰度图片&#xff0c;参考了正点原子的OV7725照相机实验&#xff0c;但发现最…

网安入门09-Sql注入(绕过方法梳理)

ByPass SQL注入ByPass是指攻击者通过各种手段绕过应用程序中已经实施的SQL注入防御措施&#xff0c;例如输入恶意数据、修改请求头等方式&#xff0c;绕过过滤、转义、限制等操作&#xff0c;从而成功地执行恶意SQL语句。攻击者使用SQL注入ByPass技术可以让应用程序的防御措施…

为什么Java中“1000==1000”为false,而”100==100“为true?

大家好&#xff0c;我是可乐。 在日常编程中&#xff0c;我们经常遇到一些看似简单却隐藏着复杂逻辑的问题。 比如&#xff0c;你是否想过为什么在 Java 中表达式10001000会返回 false&#xff0c;而 100100 却返回 true 呢&#xff1f; Integer a 100; Integer b 100; Sy…

王国维的人生三境界,这一生至少当一次傻瓜

一、人生三境界 古今之成大事业、大学问者&#xff0c;必经过三种之境界。“昨夜西风凋碧树&#xff0c;独上高楼&#xff0c;望尽天涯路。”此第一境也。“衣带渐宽终不悔&#xff0c;为伊消得人憔悴。”此第二境也。“众里寻他千百度&#xff0c;蓦然回首&#xff0c;那人却…

双向数据绑定详细解析(超详细)

文章目录 一、什么是双向绑定二、双向绑定的原理是什么理解ViewModel 三、实现双向绑定实现编译Compile依赖收集 参考文献 一、什么是双向绑定 我们先从单向绑定切入单向绑定非常简单&#xff0c;就是把Model绑定到View&#xff0c;当我们用JavaScript代码更新Model时&#xf…

永磁同步电机的磁场定向控制

目录 概述 通过系统仿真验证行为 探索模型架构 生成用于集成到嵌入式应用程序的控制器 C 代码 指定控制器模型的参考行为 创建 PIL 实现 准备用于 PIL 测试的控制器模型 测试生成的代码的行为和执行时间 结论 此示例说明从电机控制算法生成 C 代码并验证其编译行为和执…

AcWing 998. 起床困难综合症

原题链接 其实上面这一堆就是想说&#xff0c;输入 n,m以及 n 个数和该数所对应的运算&#xff0c;其中运算包括有 与、或、异或 三种&#xff0c;真正的问题就是在所有不大于 m 的数&#xff08;非负数&#xff09;中&#xff0c;对给定的 n 个数都按该数所对应的运算运算一遍…

GFS论文解读(一)——设计概述

介绍 在当今大数据时代&#xff0c;分布式文件系统已经成为处理海量数据的重要工具。而在这个领域中&#xff0c;「GFS&#xff08;Google File System&#xff09;」论文无疑是一篇具有里程碑意义的文献。GFS 由 Google 公司发表于 2003 年&#xff0c;它介绍了 Google 公司内…

C练习——肇事卡车车牌号

题目&#xff1a; 一辆卡车违反交通规则&#xff0c;撞人后逃跑。现场有3人目击事件&#xff0c;但没有记住车牌号&#xff0c;只记住了车号的一些特征。 甲说&#xff1a;“牌照前两位数字是相同的”&#xff0c;乙说&#xff1a;“牌照的后两位数字是相同的&#xff0c;但与…

centos通过yum安装redis

1. 安装yum添加epel源(此步根据环境&#xff0c;如果有源则可跳过&#xff0c;在阿里去可跳过&#xff09; yum install epel-release 2 使用yum安装Redis yum install redis 出现如下图所示的内容 3 Redis配置 vim /etc/redis.conf :set number(显示行号) 61行&#x…

Flume基础知识(十一):Flume自定义接口

1&#xff09;案例需求 使用 Flume 采集服务器本地日志&#xff0c;需要按照日志类型的不同&#xff0c;将不同种类的日志发往不同的分析系统。 2&#xff09;需求分析 在实际的开发中&#xff0c;一台服务器产生的日志类型可能有很多种&#xff0c;不同类型的日志可能需要 发送…

在群晖NAS上搭建私有部署笔记软件——Blossom

一、Blossom 简介 Blossom 是一个需要私有部署的笔记软件&#xff0c;虽然本身定位是一个云端软件&#xff0c;但你仍然可以在本地部署&#xff0c;数据和图片都将保存在本地&#xff0c;不依赖任何的图床或者对象存储。 Blossom | Blossom (wangyunf.com)https://www.wangyun…

Zoho SalesIQ:提高品牌在社交媒体上参与度的实用指南

在当今快节奏的数字世界中&#xff0c;品牌参与度变得比以往任何时候都更加重要。社交媒体在企业与客户互动方面发挥着至关重要的作用&#xff0c;了解如何很好地利用社交媒体来增强品牌参与度至关重要。 正如我们在之前的博客中所了解到的&#xff0c;品牌参与是指在品牌与其…

企业数据库安全管理规范

1.目的 为规范数据库系统安全使用活动&#xff0c;降低因使用不当而带来的安全风险&#xff0c;保障数据库系统及相关应用系统的安全&#xff0c;特制定本数据库安全管理规范。 2.适用范围 本规范中所定义的数据管理内容&#xff0c;特指存放在信息系统数据库中的数据。 本…

原文件名自动编号方法:简单易懂的文件重命名规则

当处理大量文件时&#xff0c;例如图片、文档或其他类型的文件&#xff0c;经常要给它们重新命名&#xff0c;以便于管理和查找。一种常见的方法是使用自动编号&#xff0c;这样可以从001开始&#xff0c;一直编号到最后的文件。这种方法既简单又高效&#xff0c;且易于理解。下…

【读书】《白帽子讲web安全》个人笔记Ⅱ-1

目录 第二篇 客户端脚本安全 第2章 浏览器安全 2.1同源策略 2.2浏览器沙箱 2.3恶意网址拦截 2.4高速发展的浏览器安全 第二篇 客户端脚本安全 第2章 浏览器安全 近年来随着互联网的发展&#xff0c;人们发现浏览器才是互联网最大的入口&#xff0c;绝大多数用户使用互联…

UV机-理光G5六彩一白一光油配置

UV机-理光G5六彩一白一光油配置

【Navigation】global_planner 源码解析

全局规划器 global_planner 功能包 文章目录 global_planner 功能包结构1、plan_node.cpp2、planner_core.cpp3、astar.cpp4、dijkstra.cpp5、quadratic_calculator.cpp6、grid_path.cpp7、gradient_path.cpp8、orientation_filter.cpp全局规划大都基于静态地图进行规划,产生路…