前言

上一篇我们讲了使用HttpClient的方式调用,那么如果我们现在需要更换为通过dapr实现服务调用,我们需要做哪些事情呢?

Caller.Dapr 入门

如果我们的项目原本使用的是Caller.HttpClient,现在希望改为使用Caller.Dapr,那么我们需要做什么呢?

  1. 改造Caller 服务调用 - HttpClient的中的服务端,使得服务端支持dapr调用
  2. 调整客户端代码,使客户端支持通过dapr来做到服务调用,并达到与HttpClient调用相同的结果

准备工作

  1. 创建ASP.NET Core 空白解决方案Assignment03

  2. Assignment02文件夹下的Assignment.Server复制到Assignment03的文件夹下,然后将项目Assignment.Server添加到解决方案Assignment03

  3. 选中Assignment.Server并安装Masa.Utils.Development.Dapr.AspNetCore

    dotnet add package Masa.Utils.Development.Dapr.AspNetCore --version 0.4.0-rc1
  4. 修改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)

  5. 创建ASP.NET Core 空项目Assignment.Client.DaprClientWeb作为客户端并安装Masa.Utils.Caller.DaprClient

    dotnet add package Masa.Utils.Caller.DaprClient --version 0.4.0-rc1
  6. 修改Assignment.Client.DaprClientWeb项目下的Program.cs

    using 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.HttpClientWebAssignment.Client.DaprClientWeb仅仅是更改了Program.cs,将UseHttpClient改为UseDapr,其余代码无需修改

  7. 添加环境变量DAPR_GRPC_PORT,值为7007DAPR_HTTP_PORT,值为7008

    Q: 为什么要添加环境变量?

    A: 由于当前客户端并未使用dapr sidecar,若当前客户端也使用dapr sidecar,此处可以不添加环境变量

现在Caller的HttpClient版本就可以使用了,分别启动Assignment.ServerAssignment.Client.DaprClientWeb服务,浏览器访问http://localhost:5042/Test/User/Gethttp://localhost:5042/Test/User/Add,分别输出对应的获取用户信息成功以及创建用户成功的提示,则证明调用成功了

DaprClient 最佳实践

Assignment.Client.DaprClientWeb的写法比较简单,其用法与Assignment.Client.HttpClientWeb基本一致,与Caller.HttpClient类似,DaprClient我们推荐使用下面的写法:

  1. 创建ASP.NET Core 空项目Assignment.Client.DaprClientWeb.V2作为调用方V2版本

  2. 选中Assignment.Client.DaprClientWeb.V2并安装Masa.Utils.Caller.DaprClient

    dotnet add package Masa.Utils.Caller.DaprClient --version 0.4.0-rc1
  3. 添加类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)
    {
    }
    }
  4. 添加类UserCaller.cs

    namespace 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!;
    }
  5. 添加环境变量DAPR_GRPC_PORT,值为7007DAPR_HTTP_PORT,值为7008

最后,分别启动Assignment.ServerAssignment.Client.DaprClientWeb.V2服务,浏览器访问http://localhost:5102/Test/User/Gethttp://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,欢迎联系我们

