.Net微服务实战之技术选型篇

王者荣耀 

  去年我有幸被老领导邀请以系统架构师的岗位带技术团队,并对公司项目以微服务进行了实施。无论是技术团队还是技术架构都是由我亲自的从0到1的选型与招聘成型的,此过程让我受益良多,因此也希望在接下来的系列博文尽可能的与大家分享我的经验。

  古人有云:将军难打无兵之仗。想要把微服务很好的实施也并非能一个人可以完成的事,一来需要有出色的运维提供支持,二来需要花时间做技术选型与攻关,三来还要开发兄弟们配合实施。因此,这次能顺利实施并不是一个人的王者,而是团队的荣耀。

  框架源码:https://github.com/SkyChenSky/Sikiro (文末有说明)

工欲善其事,必先利其器

  以上是我们公司的技术栈(点击图片可在浏览器打开),除了统一配置中心没有服务器资源和Hangfire还没场景使用外,其他都已经上线使用了。  

  俗话说得好:工欲善其事,必先利其器。一个优秀的工程师应该善于使用框架和工具,在微服务这一块的技术栈选型并非一蹴而就,也是我多次对比验证后,并良好的集成到公司项目然后落地实施。这系列框架单纯这么去用其实是可以无缝集成的,但是在落实项目的时候,我为了集成得更加友好和使用上更加便利,在基础上做了扩展,例如SkyWalking添加Request和Response,CAP与Chloe.ORM的集成等,下文我会逐个分享。

  有需要的朋友可以参照我这套去实施,这样大家就可以花更多的时间把精力放在业务、调优、拆分、设计等方面。

  此外大家看得出,我所有的技术栈基本上找的都是开源社区的比较出名的项目,没有一个属于自研的。这样做的原因:

  •   快速搭建

  •   降低成本

  •   社区支持维护

  •   利于人才引进

  其实可以看出.Net不缺优秀的开源项目,那么实施这么久让我唯一觉得深刻的印象是:缺少整合。

  之前我也跟不少同行讨论过甚至在面试的时候,他们觉得应该自己造一个轮子,原因各种各样,但唯独缺少了希望在开源项目基础上完善下这个原因。我也理解他们的心理,因为“优秀”的工程师应该自己写一套证明下自己。其实我认为这也许是包容心的在作祟,我们应当求同存异,学会接受已经检验过的轮子,在基础上完善您的需要,有必要还可以给社区做贡献,双赢。

原则

  我做技术选型的时候,坚持着三大原则,简单、适合、运维优先

  在满足需求的情况下,优先选择轻量级的框架,因为轻量级总比重量级的易学习,易于扩展,易于理解源码。试想一下,有个框架什么都很齐全,但是学习曲线高,在写一个demo的时候各种踩“坑”找原因,还有可能出了问题不知道怎么解决,除了开始你初认识该框架觉得他很厉害之外,后面使用每走一步都是阻碍和吐槽。

  在有限的资源、人力、时间,我们更新技术的同时还要保证业务的正常开展,我会优先选择我比较熟悉的技术,我会将他们进行封装、优化、集成,尽可能的减少开发人员对技术细节的认知负担,尽可能以他们最熟悉的使用方式提供。此外,我们团队是有运维岗,如果问题由运维解决更快、更方便则优先交给运维,尽可能让开发关注数据流转与业务流程。

  PS:我选型的时候不是一蹴而就的,下文可能我会提到某些框架工具我没有去选择原因,并不是否认它们存在的价值,而绝大问题是这些不适用于我们团队。最后我向伟大的开源项目与其作者致敬。

  

微服务

 

  有一条盛传于我们行业的公式:软件 = 程序 + 软件工程

  程序就是我们经常产出的算法、数据结构、脚本、框架、架构等。

  为什么称之为软件工程?因为这是具有科学方法论引导的、多人协作、有明确目标、有阶段性的。从以前瀑布开发再到10年前盛行的敏捷开发最后到最近几年流行的DevOps,可见开发模式也随着技术架构更新也不停的演进。我们团队选用了原型模式+DevOps模式来应对我们的微服务架构的开发。

  书本的教条主义我就不多说了,我对微服务的理解分为服务

  如何微?微到什么程度?我借助两样东西,合理的系统架构分层DDD思想,两者分别管理架构的纵向拆分与横向拆分。

  架构分层,我采用了前后端分离+多层架构,自顶向下的依赖,各司其职。

  DDD在最近几年非常流行,然而这并非新的技术,十几年前就已经它的出现了。随着微服务盛行,DDD的划分域的化繁为简的思想与微服务的本质-不谋而合,因此DDD也随之热门起来。

  下面是我们的架构图,这个话题在下一篇重点再讨论。

