.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/containercompatFROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443COPY ./ ./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/containercompatFROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443COPY ./ ./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 注册IHttpClientFactorybuilder.Services.AddHttpClient("local", config =>
{config.BaseAddress = new Uri("http://localhost");
});
builder.Services.AddHttpClient();#endregionvar 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.0docker run -d -p 8061:80 --name orderapi2 ordercontainer:1.0docker run -d -p 8062:80 --name orderapi3 ordercontainer:1.0

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

 docker run -d -p 8050:80 --name productapi1 productcontainer:1.0docker run -d -p 8051:80 --name productapi2 productcontainer:1.0docker 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.mzph.cn/news/597388.shtml

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

相关文章

C# 使用Microsoft消息队列(MSMQ)

写在前面 Microsoft Message Queuing (MSMQ) 是在多个不同的应用之间实现相互通信的一种异步传输模式&#xff0c;相互通信的应用可以分布于同一台机器上&#xff0c;也可以分布于相连的网络空间中的任一位置。 使用消息队列可以实现异步通讯&#xff0c;无需关心接收端是否在…

海康威视摄像头+服务器+录像机配置校园围墙安全侦测区域入侵侦测+越界侦测.docx

一、适用场景 1、校园内&#xff0c;防止课外时间翻越围墙到校外、从校外翻越围墙到校内&#xff1b; 2、通过服务器摄像头的侦测功能及时抓图保存&#xff0c;为不安全因素提供数字化依据&#xff1b; 3、网络录像机保存监控视频&#xff0c;服务器保存抓拍到的入侵与越界&am…

UI自动化Selenium iframe切换多层嵌套

IFRAME是HTML标签&#xff0c;作用是文档中的文档&#xff0c;或者浮动的框架(FRAME)。iframe元素会创建包含另外一个文档的内联框架(即行内框架)。 简单来说&#xff0c;就像房子内的一个个房间一样&#xff1b;你要去房间里拿东西&#xff0c;就得先开门&#xff1b; 如上图…

指针大礼包5

三、程序改错 共10题 &#xff08;共计100分&#xff09; 第1题 &#xff08;10.0分&#xff09; 题号:72 难度:中 第8章 /*------------------------------------------------------- 【程序改错】 ---------------------------------------------…

出现 No such instance field: ‘XXXX‘ 的解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 作为一个全栈的开发玩家,需要调试前后端的数据传输,方便发现问题所在! 在debug整个项目的时候,检查传输数据的时候,发现前端可以传输,但是后端一直拿不到 出现如下问题:No such instance field: parentModel 截图…

UI5与后端的文件交互(四)

文章目录 前言一、后端开发1. 新建管理模板表格2. 新建Function&#xff0c;动态创建文档 二、修改UI5项目1.Table里添加下载证明列2. 实现onClickDown事件 三、测试四、附 前言 这系列文章详细记录在Fiori应用中如何在前端和后端之间使用文件进行交互。 这篇的主要内容有&…

Leetcode的AC指南 —— 字符串/卡码网:55. 右旋字符串

摘要&#xff1a; Leetcode的AC指南 —— 字符串/卡码网&#xff1a;55. 右旋字符串。题目介绍&#xff1a;字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k&#xff0c;请编写一个函数&#xff0c;将字符串中的后面 k 个字…

灸哥问答:数据结构对软件开发的作用

在软件开发的浩瀚海洋中&#xff0c;数据结构如同一座坚固的灯塔&#xff0c;为开发者指明方向&#xff0c;确保他们在构建复杂系统时不会迷失。数据结构不仅仅是编程的基础&#xff0c;更是高效、稳定、可扩展软件的核心。 一、提升算法效率 数据结构与算法紧密相连&#xf…

Java实战项目三:图书借阅系统

