《ASP.NET Core 微服务实战》-- 读书笔记(第3章)

第 3 章 使用 ASP.NET Core 开发微服务

微服务定义

微服务是一个支持特定业务场景的独立部署单元。它借助语义化版本管理、定义良好的 API 与其他后端服务交互。它的天然特点就是严格遵守单一职责原则。

为什么要用 API 优先

所有团队都一致把公开、文档完备且语义化版本管理的 API 作为稳定的契约予以遵守,那么这种契约也能让各团队自主地掌握其发布节奏。遵循语义化版本规则能让团队在完善 API 的同时,不破坏已有消费方使用的 API。

作为微服务生态系统成功的基石,坚持好 API 优先的这些实践,远比开发服务所用的技术或代码更重要。

以测试优先的方式开发控制器

每一个单元测试方法都包含如下三个部分:

  • 安排(Arrange)完成准备测试的必要配置

  • 执行(Act)执行被测试的代码

  • 断言(Assert)验证测试条件并确定测试是否通过

测试项目:
https://github.com/microservices-aspnetcore/teamservice

特别注意测试项目如何把其他项目引用进来,以及为什么不需要再次声明从主项目继承而来的依赖项。

StatlerWaldorfCorp.TeamService.Tests.csproj

Exenetcoreapp1.1

首先创建 Team 模型类

Team.cs

using System;
using System.Collections.Generic;namespace StatlerWaldorfCorp.TeamService.Models
{public class Team {public string Name { get; set; }public Guid ID { get; set; }public ICollection Members { get; set; }public Team(){this.Members = new List();}public Team(string name) : this(){this.Name = name;}public Team(string name, Guid id)  : this(name){this.ID = id;}public override string ToString() {return this.Name;}}
}

每个团队都需要一系列成员对象

Member.cs

using System;namespace StatlerWaldorfCorp.TeamService.Models
{public class Member {public Guid ID { get; set; }public string FirstName { get; set; }public string LastName { get; set; }public Member() {}public Member(Guid id) : this() {this.ID = id;}public Member(string firstName, string lastName, Guid id) : this(id) {this.FirstName = firstName;this.LastName = lastName;}public override string ToString() {return this.LastName;}}
}

创建第一个失败的测试

TeamsControllerTest.cs

using Xunit;
using System.Collections.Generic;
using StatlerWaldorfCorp.TeamService.Models;namespace StatlerWaldorfCorp.TeamService
{public class TeamsControllerTest{TeamsController controller = new TeamsController();[Fact]public void QueryTeamListReturnsCorrectTeams(){List teams = new List(controller.GetAllTeams());}}
}

要查看测试运行失败的结果,请打开一个终端并运行 cd 浏览到对应目录,然后运行以下命令:

$ dotnet restore
$ dotnet test

因为被测试的控制器尚未创建,所以测试项目无法通过。

向主项目添加一个控制器:

TeamsController.cs

using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using StatlerWaldorfCorp.TeamService.Models;namespace StatlerWaldorfCorp.TeamService
{public class TeamsController{public TeamsController(){}[HttpGet]public IEnumerable GetAllTeams(){return Enumerable.Empty();}}
}

第一个测试通过后,我们需要添加一个新的、运行失败的断言,检查从响应里获取的团队数目是正确的,由于还没创建模拟对象,先随意选择一个数字。

List teams = new List(controller.GetAllTeams());
Assert.Equal(teams.Count, 2);

现在让我们在控制器里硬编码一些随机的逻辑,使测试通过。

只编写恰好能让测试通过的代码,这样的小迭代作为 TDD 规则的一部分,不光是一种 TDD 运作方式,更能直接提高对代码的信心级别,同时也能避免 API 逻辑膨胀。

更新后的 TeamsController 类,支持新的测试

using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using StatlerWaldorfCorp.TeamService.Models;namespace StatlerWaldorfCorp.TeamService
{public class TeamsController{public TeamsController(){}[HttpGet]public IEnumerable GetAllTeams(){return new Team[] { new Team("One"), new Team("Two") };}}
}