服务

  我接下来用一段话描述一下服务化的需要。首先API网关作为我们请求流量的入口,隔离了外网与内网的作用。接着开发人员得知道如何调用服务,那么可以从注册中心发现已注册的服务的IP地址、端口的列表,这就是服务的注册与发现;接着我们需要知道服务下接口路径、请求与响应的格式,因此我们需要服务描述。满足前面两个条件后,我们就可以进行调用服务了,因此我们需要RPC框架进行服务通信。当服务运作后,我们需要服务监控来监控服务的运行情况以此方便调优。随着服务拆分得越细、跨度越大,服务出问题的时候不容易定位,因此我们需要服务跟踪进行问题定位。

  由上述可见组件主要包括以下6点:

  •   API网关

  •   服务描述

  •   服务注册中心

  •   RPC框架

  •   服务监控

  •   分布式链路跟踪

API网关

  API网关主要起到了隔离内外网、身份验证、路由、限流等作用。我用一个生活的例子搭地铁比喻来描述下:过闸前我们需要经过安检保证客流的安全性,上下班高峰期还会排队进行限流,我们还可以通过看指示牌或者询问工作人员了解到应该往什么方向走,这就是路由。

  我们团队选型了Kong和KongA作为我们的API网关,Kong是一个在Nginx运行的Lua应用程序,由lua-nginx-module实现。Kong和OpenResty一起打包发行,其中已经包含了lua-nginx-module。基本功能有路由、负载均衡、基本认证、限流、跨域、日志等功能,其他功能例如jwt认证可以通过插件进行扩展。

  有人会问为什么不用Ocelot?回答这个问题之前,我首先声明我尊敬Ocelot项目与其开发者。

  1.易用性。需要二次开发,虽然对.Net开发者来说能接受,但不利于运维。

  2.性能。社区很多测试数据,据我了解就是kong 11K,Ocelot 3.5K,四舍五入3倍性能差,作为流量的入口,性能这块我还是比较注重的。

  3.可扩展性,Kong很多功能可以通过插件式按需使用与开发。

 

服务描述

  我们团队采用了Swagger,以此来衔接前后端开发的接口对接,省去了编写接口文档的成本,此外也支持接口调试,让开发效率提高不少。我们的服务都是以HTTP协议提供,对外API用RESTful风格,对内统一以POST的RPC风格提供。

服务注册中心

  服务注册,服务在发布后自动把IP地址与端口注册进服务中心;服务发现,通过调用服务中心的接口获取到某服务IP地址与端口的列表。我们团队选用Consul+Consul Tamplate+nginx,Consul是基于GO语言开发的开源工具,主要面向分布式,服务化的系统提供服务注册、服务发现和配置管理的功能。Consul的核心功能包括:服务注册/发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等特性。

  Consul作为服务注册中心的存在,但是我们服务发现只能拿到IP列表,我们使用RPC调用时还是得做负载均衡算法,于是使用了Consul Tamplate把服务列表同步到nginx的配置,那么RPC框架就无需集成负载均衡算法经过nginx路由。

  开始选型我并没有选择Consul Tamplate,而是选择了fabio的这个中间件。fabio是一个应用于Consul的轻量级、零配置负载均衡路由器,开始用的时候部署起来很方便、很简单。后来上了Skywalking分布式链路跟踪系统,只要经过fabio路由的都无法把调用链串起来,虽然将就的用是没什么问题,但是Skywalking的调用链日志无法很好的展示出来就会影响日后的问题排查。我当时花了两天时间研究与issue提问,并没有很好的结果,所以最后另外选择了Consul Tamplate+nginx。

服务通信

  RPC框架主要三大核心,序列化、通信细节隐藏、代理。协议支持分TCP和HTTP,当然还有两者兼容+集成MQ的。我们选择了WebApiClient做客户端,服务端仍是.Net Core WebAPI,主要考虑到WebAPIClient的轻量、易用,而且和Skywalking、Consul集成方便。我当时用的时候时.Net Core 2.2版本,gRPC并没有集成进来。

  此外我也选择过ServiceStack,ServiceStack的技术栈很全,缺点是依赖得很深,当时试用的时候,它所以依赖的一个底层包ServiceStack.Common的某个类与WebAPI冲突了,所以对于不熟悉该框架的我断定存在依赖污染,无论我需要还是不需要都统统依赖进来了,然而我只是希望要一个简单的RPC框架。此外还需要破解。

  Surging也作为我当时选型的目标,开始也是我抱着最大希望的,因为描述得很牛逼,什么都是全得。然而深入去用的时候,没有一个完整的文档,入门demo也不友好,说实话我驾驭不住只能放弃。