文章目录 一、实战概述二、知识点概览&#xff08;一&#xff09;数据库操作及连接&#xff08;二&#xff09;对象关系映射&#xff08;ORM&#xff09;&#xff08;三&#xff09;业务逻辑处理 三、思路分析&#xff08;一&#xff09;系统架构设计&#xff08;二&#xff09…

antd——a-date-picker——日期的限制功能——js基础积累

antd——a-date-picker——日期的限制功能——js基础积累 禁用日期一、限制只能选明天及之后的日期&#xff08;今天不可选中&#xff09;二、限制只能选今天及之后的日期&#xff08;今天可选中&#xff09;三、限制只能选昨天及之前的日期&#xff08;今天不可选中&#xff0…

Java业务功能并发问题处理

业务场景&#xff1a; 笔者负责的功能需要调用其他系统的进行审批&#xff0c;而接口的调用过程耗时有点长&#xff08;可能长达10秒&#xff09;&#xff0c;一个订单能被多个人提交审批&#xff0c;当订单已提交后会更改为审批中&#xff0c;不能再次审批&#xff08;下游系…

05-微服务-RabbitMQ-概述

RabbitMQ 1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&am…

canvas设置文字阴影

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

实现JavaScript中的数组排序功能

一、引言 在JavaScript中&#xff0c;数组是一种常用的数据结构&#xff0c;而排序是处理数组的常见任务。对于JavaScript中的数组排序&#xff0c;我们可以通过多种方式来实现。本篇博客将详细介绍如何使用JavaScript实现数组排序功能&#xff0c;并分享一些感悟。 二、实现…

SQL的一些基本语句

SQL&#xff08;Structured Query Language&#xff09;是一种用于管理关系型数据库的语言。下面是一些常用的SQL基本语句&#xff1a; 创建表格&#xff1a; CREATE TABLE table_name (column1 datatype,column2 datatype,column3 datatype,... );插入数据&#xff1a; INSERT…

详解C语言入门程序:HelloWorld.c

#include <stdio.h> // 头文件&#xff0c;使用<>编译系统会在系统头文件目录搜索在C语言中&#xff0c;#include 是预处理指令&#xff0c;用于将指定的头文件内容插入到当前源文件中。这里的 <stdio.h> 是一个标准库头文件&#xff0c;其中包含了与输入输出…

MySQL之CRUD、常见函数及union查询

目录 一. CRUD 1.1 什么是crud 1.2 SELECT(查询) 1.3 INSERT(新增) 1.4 UPDATE(修改) 1.5 DELETE(删除) 二. 函数 2.1 常见函数 2.2 流程控制函数 2.3 聚合函数 三. union与union all 3.1 union 3.2 union all 3.3 具体不同 3.4 结论 四. 思维导图 一. CRUD 1.1 什么是crud…

解析:Eureka的工作原理

Eureka是Netflix开源的一个基于REST的的服务发现注册框架&#xff0c;它遵循了REST协议&#xff0c;提供了一套简单的API来完成服务的注册和发现。Eureka能够帮助分布式系统中的服务提供者自动将自身注册到注册中心&#xff0c;同时也能够让服务消费者从注册中心发现服务提供者…

【愚公系列】2023年12月 HarmonyOS应用开发者高级认证(完美答案)

&#x1f3c6; 作者简介&#xff0c;愚公搬代码 &#x1f3c6;《头衔》&#xff1a;华为云特约编辑&#xff0c;华为云云享专家&#xff0c;华为开发者专家&#xff0c;华为产品云测专家&#xff0c;CSDN博客专家&#xff0c;CSDN商业化专家&#xff0c;阿里云专家博主&#xf…

express框架搭建后台服务

express 1. 使用express创建web服务器&#xff1a;2. 中间件中间件分类&#xff1a; 3.解决跨域问题&#xff1a;1. CORS2.JSONP 1. 使用express创建web服务器&#xff1a; 1. 导入express2. 创建web服务器3. 启动web服务器// 1. 导入express const express require(express)/…