序言

现有的微服务模式需要再业务代码中集成大量基础设施模块,比如注册中心,服务发现,服务调用链路追踪,请求熔断,重试限流等等,使得系统过于臃肿重量级。

Dapr作为新一代微服务模式,使用sidecar模式,简化了现有微服务系统代码,将基础设施层以sidecar模式分离,使得开发人员更集中于业务逻辑编写。

本文以net6和dapr1.5为基础,搭建一个dapr的简单使用示例。

1、安装Docker

Dapr的运行依赖于Docker环境。

作为学习环境,使用Centos 7系统安装Docker。

安装Docker推荐使用daocloud一键安装命令:

curl -sSL https://get.daocloud.io/docker | sh

安装完成后运行命令:

[root@localhost ~]# docker -v
Docker version 20.10.11, build dea9396

显示对应的Docker版本即安装成功。

2、安装Dapr CLI

官方解释:Dapr CLI 是您用于各种 Dapr 相关任务的主要工具。 您可以使用它来运行一个带有Dapr sidecar的应用程序, 以及查看sidecar日志、列出运行中的服务、运行 Dapr 仪表板。

下载Dapr CLI

wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash

验证安装情况

dapr -v

输出以下内容即安装成功。

CLI version: 1.5.0
Runtime version: 1.5.0

由于国内网络问题,使用官方的Dapr安装方法一般会遇到各种问题,因此把dapr下载下来,通过脚本进行安装。

修改hosts文件

vi /etc/hosts
140.82.114.4 github.com
199.232.69.194 github.global.ssl.fastly.net
140.82.114.9 codeload.github.com

刷新缓存

yum install -y nscd
service nscd restart

首先需要安装Git,然后执行以下命令:

git clone -v https://gitee.com/Two-Twoone/dapr-installer.git
cd dapr-installer/
./install.sh

虽然还是很慢,但是总比下不了好多了。

上面命令启动了几个容器,运行下列操作来验证:

[root@localhost dapr-installer]# docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}"
CONTAINER ID NAMES PORTS
a0565f609846 dapr_placement 0.0.0.0:50005->50005/tcp, :::50005->50005/tcp
c392f5cf7a18 dapr_redis 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp
2ee68c450b29 dapr_zipkin 9410/tcp, 0.0.0.0:9411->9411/tcp, :::9411->9411/tcp

3、安装Net6 SDK

 rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm
yum update
yum install dotnet-sdk-6.0

4、创建应用程序

创建2个项目分别为 product,cart 引用Dapr

dotnet add package Dapr.AspNetCore

Program.cs中对 的 AddDapr 调用将 DaprClient 类注册到 ASP.NET Core注入系统。 注册客户端后,现在可以将 的实例注入服务代码, DaprClient 以与 Dapr sidecar、构建基块和组件进行通信。

builder.Services.AddControllers().AddDapr();

4.1、服务调用

在微服务系统中,服务与服务间的调用必不可少,难点主要集中在服务所在位置,发生错误时如何重试,负载均衡等问题。

Dapr中使用sidecar 作为服务的反向代理模块来解决这些问题。

prodcut项目增加下列代码

    [Route("api/[controller]")]
[ApiController]
public class ProductController : ControllerBase
{
private ILogger<ProductController> _logger; public ProductController(ILogger<ProductController> logger)
{
_logger = logger;
}
private static readonly List<string> products = new List<string> { "aa", "bb", "cc", "dd", "ee", "ff", "gg", "hh", "ii", "jj", "kk", "ll", "mm", "nn" };
[HttpGet]
public ActionResult Get()
{
_logger.LogInformation($"调用了获取商品方法");
string[] temps = new string[5];
for (int i = 0; i < 5; i++)
{
Random random = new Random(Guid.NewGuid().GetHashCode());
temps[i] = products[random.Next(0, products.Count - 1)];
}
return Ok( temps);
}
}
# 启动Product 项目
dapr run --app-id ProductDemo --app-port 5010 --dapr-http-port 7015 -- dotnet /root/www/product/Dapr.Product.Sample.dll --urls "http://*:5010"