服务跟踪

  市面上的分布式链路跟踪系统基本上都是根据谷歌的dapper论文实现的,基本上都分三大块,UI、收集器、代理(探针),原理大概是把涉及的服务链路的RequestID串起来。

  我们团队选择了SkyWalking作为了项目的分布式链路跟踪系统,原因很简单:易用,无侵入,集成良好。

  实施到我们项目的时候我做了点扩展,把Reqeust、Response、Header、异常给记录了下来,并过滤了部分不需要记录的路径。

分布式事务

  只要在分布式系统,分布式事务必不可缺。

分布式事务
分类理论案例中间件
强一致性ACID二阶段提交msdtc
最终一致性BASE本地消息表CAP

 

  本地消息表是eBay在N年前提出的方案,而CAP以该思想实现的一门框架,原理大概是,本地业务表与消息凭据表作为一个事务持久化,通过各种补偿手段保证MQ消息的可靠性,包括MQ正常发布与消费。

  我花了多天的时间专门测试了该框架可靠性,的确有保证。然而有个地方我认为可以优化,Retry的查询语句条件可以更加严谨点,只需要负责相应的Group进行Retry就好,没必要全部都查询出来,因为这个问题我在测试环境与本地环境共同调试时,刚好两个环境的Group不一致,导致会Retry失败的问题。

框架源码

  写到这里,本篇的分享差不多要结束了,我将开源我们公司的工具库,有需要的朋友可以去使用。

  •   Sikiro.Tookits -公共基础库

  •   Sikiro.Nosql.Redis-StackExchange.Redis的基本封装

  •   Sikiro.Nosql.Mongo-mongodb驱动封装更新、排序等支持lambda

  •   Sikiro.MicroService.Extension-RPC注册,微服务框架-服务注册,终端跟踪忽略

  •   Sikiro.Chloe.Extension-支持多数据、事务封装、分页、IOC

  •   Sikiro.Chloe.Cap-把Chloe,ORM与CAP整合

  额外说明下DotNetCore.CAP.MySql,这个是我从CAP源码拷贝过来然后改了MySql.Data的依赖,原本CAP.MySql是用的MySqlConnector,和我的Chloe.ORM冲突了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310895.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

autohotkey php,Autohotkey+php实现免浏览器听录音