接下来关注添加团队方法。

[Fact]
public void CreateTeamAddsTeamToList()
{TeamsController controller = new TeamsController();var teams = (IEnumerable)(await controller.GetAllTeams() as ObjectResult).Value;List original = new List(teams);Team t = new Team("sample");var result = controller.CreateTeam(t);var newTeamsRaw = (IEnumerable)(controller.GetAllTeams() as ObjectResult).Value;List newTeams = new List(newTeamsRaw);Assert.Equal(newTeams.Count, original.Count+1);var sampleTeam = newTeams.FirstOrDefault( target => target.Name == "sample");Assert.NotNull(sampleTeam);
}

代码略粗糙,测试通过后可以重构测试以及被测试代码。

在真实世界的服务里,不应该在内存中存储数据,因为会违反云原生服务的无状态规则。

接下来创建一个接口表示仓储,并重构控制器来使用它。

ITeamRepository.cs

using System.Collections.Generic;namespace StatlerWaldorfCorp.TeamService.Persistence
{public interface ITeamRepository {IEnumerable GetTeams();void AddTeam(Team team);}
}

在主项目中为这一仓储接口创建基于内存的实现

MemoryTeamRepository.cs

using System.Collections.Generic;namespace StatlerWaldorfCorp.TeamService.Persistence
{public class MemoryTeamRepository :  ITeamRepository {protected static ICollection teams;public MemoryTeamRepository() {if(teams == null) {teams = new List();}}public MemoryTeamRepository(ICollection teams) {teams = teams;}public IEnumerable GetTeams() {return teams;}public void AddTeam(Team t){teams.Add(t);}}
}

借助 ASP.NET Core 的 DI 系统,我们将通过 Startup 类把仓储添加为 DI 服务

public void ConfigureServices(IServiceCollection services)
{services.AddMvc();services.AddScoped();
}

利用这种 DI 服务模型,现在我们可以在控制器里使用构造函数注入,而 ASP.NET Core 则会把仓储实例添加到所有依赖它的控制器里。

修改控制器,通过给构造函数添加一个简单参数就把它注入进来

public class TeamsController : Controller
{ITeamRepository repository;public TeamsController(ITeamRepository repo){repository = repo;}...
}

修改现有的控制器方法,将使用仓储,而不是返回硬编码数据

[HttpGet]
public async virtual Task GetAllTeams()
{return this.Ok(repository.GetTeams());
}

可从 GitHub 的 master 分支找到测试集的完整代码

要立即看这些测试的效果,请先编译服务主项目,然后转到 test/StatlerWaldorfCorp.TeamService.Tests 目录,并运行下列命令:

$ dotnet restore
$ dotnet build
$ dotnet test

集成测试

集成测试最困难的部分之一经常位于启动 Web 宿主机制的实例时所需要的技术或代码上,我们在测试中需要借助 Web 宿主机制收发完整的 HTTP 消息。

庆幸的是,这一问题已由 Microsoft.AspNetCore.TestHost.TestServer类解决。

对不同场景进行测试

SimpleIntegrationTests.cs