cart项目增加下列代码,dapr支持http,grpc调用方式,这里以常用的webapi为例,使用http方式调用。

InvokeMethodAsync方法中appid对应的就是dapr run 中的appid,无需关系调用地址。

    [Route("api/[controller]")]
[ApiController]
public class CartController : ControllerBase
{
private readonly DaprClient _daprClient;
private readonly ILogger<CartController> _logger;
public CartController(DaprClient daprClient, ILogger<CartController> logger)
{
_daprClient = daprClient;
_logger = logger;
} [HttpGet]
[Route("GetProducts")]
public async Task<IActionResult> GetProducts()
{
_logger.LogInformation($" Cart 获取商品");
var products = await _daprClient.InvokeMethodAsync<List<string>>(HttpMethod.Get, "ProductDemo", "/api/Product/GetAll");
return Ok(products);
}
}

将程序上传到linux服务器,运行程序

# 启动 Cart 项目
dapr run --app-id CartDemo --app-port 5020 --dapr-http-port 7025 -- dotnet /root/www/cart/Dapr.Cart.Sample.dll --urls "http://*:5020"

调用接口,可以看到Cart项目几乎没有代码入侵就实现了接口调用。

[root@localhost ~]# curl -X 'GET'   'http://192.168.2.110:5020/api/Cart/GetProducts'
["aa","bb","cc","dd","ee","ff","gg","hh","ii","jj","kk","ll","mm","nn"]

Dapr内部使用了mDns进行了服务注册发现和负载均衡,部署多个product后调用,可以看到轮询调用效果。

在自承载模式下,Dapr 使用 mDNS 查找它。在 Kubernetes 模式下运行时,Kubernetes DNS 服务确定地址。

在调用失败和瞬态错误的情况下,服务调用会执行自动重试,Dapr 默认是开启了重试,所以接口不支持幂等是十分危险的行为。

4.2、发布订阅

​ 发布订阅模式,主要是用于微服务间基于消息进行相互通信。你可能也会说,这也要拿出来说,我搞个RabbitMQ/Kafka就是了,

原来我们都会根据使用的组件引入不同的sdk,不同的消息队列监听、消费模式还不一样。

​ Dapr 提供了一个构建基块,可显著简化实现发布/订阅功能,从而和底层基础设施解耦,编写业务逻辑时不需要关心是什么消息队列。

再Program中添加发布订阅支持

        app.UseCloudEvents();
app.UseEndpoints(endpoints =>
{
endpoints.MapSubscribeHandler();
});

订阅消息,使用Topic特性,传递pubsub和主题名称

        [Topic("pubsub", "newUser")]
public ActionResult subUserInfo(UserInfo us)
{
_logger.LogInformation($"接收到订阅消息 id:{us.id} name:{us.name},age:{us.age},sex:{us.sex}");
return Ok("处理完毕");
}

发布消息,使用dapr公开方法PublishEventAsync,传递pubsub和主题名称,以及消息体

    	[HttpPost]
public async Task<IActionResult> PubUserInfo(UserInfo us)
{
await _daprClient.PublishEventAsync("pubsub", "newUser", us);
return Ok();
}

消息发布订阅组件支持RabbitMQ,Redis,Kafka等。

4.3、状态管理

​ Dapr 默认使用 Redis 作为状态存储。它也支持MongoDB,PostgreSQL,SQL Server等。

它不会对上层暴露底层用的那个中间件,也就是说在不同环境下可以使用同一套代码来使用不同的中间件。

       [HttpPost]