环境说明:Elastix 2.5ln -s /var/spool/asterisk/monitor /var/www/html/接口文件(php):$conmysql_connect("localhost","root","passwd");if(!$con) echo "没有连接成功!";mysql_select_db("asteriskcdrd…

.NET Core开发实战(第32课:集成事件:解决跨微服务的最终一致性)--学习笔记...

32 | 集成事件:解决跨微服务的最终一致性首先看一下集成事件的工作原理它的目的时为了实现系统的集成,它主要是用于系统里面多个微服务之间相互传递事件集成事件的实现方式有两种,一种是图上显示的发布订阅的方式,通过 EventBus&a…

Dapper操作MySQL数据库获取JSON数据中文乱码

在项目中利用Dapper将JSON数据存储到MySQL数据库,结果发现JSON数据中的中文乱码,特此记录,希望对存储JSON的童鞋能有所帮助,文中若有错误之处,还望批评指正。为了引出最终问题出在什么地方,我们重头开始进行…

C++set容器去重法

给出一个10位数&#xff0c;它如果每个位的数都把0-9这10个数都只用了一次&#xff0c;就输出yes&#xff0c;否则输出no #include <iostream> #include <set> using namespace std; set<int>s; typedef long long LL;int main() {LL n;while (cin >>…

流传在程序员中的传说,你知道几个?

在号称从事高薪新 科技工作的程序员周遭流传着许多与他们单纯气质不符的传说在这些被神秘面纱笼罩的人群中即使是从事着同一工种都有着许许多多科学无法解释的差异老程序员们对此讳莫如深新程序员们却有时感到惶恐不安...这背后究竟是道德的扭曲还是人性的沦丧倒是都并不至于只…

C++关于getline()和getchar()的小点

getchar只能“吃”一个字符&#xff0c;而getline可以“吃”一行字符 代码如下: #include <iostream> #include <cstring> using namespace std;int main() {char c;cin>>c;string line;getline(cin,line);cout<<c<<endl;cout<<line<…

谁说docker-compose不能水平扩展容器、服务多实例?

❝虽说我已经从docker-compose走上了docker swarm的邪门歪道&#xff0c;目前被迫走在k8s这条康庄大道&#xff0c; 但是我还是喜欢docker-compose简洁有效的部署方式。❞曾其何时docker-compose非常适合开发、测试、快速验证原型&#xff0c;这个小工具让单机部署容器变得简洁…

.NET Core开发实战(第33课:集成事件:使用RabbitMQ来实现EventBus)--学习笔记(上)...

33 | 集成事件&#xff1a;使用RabbitMQ来实现EventBus这一节我们来讲解如何通过 CAP 组件和 RabbitMQ 来实现 EventBus要实现 EventBus&#xff0c;我们这里借助了 RabbitMQ&#xff0c;它的整个安装和使用的体验是非常人性化的&#xff0c;如果是在 Windows 下开发的话&#…

nginx php iconv,Nginx +PHP部署一

Nginx PHP部署一Alvin.zeng目录一、安装PHP1、Yum安装需要的包yum -y install gcc gcc-c autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurs…

以Blog.Core的方式来打开Abp.vNext

&#xff08;发现Abp这个logo真像佐助写轮眼&#xff09;最近自己的框架已经基本的成型了&#xff0c;当然还有很多质疑的地方&#xff0c;比如这些人是这么说的&#xff0c;基本都是原文&#xff1a;你的教程太乱了&#xff0c;和框架代码都不一样&#xff08;&#xff1f;&am…

算法题目中常见的几种输入小点-gets,cin,scanf,getline,sstream

cin,scanf遇到空格都会停下来。gets可读入空格 例如: 在这里由于要读入空格&#xff0c;我们用gets来读入。 const int N 8; char g[N][N];while(gets(g[0])!NULL) {gets(g[1]);}C关于getline()和getchar()的小点C stringstream输入方式下面这两段代码要注意一下: const in…

如何做好一个开源项目(一)

做好一个开源项目其实是一件比较费时费力费心的工作&#xff0c;它的最大难点除了代码维护之外&#xff0c;还包括后期的维护和持续的跟进。我曾经做过不少开源项目&#xff0c;但是坚持下来的&#xff0c;目前有信心能够持续维护的也只有Magicodes.IE。这里请允许我来一波硬广…

Abp商业版 - Identity Server模块

该模块提供了Identity Server的集成和管理功能.建立在IdentityServer4类库之上.管理系统中的客户端,身份资源和API资源(Clients, Identity resources, API resources)设置客户端(Clients)权限.轻松创建标准的身份资源(例如role, profile)创建自定义身份资源(identity resources…

洛谷 P1162 填涂颜色-dfs染色法

输入: 6 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1输出: 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 1代码如下: #include <iostream> using namespace std; const int N 40; int mp[N][N], mps[N][N…

15个基本的C#面试问题

原文来自互联网&#xff0c;由长沙DotNET技术社区【笑语】编译。原文来自&#xff1a;https://www.toptal.com/c-sharp/top-10-mistakes-that-c-sharp-programmers-make1、给定一个int数组&#xff0c;编写方法以统计所有偶数的值。有很多方法可以做到这一点&#xff0c;但是最…

洛谷 P1122 最大子树和-求树的最大子树权值和

输入: 7 -1 -1 -1 1 1 1 0 1 4 2 5 3 6 4 7 5 7 6 7输出: 3代码如下: #include <iostream> #include <vector> using namespace std; const int N 100010; typedef long long LL; LL ans; vector<LL>t[N]; LL w[N];void dfs(int root, int fa) {for (int…

玩转控件:封装Dev的LabelControl和TextEdit

缘由俗话说的好&#xff1a;"工欲善其事必先利其器"&#xff0c;作为软件攻城狮也是同样道理&#xff0c;攻城狮开发的软件目的是简化客户的操作&#xff0c;让客户动动手指就可以完成很多事情&#xff0c;减少人力成本。这也是系统/软件存在的目的。那对于攻城狮来说…

洛谷 P1958 上学路线-dfs

输入: 5 4 3 2 2 2 3 4 2输出: 5注意点:这题目输入的时候先输入列&#xff0c;再输入行&#xff0c;这点要注意&#xff0c;然后这个图跟我们二维数组是反过来的&#xff0c;所以题目说得向北走要变成向南走&#xff0c;注意一下细节就好了。 代码如下: #include <io…

.NET Core开发实战(第33课:集成事件:使用RabbitMQ来实现EventBus)--学习笔记(下)...

33 | 集成事件&#xff1a;使用RabbitMQ来实现EventBus为了演示我们的发布和订阅的话&#xff0c;我们在这里的代码做一些稍微的调整namespace GeekTime.API.Application.DomainEventHandlers {public class OrderCreatedDomainEventHandler : IDomainEventHandler<OrderCre…

文本框赋值guide matlab,科学网-Matlab: 学习GUI (使用GUIDE时需注意的几个问题)-刘磊的博文...

在博文《Matlab&#xff1a;学习GUI(一个简单的例子)》(介绍的方法是完全用代码来建立一个GUI&#xff0c;实际上Matlab本身有一个设计GUI的交互系统——GUIDE&#xff0c;用户可以使用该系统更方便的建立自己的GUI.本文将介绍使用GUIDE时常常遇到的几个问题。本文将通过一个简…