首先感谢张队@geffzhang公众号转发了上一篇文章,希望广大.neter多多推广dapr,让云原生更快更好的在.net这片土地上落地生根。

目录:
一、通过Dapr实现一个简单的基于.net的微服务电商系统

二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

四、通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布
附录:(如果你觉得对你有用,请给个star)
一、电商Demo地址

二、通讯框架地址

  书接上回,今天来分享一下这套电商demo的通讯部分到底是如何工作的,看看它是如何屏蔽与dapr繁琐的沟通工作让开发者专注于解决业务问题的。

  首先我们再回顾一下dapr的sidecar是如何与应用相互协同的。和istio类似,dapr的sidecar注入可以分为自动注册和手动注册,下面以手动加注解注册的方式我们来聊一聊dapr的工作逻辑。首先当我们设置一个应用(deployment)的时候,在template-metadata配置了dapr相关注解之后,凡是安装dapr集群的k8s会自动将dapr的sidecar注册到我们的pod中,如下图:

当服务启动后,我们可以用kubectl describe po xxx的方式看到当前该pod会产生两个容器:

  凡是了解k8s的开发人员应该知道。在同一个pod之中,container实例之间的通讯应该是基于同一个虚拟内网的,通俗的说就是两者通讯可以直接通过localhost:port的方式,这是dapr与应用交互的基础。和istio通过iptables 来做流量劫持让Envoy代理可以拦截所有的进出Pod的流量,即将入站流量重定向到 Sidecar,再拦截应用容器的出站流量经过 Sidecar 处理的方案相比,dapr选择了一个更加灵活的方式,也就是它只是主动暴露一个端口(默认3500),将是否和dapr通讯的选择权留给了应用本身。

  当我们发起一个rpc请求时,实际上我们是通过http(or grpc这里不展开)的方式,访问了了http://localhost:3500/v1.0/{invoke}/{servicename}/method/{path} 这么一个地址。sidecar通过解析这个地址得到远程服务名{servicename},以及一个谓词{invoke}以及远程服务的endpoint:{path}。它会通过内部的dns服务名查询servicename得到一个该服务在集群内的实例列表,通过负载均衡的方式发起一个下游调用。这个下游调用也并非直接像普通k8s应用内通过调用service name的方式去调用下游pod的container,而是访问下游pod内的sidecar,通过sidecar再去访问pod内的应用实例。他们之间的调用关系如图所示:  

  通过这样的设计,实际上应用只需要和daprd这个sidcar打交道即可。同时dapr实现了通过谓词解析成不同的服务类型实现。比如服务间调用通过谓词:{invoke}、状态读写的谓词是{state}、订阅发布的谓词是{publish}、{subscribe},几乎所有的行为都可以分解成谓词+服务名+endpoint这种模式(所有api可参考:https://docs.dapr.io/reference/api/),这也是实现这套通讯框架的基础。

  所以剩下的事情就比较简单了,dapr通讯基于http/grpc,所以我们只需要启动一套kestrel+httpclient or grpc service/client即可简单快捷的接入dapr。首先我们还是看看整个repo(https://github.com/sd797994/Oxygen-Dapr)的结构:

  Oxygen这部分主要是包含通用工具层、IOC依赖注入(基于autofac)、本地代理生成器ProxyGenerator。Client主要包含一些远程服务attr标记以及客户端代理工厂。而在Mesh这个单独分层里主要是对Dapr的Actor实现了相关封装、Service层比较简单,只是在hostbuilder启动了一个kestrel并获取所有标记了远程服务的接口来构建路由字典方便将我们的Application服务暴露成restapi。

  本地代理ProxyGenerator实现比较简单,使用了微软自带的代理类DispatchProxy。通过Autofac依赖注入接口的时候将接口和代理类实现注册到ioc容器中,这样当我们通过IServiceProxyFactory.CreateProxy时实际上是从ioc容器中拿到的DispatchProxy实例,这样调用任意该接口的方法都会被路由到DispatchProxy实例,从而实现方法拦截并最终通过RemoteMessageSender类型里的HttpClient发起对dapr的sidecar请求。

  Client层的ServerProxyFactory也比较简单,其实就三个东西,一个是IServiceProxyFactory,这个主要用于发起对远程rpc和actor的调用、一个是IEventBus以及IStateManager,分别用于发布事件和调用dapr的状态管理器。

  Service.Kestrel层主要是通过启动时由RequestDelegateFactory.CreateDelegate的方式将所有注册为remoteservice的接口实现为其构造一个Func<Tservice, Tin, Task<Tout>>这样的匿名委托并将其路由键和该委托注册到一个全局静态字典中。当收到请求时通过kv键值对的方式查询当前key(router)对应的匿名委托,并通过ioc容器构造一个Tservice实例(为什么要请求时创建一个实例?因为这样可以模拟MVC创建controller的方式将Tservice作为一个scope生命周期的对象创建出来,避免Tservice内部的构造函数依赖的非单例对象生命周期失效)

  整个请求收发流程如下:

    1、当客户端通过IServiceProxyFactory.CreateProxy<IxxxServcice>()时获取到该接口的DispatchProxy实例。

    2、实例解析各种参数后发起一个http调用,http请求localhost:3500的sidecar后等待回调。

    3、sidecar将请求组装后发给下游sidecar并由下游sidecar转发给pod内的应用。

    4、应用收到请求后解析path得到对应的RequestDelegate,调用RequestDelegate将请求打到具体的xxxServcice服务上,由服务完成具体的业务。

  Mesh.Dapr则是对Actor行为的一个具体封装,由于原始的dapr sdk需要继承BasicActor然后进行各种actor作业,我采用了另外一种方式,通过emit静态代理的方式创建了一个Actor服务,由其代为接收actor请求后再转发给具体的xxxServcice。同时这个Actor服务会启动一个timer,当timer到期时会进行一次model的版本检查,当版本变化后(一般是由于xxxServcice被调用),会通知xxxServcice继承自基类并重写的SaveData方法,由xxxServcice自身考虑是否需要做业务层的持久化(默认Actor代理服务会自动持久化到dapr的状态设备里),这一步是完全异步的并不会阻塞Actor代理原方法的执行,另外在Actor的使用中,我们也尽量避免在同步调用时去读取第三方的设备可能导致IO阻塞actor。在源码中涉及对actor调用xxxServcice异步的支持,我主要参考了async/await生成状态机的方式创建了一个ActorAsyncStateMachine,由该状态机来完成actor服务调用xxxServcice的async/await实现。

  sample包含一对客户端/服务端案例包含上述涉及的所有远程call,大家可以多参考一下。

  Dapr原始提供了一套sdk用于远程服务,该框架主要是用于实现rpc以及对dapr这些api的自定义封装,当使用这套框架后我们就可以不用再考虑创建具体的webapi控制器,由iapplicationservice申明远程服务后框架即可自动生成代理服务即可。

  该框架的实现方式当然还有诸多不完善或者我没考虑到的地方,主要起到一个抛砖引玉的作用,另外也是通过这个来了解dapr是如何统一了我们网络编程模型的,只有更了解dapr才能更好的使用和推广它。惯例,欢迎fork+star:

  https://github.com/sd797994/Oxygen-Dapr

  https://github.com/sd797994/Oxygen-Dapr.EshopSample

通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解的更多相关文章

  1. 通过Dapr实现一个简单的基于.net的微服务电商系统(二十)——Saga框架实现思路分享

    今天这篇博文的主要目的是分享一下我设计Saga的实现思路来抛砖引玉,其实Saga本身非常的类似于一个简单的工作流体系,相比工作流不一样的部分在于它没有工作流的复杂逻辑处理机制(比如会签),没有条件分支 ...

  2. 通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

    之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能--订阅发布 目录:一.通过Dapr实现一个简单的基 ...

  3. 通过Dapr实现一个简单的基于.net的微服务电商系统

    本来想在Dpar 1.0GA时发布这篇文章,由于其他事情耽搁了放到现在.时下微服务和云原生技术如何如荼,微软也不甘示弱的和阿里一起适时推出了Dapr(https://dapr.io/),园子里关于da ...

  4. 通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

    目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实现一个简单的基于.net的微服务电商系统(二)--通讯框架讲解 三.通过Dapr实现一个简单的基于.net的微服务电 ...

  5. 通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

    状态管理和上一章的订阅发布都算是Dapr相较于其他服务网格框架来讲提供的比较特异性的内容,今天我们来讲讲状态管理. 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实 ...

  6. 通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务

    我个人认为Actor应该是Dapr里比较重头的部分也是Dapr一直在讲的所谓"stateful applications"真正具体的一个实现(个人认为),上一章讲到有状态服务可能很 ...

  7. 通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流

    在一般的互联网应用中限流是一个比较常见的场景,也有很多常见的方式可以实现对应用的限流比如通过令牌桶通过滑动窗口等等方式都可以实现,也可以在整个请求流程中进行限流比如客户端限流就是在客户端通过随机数直接 ...

  8. 通过Dapr实现一个简单的基于.net的微服务电商系统(八)——一步一步教你如何撸Dapr之链路追踪

    Dapr提供了一些开箱即用的分布式链路追踪解决方案,今天我们来讲一讲如何通过dapr的configuration来实现非侵入式链路追踪的 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系 ...

  9. 通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权

    Oauth2授权,熟悉微信开发的同学对这个东西应该不陌生吧.当我们的应用系统需要集成第三方授权时一般都会做oauth集成,今天就来看看在Dapr的语境下我们如何仅通过配置无需修改应用程序的方式让第三方 ...

随机推荐

  1. CF102920L Two Buildings【分治】【决策单调性】

    优秀的分治题目.是"2020-2021 ACM-ICPC, Asia Seoul Regional Contest"的一道题. Description There are \(n\ ...

  2. 你真的了解URLEncode吗?

    使用浏览器进行Http网络请求时,若请求query中包含中文,中文会被编码为 %+16进制+16进制形式,但你真的深入了解过,为什么要进行这种转义编码吗?编码的原理又是什么? 例如,浏览器中进行百度搜 ...

  3. 手把手教你Spring Boot2.x整合kafka

    首先得自己搭建一个kafka,搭建教程请自行百度,本人是使用docker搭建了一个单机版的zookeeper+kafka作为演示,文末会有完整代码包提供给大家下载参考 废话不多说,教程开始 一.老规矩 ...

  4. monkey稳定性测试的步骤及策略

    1.adb的作用是什么?adb的全称:android debug bridge 安卓调试桥梁,包含在 Android SDK 平台工具软件包中.通过该命令与设备进行通信,以便进行调试adb可以同时管理 ...

  5. 剑指 Offer 67. 把字符串转换成整数 + 字符串

    剑指 Offer 67. 把字符串转换成整数 Offer_67 题目描述 题解分析 java代码 package com.walegarrett.offer; /** * @Author WaleGa ...

  6. CCF(压缩编码):动态规划+平行四边形优化

    压缩编码 201612-4 一开始看这题还以为是哈夫曼编码的题目,结果是哈夫曼题目的变形. 哈夫曼编码是每次合并任意两堆石子,而这里的题目是合并相邻的两堆石子,而且这里的合并花费是合并两堆石子加上所有 ...

  7. 从JVM底层原理分析数值交换那些事

    基础数据类型交换 这个话题,需要从最最基础的一道题目说起,看题目:以下代码a和b的值会交换么: public static void main(String[] args) { int a = 1, ...

  8. Java 查找算法

    1 查找算法介绍 在 java 中,我们常用的查找有四种: 1) 顺序(线性)查找 2) 二分查找/折半查找 3) 插值查找 4) 斐波那契查找   2 线性查找算法 有一个数列: {1,8, 10, ...

  9. WeihanLi.Npoi 1.16.0 Release Notes

    WeihanLi.Npoi 1.16.0 Release Notes Intro 最近有网友咨询如何设置单元格样式,在之前的版本中是不支持的,之前主要考虑的是数据,对于导出的样式并没有支持,这个 is ...

  10. 确保某个BeanDefinitionRegistryPostProcessor Bean被最后执行的几种实现方式

    目录 一.事出有因 二.解决方案困境 三.柳暗花明,终级解决方案 第一种实现方案 第二种实现方案 第三种实现方案 四.引发的思考 一.事出有因 ​ 最近有一个场景,因同一个项目中不同JAR包依赖同一个 ...