[Route("SaveUserList")]
public async Task<IActionResult> SaveUserList()
{
var temps = new List<UserInfo>
{
new UserInfo("小红",1,true,Guid.NewGuid().ToString()),
new UserInfo("小黄",1,true,Guid.NewGuid().ToString()),
new UserInfo("小蓝",1,true,Guid.NewGuid().ToString())
};
await _daprClient.SaveStateAsync("statestore", "UserList", temps);
return Ok(1);
}
[HttpGet]
[Route("GetUserList")]
public async Task<IActionResult> GetUserList()
{
var list = await _daprClient.GetStateAsync<List<UserInfo>>("statestore", "UserList");
return Ok(list);
}
[HttpGet]
[Route("DeleteUserList")]
public async Task<IActionResult> DeleteUserList()
{
await _daprClient.DeleteStateAsync("statestore", "UserList");
return Ok(1);
} public record UserInfo(string name, int age, bool sex, string id);

4.4、链路追踪

传统微服务中,要实现链路追踪,对代码的侵入强。

Dapr 在 Sidecar 中添加了一个 http/grpc中间件。拦截所有应用程序流量,并自动注入关联 ID 以跟踪分布式事务。

使用 Zipkin 协议进行分布式跟踪,无需代码检测,使用可配置的跟踪级别自动跟踪所有流量。

5、总结

​ 本文只是对Dapr做了一个简单示例,对各个组件具体的实现原理没有做过多深入讲解。

​ Dapr区别于传统微服务框架最大的优点就是Sidecar 。以前的微服务框架都需要代码项目引用微服务相关的一些类库,无论是服务注册发现、熔断、配置等都是要调用对应类库实现,这些类库是运行在微服务的进程中的,因此这些类库都需要使用和业务代码一样(或者兼容)的语言来开发,因此是比较重的。

​ 而Sidecar 这种模式,把“注册发现、熔断、配置”等这些微服务的功能都剥离到一个和业务代码的进程相伴而行的独立进程中,业务代码通过http或者grpc等方式和这个Sidecar 进程通讯来完成微服务的相关服务的调用。

​ 显而易见,在Sidecar 这种模式中,业务代码中只有极少数和Sidecar 进程通讯的代码,因此非常轻量级。这样Sidecar 进程中的服务可以独立升级,模块可以自由组合,不会干扰业务代码。同时由于Sidecar 的进程是独立的进程,业务代码和Sidecar 进程通讯是采用http、grpc这样语言无关的协议,因此业务代码可以采用任何语言来进行开发。

福禄·研发中心
熊迪

从零开始,使用Dapr简化微服务的更多相关文章

  1. Blazor+Dapr+K8s微服务之开发环境调试

    1         安装Dapr开发调试环境 1.1         Dapr 完整安装模式不支持开发调试 在上一篇随笔<Blazor+Dapr+K8s微服务之服务调用>中,我们通过为每个 ...

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

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

  3. Dapr是如何简化微服务的开发和部署

    基于微服务设计模式的现代应用程序面临着一系列挑战.微服务需要有一个强大的服务发现机制来实现动态连接.它们需要松散耦合,实现自主性和独立缩放.微服务需要支持多种语言,其中每个服务都是以最合适的语言.框架 ...

  4. Blazor+Dapr+K8s微服务之状态管理

    1         状态管理服务器端接口 1.1         添加Dapr.AspNetCore包 在DaprTest1.Server项目中添加Dapr.AspNetCore包,该包实现了ASP. ...

  5. Blazor+Dapr+K8s微服务之事件发布订阅

    我们要实现的是:在blazorweb服务中发布一个事件,并传递事件参数,然后在serviceapi1服务中订阅该事件,接收到blazorweb服务中发布的事件和参数. 1         在blazo ...

  6. Blazor+Dapr+K8s微服务之基于WSL安装K8s集群并部署微服务

         前面文章已经演示过,将我们的示例微服务程序DaprTest1部署到k8s上并运行.当时用的k8s是Docker for desktop 自带的k8s,只要在Docker for deskto ...

  7. Dapr Actor 的微服务架构

    Dapr中的Actor模型,和Orleans的Virtual Actor一脉相传, 圣杰写过一篇文章Orleans 知多少 | .NET Core 分布式框架介绍过.简单来讲:Actor模型 = 状态 ...

  8. Dapr微服务应用开发系列2:Hello World与SDK初接触

    题记:上篇介绍了Dapr的环境配置,这次我们来动手尝试一下Dapr应用的开发 Hello World Dapr应用的Hello World其实和其他的Hello World一样简单: 首先用你喜欢的语 ...

  9. 【架构】linkerd:来自Twitter为微服务而生的开源RPC解决方案

    大家要如何以规模化方式运维微服务应用程序?实践当中会出现哪些问题,我们又该如何加以解决?在大规模与非预测性工作负载场景当中,我们需要满足哪些条件才能运行一款大型微服务应用程序,而又能够确保不必受到功能 ...

