云原生电商微服务实战3:Asprise和Dapr集成
2024-09-28 13:27:01一、云原生介绍
-
概念
什么是云原生:云原生是一种软件开发的方法论,通俗来说就是在云计算环境中构建和运行应用程序 云计算的特点:弹性、可扩展、高可用 云原生关键点:
- 容器化
- 微服务
- 动态管理
- CI/CD
- 弹性(容错机制)
- 服务网格
- 容器编排工具
- 监控平台(指标、链路、日志)
-
云原生开发与微服务开发 理念(云原生:云计算为基础;传统:固定的硬件) 架构(云原生:微服务、Serverless;传统:单机架构) 开发流程(云原生:CI/CD;传统:瀑布模型(没有自动化)) 基础设施(云原生:通过代码管理依赖资源、自动配置;传统:事先准备、手动配置) 服务发现通信(云原生:环境中自带服务发现;传统:Consul、自己实现、提前准备、依赖硬编码、配置文件) 监控和日志(云原生:环境中准备好了各种监控、日志、链路追踪;传统:过于依赖外部组件) 资源利用率(云原生:动态调整资源;传统:资源分配不灵活)
-
开发环境和生产环境
云原生概要已经提出很久了,k8s可以说就是上就是一个云原生环境。
我在2019年的时候就开始带团队成员来是公司的微服务转型,那个时候都是自己手撸k8s环境,各种中间件集群也是自己搭建的。
那个时候云原生环境确实不好,虽然各大云平台都提供直接的paas产品,但很多收费都较贵,最终还是自己搭建。
更重要的时候本地开发时虽然安装的docker版本,提供了mini k8s环境,但是开发和部署的环境还是不一样的。
二、Asprise介绍
如果能有一个让开发和部署都是云原生的,开发的时候不需要自己去安装mysql,redis;同时开发的时候也不需要知道最终部署的是阿里云,还是微软云。只要开发环境是基于云原生的,开发环境拥有云原生的特性,比如监控、日志、链路追踪。部署的时候只需要提供对应的云产品的产品就能让系统跑起来,这种跑起来的方式,可能就是提供一个连接字符串就可以了。如果有这样的环境,那云原生开发将变得简单很多。
对的,这就是我们当前的明星出场的时候了。
.NET Aspire是NET 8.0 LTS提出的一套开发工具,它的作用就是构建和运行云原生应用程序。
特点:
-
Dashboard - 中心化的应用监控和探查:F5 启动 .NET Aspire可显示服务的统一视图以及它们的日志、指标和跟踪。
-
组件 Component:开发Asprise应用时,我们无需再在本地安装需要用到的组件环境了,比如mysql、redis。只需要为启动的服务体提供配置,告诉Asprise该服务需要用到MongoDb、RabbitMQ等。
-
Azure 专用组件:这里我们用不到,但还是介绍一下,Asprise提供了专用的Azure组件,可以将应用直接部署到Azure,并且本地开发也能直接只用Azure云服务,比如Blob、Cosmos DB等
-
服务发现:使用Asprise后,开发人员不需要在配置服务发现,而且每一个服务Asprise还提供一个代理,这样我们启动某个服务的时候可以直接生成集群,跨服务的请求通过代理来访问集群
-
生成k8s部署清单,当然这个目前还存在bug,但生成出来的清单稍做修改基本就可以用了。
Asprise它很新,每天都在变化,目前我只研究到这里。还有很多特点我可能还不知道,期待Asprise更好的发展。
三、创建Asprise项目
-
环境:.NET 8.0、Docker桌面版、VS 2022最新
-
创建aspire文件夹,再创建名为DDM.DHT.AppHost的.NET Asprise应用主机项目。添加下列引用
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.0" /> <PackageReference Include="Aspire.Hosting.MongoDB" Version="9.2.0" /> <PackageReference Include="Aspire.Hosting.MySql" Version="9.2.0" /> <PackageReference Include="Aspire.Hosting.RabbitMQ" Version="9.2.0" /> <PackageReference Include="Aspire.Hosting.Redis" Version="9.2.0" />
-
添加DDM.DHT.UserService.HttpApi和DDM.DHT.UserService.MigrationWorker项目引用
-
在Program.cs中添加下列代码,用于创建组件:
var mysql = builder.AddMySql("dht-mysql") .WithPhpMyAdmin(resourceBuilder => resourceBuilder.WithLifetime(ContainerLifetime.Persistent)) .WithLifetime(ContainerLifetime.Persistent); var mongo = builder.AddMongoDB("dht-mongo") .WithMongoExpress() .WithLifetime(ContainerLifetime.Persistent); var redis = builder.AddRedis("dht-redis") .WithRedisInsight(resourceBuilder => resourceBuilder.WithLifetime(ContainerLifetime.Persistent)) .WithLifetime(ContainerLifetime.Persistent); var username = builder.AddParameter("username", secret: true); var password = builder.AddParameter("password", secret: true); var rabbitmq = builder.AddRabbitMQ("rabbitmq", username, password, 5672) .WithManagementPlugin() .WithLifetime(ContainerLifetime.Persistent);
上面代码分别创建了mysql、mongodb、redis和rabbit MQ组件,开发者本地不需要安装这些。
-
创建UserServiceBuilder.cs文件,代码如下:
public static class UserServiceBuilder { private const string MasterDb = nameof(MasterDb); private const string SlaveDb = nameof(SlaveDb); public static void AddUserService(this IDistributedApplicationBuilder builder, IResourceBuilder<MySqlServerResource> mysql, IResourceBuilder<RedisResource> redis, IResourceBuilder<RabbitMQServerResource> rabbitmq) { var db = mysql.AddDatabase("dht-user"); var appId = builder.Configuration["AppId:UserService"]; ArgumentNullException.ThrowIfNull(appId); var migration = builder.AddProject<DDM_DHT_UserService_MigrationWorker>("user-service-migration") .WithReference(db, MasterDb) .WaitFor(mysql); builder.AddProject<DDM_DHT_UserService_HttpApi>(appId) .WithReference(db, MasterDb) .WithReference(db, SlaveDb) .WaitFor(mysql) .WithReference(redis) .WaitFor(redis) .WaitFor(rabbitmq) .WaitForCompletion(migration); } }
这个文件是用户微服务的配置,以后每个微服务都单独创建一个Builder类来配置。
文件中我们可以看到,参数分别传入了mysql、redis、rabbitmq,这是用户服务需要用到的中间件。
创建的migration对象是DDM.DHT.UserService.MigrationWorker项目的实例,这是用来在Asprise的mysql组件中,自动去实现迁移。
最后就是builder的链式代码,分别添加了mysql写库、读库、redis、rabbitmq等资源后,再自动启动项目。
-
在Program.cs添加UserServiceBuilder方法的调用
builder.AddUserService(mysql, redis, rabbitmq);
-
设置DDM.DHT.AppHost为启动项目
-
创建名为DDM.DHT.ServiceDefaults的Aspire服务默认值项目,这个项目创建后什么都不用管,也不用写代码
-
让DDM.DHT.UserService.HttpApi和DDM.DHT.UserService.MigrationWorker引用Aspire服务默认值项目
我们看项目的Program类,里面有代码 builder.AddServiceDefaults(); 这个就是Aspire服务默认值项目提供的。
-
集成下一个主题Dapr后,就可以使用AppHost F5启动项目了。
四、集成Dapr
-
Dapr 是一个便携的事件驱动运行时环境,帮助开发者轻松构建在云端和边缘运行的高可用、无状态和有状态应用程序,并支持多种编程语言和开发框架。通过利用 sidecar 架构的优势,Dapr 帮助您解决构建微服务时的挑战,并保持代码与平台无关。
-
安装:
-
Dapr CLI运行时,我们项目安装的是 Dapr CLI v1.15.1.msi
https://github.com/dapr/cli/releases
-
C盘会自动有个dapr的文件夹
-
dapr init,执行后可以看到docker里面出现4个容器
-
dapr --version 验证一下
-
双击dapr.exe启动
-
-
User微服务集成Dapr
-
在DDM.DHT.AppHost项目添加文件夹DaprComponents,我们先添加一个configuration.yaml文件
apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: redis-config spec: type: configuration.redis version: v1 metadata: - name: redisHost value: localhost:6379 - name: redisPassword value: ""
这个文件启动一个Dapr的配置构建块,使用redie来存储配置
-
在Program类,添加Dapr文件目录
var daprSidecarOptions = new DaprSidecarOptions { ResourcesPaths = ["./DaprComponents"] };
-
在UserServiceBuilder类启动User项目时,启动Dapr
builder.AddProject<DDM_DHT_UserService_HttpApi>(appId) .WithReference(db, MasterDb) .WithReference(db, SlaveDb) .WaitFor(mysql) .WithReference(redis) .WaitFor(redis) .WithEnvironment("APP_API_TOKEN", appApiToken) .WithDaprSidecar(daprSidecarOptions) .WaitFor(rabbitmq) .WaitForCompletion(migration);
这里的参数daprSidecarOptions是通过方法参数传进来的
builder.AddUserService(mysql, redis, rabbitmq, appApiToken, daprSidecarOptions);
-
五、项目演示
- 项目启动展示
- 图中我们可以看到,Asprise为不仅项目启动了mongdb、mysql、redis和rabbit MQ中间件,还同时启动了phpmyadmin、redis-insight等管理工具。
- 同时启动了user-service-httpapi项目,生成了https和http两个访问端口。
- 并且还生成了该api项目的 dapr-cli代理,可以用这个代理来访问api集群,只不过我们目前只生成了一个userapi项目。
- 项目user-service-migration的状态是Finished,说明启动api项目时同时也启动了数据库迁移项目,并且在完成项目迁移后关闭了该项目,所以状态是Finished。
-
点击api项目5196端口访问进去,输入/scalar/v1。我们可以看到api接口页面,这个页面是取代swagger的。.net 9 swagger ui不支持了,本项目使用来代替。我们已经在DDM.DHT.HttpApi.Common功能项目集成Scalar了,下图是访问效果。
我们再选择创建用户,点击右边的Test按钮,输入json传入参数,即可调试接口
-
项目左侧菜单,我们可以看到还有控制台、结构化、跟踪、指标等页面,这些是Asprise为项目生成的查看日志、链路追踪、系统指标等功能,这些功能无需开发者再去实现了,让本地开发变得更容易。
上图部分数据就是显示了调用创建用户接口产生的数据。