Caller 服务调用 - Dapr的更多相关文章

  1. 3. Caller 服务调用 - dapr

    前言 上一篇我们讲了使用HttpClient的方式调用,那么如果我们现在需要更换为通过dapr实现服务调用,我们需要做哪些事情呢? Caller.Dapr 入门 如果我们的项目原本使用的是Caller ...

  2. 让服务调用更简单 - Caller.HttpClient

    前言 绝大多数项目都离不开服务调用,服务的调用方式通常是基于Http.RPC协议的调用,需要获取到对应服务的域名或者ip地址以及详细的控制器方法后才能进行调用,如果项目需要支持分布式部署,则需要借助服 ...

  3. Dapr微服务应用开发系列3:服务调用构件块

    题记:这篇开始逐一深入介绍各个构件块,从服务调用开始 原理 所谓服务调用,就是通过这个构件块让你方便的通过HTTP或者gRPC协议同步调用其他服务的方法,这些方法也是通过HTTP或者gRPC来暴露的. ...

  4. Dapr 客户端 搭配 WebApiClientCore 玩耍服务调用

    使用Dapr 客户端 处理服务调用,需要遵循的他的模式,通常代码是这个样子的: var client = DaprClient.CreateInvokeHttpClient(appId: " ...

  5. Blazor+Dapr+K8s微服务之服务调用

    1.1         Dapr环境配置 1.1.1        在开发机安装Docker Desktop并启用Kubernetes 安装过程略,安装好后效果如下:(左下角两个绿色指示Docker和 ...

  6. Dapr实战(二) 服务调用

    服务调用是什么 在分布式应用程序中的服务之间进行调用会涉及到许多挑战. 例如: 维护其他服务的地址. 如何安全地调用服务. 在发生短暂的 暂时性错误 时如何处理重试. 分布式应用程序调用链路追踪. 服 ...

  7. Dapr + .NET Core实战(十二)服务调用之GRPC

    什么是GRPC gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架. gRPC 的主要优点是: 高性能轻量级 RPC 框架. 协定优先 API 开发,默认使用协议缓冲区,允许与语言无关的 ...

  8. 手把手教你学Dapr - 4. 服务调用

    上一篇:手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序 介绍 通过使用服务调用,您的应用程序可以使用标准的gRPC或HTTP协议与其他应用程序可靠.安全地通信. 为什么不直接用Ht ...

  9. Dapr初体验之服务调用

    初次理解服务调用 在微服务中,有一个难点就是:如果你想使用各个服务组件,你就得知道不同服务的地址和端口,也就是服务发现. 在传统应用我们是怎么做的?就是在web项目里配置上api地址,如下: 在一个w ...

随机推荐

  1. Java基础之浅谈异常与了解断言

    一.产生错误原因 用户输入错误 设备错误 物理限制 代码错误 二.解决错误---异常 在Java中异常对象都是派生于Throwable类的一个实例. 我们一般将异常分为两种:①Error和②Excep ...

  2. 1.c语言非递归乘法表(帧栈理解)

    1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdbool.h> 4 5 typedef stru ...

  3. 数据库纳管平台DBhouse的技术路线与实践

    为帮助开发者更好地了解和学习前沿数据库技术,腾讯云数据库特推出"DB · TALK"系列技术分享会,聚焦干货赋能创新,邀请数十位鹅厂资深数据库专家每月和您一起深入探讨云数据库的内核 ...

  4. 自己写的一个Hash文件校验软件

    原因 学校网络安全课讲到了Hash函数,老师提了一句上机操作的时候可以用自己的写的文件校验软件,所以我干脆就自己写一个. 说明 支持算法 MD5 SHA1 SHA256 SHA512 SHA384 为 ...

  5. [转载] go get 拉取第三方包过慢、卡住解决方案

    修改go env,选用国内的代理地址下载.

  6. CUDA02 - 访存优化和Unified Memory

    CUDA02 - 的内存调度与优化 前面一篇(传送门)简单介绍了CUDA的底层架构和一些线程调度方面的问题,但这只是整个CUDA的第一步,下一个问题在于数据的访存:包括数据以何种形式在CPU/GPU之 ...

  7. Intellij IDEA 高效使用教程 (插件,实用技巧) 最好用的idea插件大全

    安装好Intellij idea之后,进行如下的初始化操作,工作效率提升十倍. 一. 安装插件 1. Codota 代码智能提示插件 只要打出首字母就能联想出一整条语句,这也太智能了,还显示了每条语句 ...

  8. 安装黑苹果 、 Mac OS虚拟机

    Mac OS 虚拟机 所需文件地址 unlocker 为VMware 新增Apple Mac OS X操作系统 Install_macOS_Monterey_12.0.1_21A559.iso 提取码 ...

  9. 入行IT,一定要会Linux吗?

    现在是21世纪,是科学技术大力发展的一个时代,IT行业已经成为现在的一个非常热门的一个行业,许许多多的人都想要往IT方面发展,找IT方面相关的一个工作.很多想要接触IT行业的初学者伤透了脑筋,我该学什 ...

  10. Docker系列教程04-Docker构建镜像的三种方式

    简介 创建镜像的方法主要有三种:基于已有镜像的容器创建.基于本地模板导入.基于Dockerfile创建. 今天就逐一讲述为大家讲述,如何构建属于自己的docker镜像. 1.基于容器构建镜像 基于已有 ...