随机推荐

  1. Git学习笔记02-配置

    安装好Git之后,做的就是需要配置Git了 第一步,配置自己的名称和邮箱 打开Git Bash 输入命令 git config --global user.name "用户名" g ...

  2. hdu3507 斜率优化学习笔记(斜率优化+dp)

    QWQ菜的真实. 首先来看这个题. 很显然能得到一个朴素的\(dp\)柿子 \[dp[i]=max(dp[i],dp[j]+(sum[i]-sum[j])^2) \] 但是因为\(n\le 50000 ...

  3. 使用Python写词云数据可视化

    词云的应用场景 会议记录 海报制作 PPT制作 生日表白 数据挖掘 情感分析 用户画像 微信聊天记录分析 微博情感分析 Bilibili弹幕情感分析 年终总结 安装本课程所需的Python第三方模块 ...

  4. 一站式交付体验:云效+Kubernetes

    背景 云效依托于阿里巴巴研发效能多年规模化持续交付,赋能云上开发者专为云端用户提供的一站式研发协作平台.Kubernetes,由Google开源的容器集群管理平台,面向运维侧提供自动化的集群和应用管理 ...

  5. 【UE4 C++ 基础知识】<4> 枚举 Enum、结构体 Struct

    枚举 UENUM宏搭配BlueprintType可以将枚举暴露给蓝图,不使用的话,仅能在C++使用 //定义一个原生enum class enum class EMyType { Type1, Typ ...

  6. 【UE4 C++】 启动 / 关闭外部exe、开启虚拟键盘

    启动/关闭外部exe 引擎自带 FPlatformProcess::CreateProc() FPlatformProcess::TerminateProc() windows api ShellEx ...

  7. 【数据结构与算法Python版学习笔记】递归(Recursion)——优化问题与策略

    分治策略:解决问题的典型策略,分而治之 将问题分为若干更小规模的部分 通过解决每一个小规模部分问题,并将结果汇总得到原问题的解 递归算法与分治策略 递归三定律 体现了分支策略 应用相当广泛 排序 查找 ...

  8. [no code][scrum meeting] Beta 2

    例会时间:5月14日11:30,主持者:乔玺华 下次例会时间:5月15日11:30,主持者:肖思炀 一.工作汇报 人员 昨日完成任务 明日要完成的任务 乔玺华 - 开issue,分配时间 黎正宇 - ...

  9. STM32中断编程三步曲教你弄会中断设置以及中断优先级设置

    中断作为stm32中必不可少的一个功能,其重要性是不言而喻的因此把中断学习好是根本. 所以今天就来好好啃一下中断配置的知识,俗话说:磨刀不误砍柴工.问题是什么呢?项目中我用到了一个触摸键盘TTP229 ...

  10. 页表 Page tables

    逻辑地址与物理地址的转化 页表是由页表项(PTE)组成的数组.512个PTE构成一个页表页(Page-table page). PTE中包含了物理页码(PPN physical page number ...