当前位置:网站首页> 3. Caller 服务调用 - dapr
3. Caller 服务调用 - dapr
2022-06-24 12:48:00 【MASA技术团队】
前言
上一篇我们讲了使用HttpClient的方式调用,那么如果我们现在需要更换为通过dapr实现服务调用,我们需要做哪些事情呢?
Caller.Dapr 入门
如果我们的项目原本使用的是Caller.HttpClient,现在希望改为使用Caller.Dapr,那么我们需要做什么呢?
- 改造Caller 服务调用 - HttpClient的中的服务端,使得服务端支持dapr调用
- 调整客户端代码,使客户端支持通过dapr来做到服务调用,并达到与HttpClient调用相同的结果
准备工作
- 安装.Net 6.0
创建ASP.NET Core 空白解决方案
Assignment03将
Assignment02文件夹下的Assignment.Server复制到Assignment03的文件夹下,然后将项目Assignment.Server添加到解决方案Assignment03中选中
Assignment.Server并安装Masa.Utils.Development.Dapr.AspNetCoredotnet add package Masa.Utils.Development.Dapr.AspNetCore --version 0.4.0-rc1修改
Assignment.Server项目下的Program.cs//忽略命名空间引用var builder = WebApplication.CreateBuilder(args);// 添加DaprStarter,用于服务端启动dapr sidecar,改造服务端支持dapr调用的重点(建议在开发环境下使用,线上环境使用k8s部署)builder.Services.AddDaprStarter(option =>{ option.AppId = "Assignment-Server"; option.DaprGrpcPort = 7007; option.DaprHttpPort = 7008; option.AppIdSuffix = string.Empty;});var app = builder.Build();/// 忽略路由等Q: 什么是DaprStarter?为什么要使用DaprStarter? A: DaprStarter是Masa团队开发出来用于管理Dapr sidecar的包,可以帮助我们在开发环境下很简单的使用dapr sidecar
Q: 为什么要指定AppId、DaprGrpcPort、DaprHttpPort等信息? A: 客户端调用需要得到Dapr的AppId、设置DaprGrpcPort、DaprHttpPort是因为客户端演示项目没有使用dapr sidecar,如果客户端项目也使用dapr sidecar,此处可以不指定DaprGrpcPort、DaprHttpPort,更多信息请参考[文章](https://www.cnblogs. com/zhenlei520/p/16157625.html)
创建ASP.NET Core 空项目
Assignment.Client.DaprClientWeb作为客户端并安装Masa.Utils.Caller.DaprClientdotnet add package Masa.Utils.Caller.DaprClient --version 0.4.0-rc1修改
Assignment.Client.DaprClientWeb项目下的Program.csusing Masa.Utils.Caller.Core;using Masa.Utils.Caller.DaprClient;using Microsoft.AspNetCore.Mvc;var builder = WebApplication.CreateBuilder(args);builder.Services.AddCaller(option =>{ // 注意: 与Caller.HttpClient相比,需要修改的地方 options.UseDapr(masaDaprClientBuilder => { masaDaprClientBuilder.Name = "userCaller"; // 当前Caller的别名(仅有一个Caller时可以不填),Name不能重复 masaDaprClientBuilder.IsDefault = true; // 默认的Caller支持注入ICallerProvider获取(仅有一个Caller时可不赋值) masaDaprClientBuilder.AppId = "Assignment-Server";//设置当前caller下Dapr的AppId });});var app = builder.Build();app.MapGet("/", () => "Hello HttpClientWeb.V1!");app.MapGet("/Test/User/Get", async ([FromServices] ICallerProvider callerProvider) =>{ var user = await callerProvider.GetAsync<object, UserDto>("User", new { id = new Random().Next(1, 10) }); return $"获取用户信息成功:用户名称为:{user!.Name}";});app.MapGet("/Test/User/Add", async ([FromServices] ICallerProvider callerProvider) =>{ var dateTimeOffset = new DateTimeOffset(DateTime.UtcNow); string timeSpan = dateTimeOffset.ToUnixTimeSeconds().ToString(); var userName = "ss_" + timeSpan; //模拟一个用户名 string? response = await callerProvider.PostAsync<object, string>("User", new { Name = userName }); return $"创建用户成功了,用户名称为:{response}";});app.Run();public class UserDto{ public int Id { get; set; } public string Name { get; set; } = default!;}相较于
Assignment.Client.HttpClientWeb,Assignment.Client.DaprClientWeb仅仅是更改了Program.cs,将UseHttpClient改为UseDapr,其余代码无需修改添加环境变量
DAPR_GRPC_PORT,值为7007、DAPR_HTTP_PORT,值为7008Q: 为什么要添加环境变量? A: 由于当前客户端并未使用dapr sidecar,若当前客户端也使用dapr sidecar,此处可以不添加环境变量
现在Caller的HttpClient版本就可以使用了,分别启动Assignment.Server、Assignment.Client.DaprClientWeb服务,浏览器访问http://localhost:5042/Test/User/Get、http://localhost:5042/Test/User/Add,分别输出对应的获取用户信息成功以及创建用户成功的提示,则证明调用成功了

DaprClient 最佳实践
Assignment.Client.DaprClientWeb的写法比较简单,其用法与Assignment.Client.HttpClientWeb基本一致,与Caller.HttpClient类似,DaprClient我们推荐使用下面的写法:
创建ASP.NET Core 空项目
Assignment.Client.DaprClientWeb.V2作为调用方V2版本选中
Assignment.Client.DaprClientWeb.V2并安装Masa.Utils.Caller.DaprClientdotnet add package Masa.Utils.Caller.DaprClient --version 0.4.0-rc1添加类
ServerCallerBase(对应服务端服务)using Masa.Utils.Caller.DaprClient;namespace Assignment.Client.DaprClientWeb.V2;/// <summary>/// 注意:ServerCallerBase是抽象类哟(抽象类不会被DI注册), 与使用Caller.HttpClient相比,需要修改的是继承的基类改为DaprCallerBase/// </summary>public abstract class ServerCallerBase : DaprCallerBase{ protected override string AppId { get; set; } = "Assignment-Server";//设置当前Caller需要请求的服务端项目Dapr的AppId public ServerCallerBase(IServiceProvider serviceProvider) : base(serviceProvider) { }}添加类
UserCaller.csnamespace Assignment.Client.DaprClientWeb.V2;public class UserCaller : ServerCallerBase{ public UserCaller(IServiceProvider serviceProvider) : base(serviceProvider) { } /// <summary> /// 调用服务获取用户信息 /// </summary> /// <param name="id">用户id</param> /// <returns></returns> public Task<UserDto?> GetUserAsync(int id) => CallerProvider.GetAsync<object, UserDto>("User", new { id = id }); /// <summary> /// 调用服务添加用户 /// </summary> /// <param name="userName"></param> /// <returns></returns> public Task<string?> AddUserAsync(string userName) => CallerProvider.PostAsync<object, string>("User", new { Name = userName });}public class UserDto{ public int Id { get; set; } public string Name { get; set; } = default!;}添加环境变量
DAPR_GRPC_PORT,值为7007、DAPR_HTTP_PORT,值为7008
最后,分别启动Assignment.Server、Assignment.Client.DaprClientWeb.V2服务,浏览器访问http://localhost:5102/Test/User/Get、http://localhost:5102/Test/User/Add,分别输出对应的获取用户信息成功以及创建用户成功的提示,则证明调用成功了

常见问题
在开发中我们会遇到各种各样的问题,下面就来列举几个我们项目中遇到的问题:
一个项目在同一个k8s集群部署了两套环境,为什么会出现代码调用混乱(开发环境调用线上环境)?
在于同一个K8s集群下,dapr会将服务组网,并将它们认为是同一个服务(AppId一致的服务)。如何解决同一个k8s集群中调用混乱的问题?
解决方案有两种:1. 将不同环境下的服务分别部署在不同的K8s集群2. 根据环境调整相对应服务的dapr sidecar的配置,其`AppId`的命名规则:`AppId`-`环境名`。修改自定义Caller的规则:public abstract class CustomizeDaprCallerBase : DaprCallerBase{ protected CustomizeDaprCallerBase(IServiceProvider serviceProvider) : base(serviceProvider) { var hostEnvironment = serviceProvider.GetRequiredService<IWebHostEnvironment>(); if (!hostEnvironment.IsDevelopment() || hostEnvironment.IsStaging()) AppId = AppId + "-" + hostEnvironment.EnvironmentName; }}如何修改支持自定义Header?
目前Caller.Dapr不支持自定义Header,目前只能使用`SendAsync`才能自定义Header,不过此功能已经在0.5.0的开发计划中,在0.5.0中会支持
总结
使用Masa提供的Caller服务,有助于我们的项目在前期没有使用Dapr的情况下先利用Caller.HttpClient做缓冲,等后期时机成熟,只需要更改相对应的CallerBase即可,其他代码基本不需要调整,减轻了我们的开发成本,并且不同的Caller仍然可以很灵活的调整超时时间、Header等信息,并且Caller默认提供了处理异常的功能,当调用出错后,会自动抛出异常,让我们可以更专心的处理业务。
但目前Caller还有不足之处,目前Caller.Dapr版针对请求头处理的并不完善,除此之外,目前不支持Content-Type为非Json类型,这块功能会在0.5.0版本中加以支持完善
本章源码
Assignment03
https://github.com/zhenlei520/MasaFramework.Practice
开源地址
MASA.BuildingBlocks:https://github.com/masastack/MASA.BuildingBlocks
MASA.Contrib:https://github.com/masastack/MASA.Contrib
MASA.Utils:https://github.com/masastack/MASA.Utils
MASA.EShop:https://github.com/masalabs/MASA.EShop
MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor
如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

边栏推荐
- Perhaps the greatest romance of programmers is to commemorate their dead mother with a software
- 快速了解常用的消息摘要算法,再也不用担心面试官的刨根问底
- Pycharm中使用Terminal激活conda服务(终极方法,铁定可以)
- Leetcode 1218. 最长定差子序列
- How to make secruecrt more productive
- J'a i ouvert quelques mots d'un ami et quelques réflexions personnelles sur le livre des six ancêtres
- How to solve the problem that MBR does not support partitions over 2T, and lossless transfer to GPT
- 使用 Abp.Zero 搭建第三方登录模块(一):原理篇
- 【概率论期末抱佛脚】概念+公式(不含参数估计)
- “我这个白痴,招到了一堆只会“谷歌”的程序员!”
猜你喜欢
随机推荐
What is SCRM? What is the difference between SCRM and CRM
手把手教你用AirtestIDE无线连接手机!
Interesting erasure code
Party, Google's autoregressive Wensheng graph model
Redis' contribution in the field of microservices
Who is the fish and who is the bait? Summary of honeypot recognition methods from the perspective of red team
天猫618农产品“百强县” 35个县域来自中西部及东北
Use abp Zero builds a third-party login module (I): Principles
我从根上解决了微信占用手机内存问题
Getting started with the lvgl Library - colors and images
初中级开发如何有效减少自身的工作量?
【数据挖掘】期末复习(样卷题目+少量知识点)
Babbitt | metauniverse daily must read: 618 scores have been announced. How much contribution has the digital collection made behind this satisfactory answer
Parti,谷歌的自回归文生图模型
我开导一个朋友的一些话以及我个人对《六祖坛经》的一点感悟
几种常见的DoS攻击
[live broadcast of celebrities] elastic observability workshop
Comparator sort functional interface
Configure Yum proxy
用一个软件纪念自己故去的母亲,这或许才是程序员最大的浪漫吧