using Xunit;
using System.Collections.Generic;
using StatlerWaldorfCorp.TeamService.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
using System;
using System.Net.Http;
using System.Linq;
using Newtonsoft.Json;
using System.Text;namespace StatlerWaldorfCorp.TeamService.Tests.Integration
{public class SimpleIntegrationTests{private readonly TestServer testServer;private readonly HttpClient testClient;private readonly Team teamZombie;public SimpleIntegrationTests(){testServer = new TestServer(new WebHostBuilder().UseStartup());testClient = testServer.CreateClient();teamZombie = new Team() {ID = Guid.NewGuid(),Name = "Zombie"};}[Fact]public async void TestTeamPostAndGet(){StringContent stringContent = new StringContent(JsonConvert.SerializeObject(teamZombie),UnicodeEncoding.UTF8,"application/json");// ActHttpResponseMessage postResponse = await testClient.PostAsync("/teams",stringContent);postResponse.EnsureSuccessStatusCode();var getResponse = await testClient.GetAsync("/teams");getResponse.EnsureSuccessStatusCode();string raw = await getResponse.Content.ReadAsStringAsync();List teams = JsonConvert.DeserializeObject>(raw);Assert.Equal(1, teams.Count());Assert.Equal("Zombie", teams[0].Name);Assert.Equal(teamZombie.ID, teams[0].ID);}}
}

运行团队服务的 Docker 镜像

$ docker run -p 8080:8080 dotnetcoreseservices/teamservice

端口映射之后,就可以用 http://localhost:8080 作为服务的主机名

下面的 curl 命令会向服务的 /teams 资源发送一个 POST 请求

$ curl -H "Content-Type:application/json" \ -X POST -d \ '{"id":"e52baa63-d511-417e-9e54-7aab04286281", \ "name":"Team Zombie"}' \ http://localhost:8080/teams

它返回了一个包含了新创建团队的 JSON 正文

{"name":"Team Zombie","id":"e52baa63-d511-417e-9e54-7aab04286281","members":[]}

注意上面片段的响应部分,members 属性是一个空集合。

为确定服务在多个请求之间能够维持状态(即使目前只是基于内存列表实现),我们可以使用下面的 curl 命令

$ curl http://localhost:8080/teams
[{"name":"Team Zombie","id":"e52baa63-d511-417e-9e54-7aab04286281","members":[]}]

至此,我们已经拥有了一个功能完备的团队服务,每次 Git 提交都将触发自动化测试,将自动部署到 docker hub,并未云计算环境的调度做好准备。

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

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

相关文章

数据结构----------实现最小堆排序

数据结构----------实现最小堆排序 原理&#xff1a; 来源于---------------趣学数据结构 代码&#xff1a; #include<stdio.h> #include<stdlib.h> #define N 65535//最大个数排序 int r[N] { -1,1,4,590,4,2,8,7,5,89,67,5,2,1,67,86,54 };//存储要排序的数,第…

abp vnext2.0之核心组件模块加载系统源码解析

abp vnext是abp官方在abp的基础之上构建的微服务架构,说实话,看完核心组件源码的时候,很兴奋,整个框架将组件化的细想运用的很好,真的超级解耦.老版整个框架依赖Castle的问题,vnext对其进行了解耦,支持AutoFac或者使用.Net Core的默认容器.vnext依然沿用EF core为主,其余ORM为辅…

最大堆和最小堆排序

最大堆和最小堆排序 原理参考趣学数据结构 代码 #include<stdio.h> #include<stdlib.h> int r[] { -1,1,4,590,4,2,8,7,5,89,67,5,2,1,67,86,54 };//存储要排序的数,第一个元素不存储元素赋值为-1 int length sizeof(r) / sizeof(int);//待排序的数的个数 void s…

wordList01

wordList one 如果存在什么问题请批评指正&#xff01;谢谢

《ASP.NET Core 微服务实战》-- 读书笔记(第4章)

第 4 章 后端服务现实中的服务不可能处于真空之中&#xff0c;大多数服务都需要与其他服务通信才能完成功能。我们将这些支持性服务称为后端服务&#xff0c;接下来我们将通过创建一个新的服务并修改之前的团队服务与这个服务通信&#xff0c;以探索如何创建并消费后端服务。微…

WordList02

WordList 2 如果存在什么问题欢迎批评指正&#xff01;谢谢&#xff01;

CentOS7 安装 Jenkins( 构建 Vue 和 dotNET Core )

之前的自动构建工具 Jenkins 是部署在公司内网的 Windows 服务器上&#xff0c;现在武汉处于非常时期&#xff0c;兄弟们都在家自我隔离&#xff0c;为了远程提交的代码能自动构建&#xff0c;需要在外网的 CentOS 服务器上搭建 Jenkins 环境来进行构建工作。目的产品采用前后端…

shell(希尔排序)

shell&#xff08;希尔排序&#xff09; 原理:参考趣学数据结构 代码&#xff1a; #include<stdio.h> #include<stdlib.h> void shellInsert(int b[],int dk,int length) {//希尔排序以dk的增量插入int j;for (int i 1 dk; i < length-1; i) {b[0] b[i];//哨…

[蓝桥杯2019初赛]等差数列-数列

解题思路: 给你n个数&#xff0c;是某个等差数列的一部分&#xff0c;问该等差数列最小有几项&#xff1f;&#xff1a;((最大数−最小数)/d)1((最大数-最小数)/d)1((最大数−最小数)/d)1,其中d是该等差数列所有&#xff08;所有已知数与最小数差值&#xff09;的最大公因数&am…

Dynatrace成功扩展kubernetes全栈可观察性

软件情报公司Dynatrace&#xff08;NYSE&#xff1a;DT&#xff09;在Perform 2020会议上宣布了对Kubernetes支持的新增强功能。Dynatrace可解释的AI引擎DavisTM现在自动获取其他Kubernetes事件和指标&#xff0c;使其能够在整个Kubernetes集群&#xff0c;容器和工作负载的堆栈…

wordList3

wordList3 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

[蓝桥杯2019初赛]矩形切割-找规律

代码如下&#xff1a; #include <iostream> using namespace std;int main() {int a, b;int ans 1;cin >> a >> b;while (1) {if (a 1 && b 1 || a b)break;if ( a > b) {int c b;b a;a c;}b b - a;ans;}cout << ans << end…

K8S水平伸缩器 - 自动伸缩微服务实例数量

作者&#xff1a;justmine头条号&#xff1a;大数据达摩院微信公众号&#xff1a;大数据处理系统创作不易&#xff0c;在满足创作共用版权协议的基础上可以转载&#xff0c;但请以超链接形式注明出处。为了方便大家阅读&#xff0c;可以关注头条号或微信公众号&#xff0c;后续…

[蓝桥杯2019初赛]质数-质数筛or 水题

法一&#xff1a; 代码如下&#xff1a; #include <iostream> #include <cmath> using namespace std;bool check(int x) {for (int i 2; i < sqrt(x); i)if (x % i 0)return false;return true; }int main() {int n;int ans 0;for (int i 2; i; i) {if (c…

SqlServer 利用游标批量更新数据

SqlServer 利用游标批量更新数据Intro游标在有时候会很有用&#xff0c;在更新一部分不多的数据时&#xff0c;可以很方便的更新数据&#xff0c;不需要再写一个小工具来做了&#xff0c;直接写 SQL 就可以了Sample下面来看一个实际示例&#xff1a;-- 声明字段变量 DECLARE Re…

math:线性代数之行列式

math&#xff1a;线性代数之行列式 提供解题的方法总结 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢!

[蓝桥杯2019初赛]旋转-找规律

代码如下&#xff1a; #include <iostream> using namespace std; const int N 110; int a[N][N];int main() {int n, m;cin >> n >> m;for (int i 1; i < n; i)for (int j 1; j < m; j)cin >> a[i][j];for (int i 1; i < m; i){for (in…

《ASP.NET Core 微服务实战》-- 读书笔记(第5章)

第 5 章 创建数据服务选择一种数据存储由于我坚持要尽可能的跨平台&#xff0c;所以我决定选用 Postgres&#xff0c;而不用 SQL Server 以照顾 Linux 或 Mac 电脑的读者构建 Postgres 仓储在本节&#xff0c;我们要升级位置服务让它使用 Postgres为了完成这一过程&#xff0c;…

腾飞答不忘初心的三个问题

去年的时候有同学在微信上问了我3个问题&#xff0c;我觉得非常有代表性&#xff0c;当时没有回答。主要是通过简短的语言无法全面表述可能会产生歧义&#xff0c;所以决定专门写一篇文章来表达一下我的想法&#xff0c;需要说明的是以下内容只是以我个人的经验给大家一些参考。…

wordList04

wordList04 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;