软件开发中常见的几种不同服务模型包括SaaS(软件即服务)、LaaS(许可即服务)、PaaS(平台即服务)、CaaS(容器即服务)、IaaS(基础设施即服务)和FaaS(功能即服务)。
很多人认为IaaS和FaaS是趋势,是未来软件设计与开发人员的基本必备技能,PowerDotNet和PowerDotNetCore也特别注重这方面的设计开发和积累,目前已经做出了一些尝试、实践和探索。
PowerDotNet和PowerDotNetCore实现的公共服务按照主要功能模块进行划分,可以分为基础设施、框架工具以及业务(微)服务三大类,客户端和前端也小有所成,工作量较为饱和。
根据个人经验,框架类库和各种流行中间件代码质量相对都比较高. 直接原因是需求明确,逻辑变动少,再加上有很多牛人出手或者设计优秀比较具有前瞻性,实现之后,通常都会稳定很多年不变。
PowerDotNet和PowerDotNetCore实现的公共服务虽然不完全等同于框架类库和中间件,但也最大程度将稳定的改动极少的部分抽象提取出来沉淀固化,隔离变化点,面向接口编程应对变化。
面向对象编程的很多原则和规范同样适用于PowerDotNet和PowerDotNetCore,经过积累、改进和优化,PowerDotNet经受了多次大规模实践的检验,也逐渐形成了自己的一套平台技术规约和接入规范。
本文结合自己的开发实践经验,给PowerDotNet和PowerDotNetCore应用开发、部署和运维等事项记个流水账,毕竟积累历史较为悠久且内容丰富,就算淡忘了也能做为参考文档使用,咩哈哈。
在PowerDotNet和PowerDotNetCore中,DBKey是应用的起点和基石,是所有PowerDotNet和PowerDotNetCore应用中唯一需要配置数据库连接串用户名和密码的地方。
一开始DBKeyApi服务在数据库管理平台进行管理,后续开发DataX应用的时候,经过重构和完善,所有关系型数据库、NoSQL、NewSQL等连接和元数据管理都被划分到DataX数据同步平台。
DBKey服务所在的应用名称叫Power.DataX.DBKeyApi,这是PowerDotNet必选应用,基于WebApi开发的服务接口,性能中规中矩,满足绝大多数业务需求。
DBKeyApi需要对敏感字符串进行加密解密处理,目前PowerDotNet和PowerDotNetCore默认直接使用Framework下的DESUtil类(也可配置使用AES),需要在本地配置文件中新增加密和解密组件配置。
PowerDotNet同时还开发了Power.DataX.ThriftDBKeyApi和Power.DataX.GrpcDBKeyApi两个备选应用。
(1)、Power.DataX.ThriftDBKeyApi
这是备选应用,基于Thrift协议开发的服务接口,对比下来,性能最好。
(2)、Power.DataX.GrpcDBKeyApi
这也是备选应用,基于Grpc协议开发的服务接口,对比下来,性能最差。
如果追求极致的性能体验,建议使用Thrift协议,根据我的性能测试对比,Thirft>WebApi>Grpc,默认推荐使用WebApi,毕竟DBKey服务属于数据量极少的字典型应用,访问量并不大。
DBKeyApi是应用的基石接口,不依赖任何外部API服务,不接入配置中心、日志平台和监控平台,也不使用消息队列和分布式缓存,可以说是PowerDotNet和PowerDotNetCore中最简单最稳定的应用。
API服务开发好了总是需要被调用,对于内容敏感的DBKey服务,PowerDotNet做了最为严格的安全规约,调用方有IP和域名白名单限制,所有调用DBKey的应用必须添加应用安全访问审计日志。
在PowerDotNet的服务治理平台中,所有应用对DBKey服务的调用都需要进行严格审核和授权,APP、客户端、前端等应用禁止直接通过内部或者外部网关调用DBKey服务。
DBKey服务支持心跳检查,服务启动后会定时(默认5秒)异步发送心跳包到注册中心,PowerDotNet和PowerDotNetCore服务治理框架对所有服务接口都支持心跳检查。
因为DBKey是根应用,也就是需要被第一个部署和启动的应用,而注册中心是后续依赖DBKey的应用,那么我们就会有疑问:注册中心没有启动的情况下DBKey心跳检查不是一种浪费吗?
这个问题很好解决,一种是直接将PowerDotNet配置的心跳检查设置为禁用,另一种是对心跳检查结果进行特殊code处理,注册中心不启动的情况下,忽略心跳结果,延迟当前间隔的6倍时间再发送心跳。
除了普通接口和页面应用自动内置了心跳健康检查,其他如关系型数据库、NoSQL、Redis、MQ等都自动根据DBKey实现了简易心跳检查(判断连接是否成功),排查问题有立竿见影的效果。
PowerDotNet和PowerDotNetCore从最初的设计开始算起,心跳就是最基础最核心最稳定的功能之一,可以说很多系统可靠性和可用性的基础就是心跳健康检测。
有根应用,就有根服务。根应用和根服务就像核弹一样,可以不用,但不能没有^_^。
DBKeyApi应用下的所有接口都是全局系统根服务,但是PowerDotNet和PowerDotNetCore的系统根服务远远不止DBKeyApi下的服务。
PowerDotNet全局根服务接口名称,不可被修改或清理(移除)。在服务治理平台,根服务被修改时,系统会给出明显的错误提示。
下面根据系统分组的不同列举几个PowerDotNet和PowerDotNetCore中典型的根服务。
(1)、查询DB信息
(2)、查询DBKey和DB类型信息
(3)、清理DBKey本地缓存
(1)、查询系统信息
(2)、查询应用信息(不含应用密钥)
(3)、查询应用密钥 (严格授权访问)
(4)、应用移入集群
(5)、应用移出集群
(1)、查询应用配置信息
(2)、根据版本号增量查询配置
(3)、回调通知已获取最新版本配置的应用和服务器
(4)、查询应用配置的DBKey信息
(1)、添加日志
(2)、添加监控
(1)、查询ETCD路由分组
(2)、刷新ETCD键值对
(1)、注册应用服务器
(2)、下线应用服务器
(3)、注册API服务
(4)、下线API服务
(5)、查询API服务信息
(6)、查询可用的应用部署信息
(7)、心跳检查
(8)、查询服务器信息
(9)、人工注册API服务
(10)、查询缓存统计信息
上面列举的监控预警和ETCD接口都是可选的根服务,PowerDotNet内置了很多开关,如果你有更好的监控预警和一致性方案,完全可以自己按需二次开发。
记录日志API接口,不依赖于平台基础和服务治理根服务接口,也不依赖配置中心,仅依赖于DBKey服务。
DBKey服务不依赖于日志平台,日志平台则直接调用DBKey的服务。日志API接口需要保证极高的性能和稳定性,当然应对业务和流量变化的可控的开关必不可少。
日志平台支持分片查询,建议按照应用进行查询,应用必选,支持全链路调用链跟踪查询。
日志平台支持敏感信息过滤功能,默认情况下,DEV和Test环境可配置为不过滤敏感信息,便于发现并快速排查问题。
记录监控API接口,不依赖于平台基础和服务治理根服务接口,也不依赖配置中心,仅依赖于DBKey服务和日志平台。
和日志API接口非常类似,监控API接口也需要保证极高的性能和稳定性,当然应对业务和流量变化的可控的开关也是必不可少。
应用基础根服务直接调用DBKey服务,同时也依赖日志平台和监控平台,但不需要接入配置中心。
当然日志平台和监控平台是可选的,可按需要进行配置。因为应用基础也是字典型应用,稳定后改动极少,为了保证稳定和性能,依赖的外部服务当然是越少越好。
注意,平台应用基础服务依赖缓存,如使用Redis分布式缓存,建议配置分布式缓存优先 :
<add key="RedisCacheFirst" value="1"/>
配置中心直接调用DBKey服务,同时也依赖日志平台和监控平台,当然日志平台和监控平台是可选的,可按需要进行配置。
配置中心定时异步拉取数据,间隔时间默认为15秒,可以配置ConfigRefreshSeconds达到动态控制的目标,建议是15的整数倍。配置中心通过ETCD或者RDBMS和Redis达到配置“及时”更新的目的。
PowerDotNet创建的默认Power.Platform.RootApp是一个虚拟根应用,主要用于供其他新应用复制配置用,减少开发人员的配置工作量。
PowerDotNet开发的配置中心客户端,对外暴露的类ConfigClientTool和KVConfigService,推荐ConfigClientTool类,但是如果需要取非当前应用的配置,可以使用KVConfigService类。
对于一些缓存数据,建议应用拼接缓存键的时候加上缓存版本CacheVersion,这样可以集中控制数据变更。
通过DBKey服务获取的数据库连接串通常不会修改,所以建议缓存键不带版本CacheVersion,且缓存时间长一点,系统默认缓存一天。
对于需要心跳检查的应用,在配置中心配置心跳健康检查相关参数时,建议将心跳间隔时间设置为缓存时间的五分之一或十分之一,系统数据正常缓存5分钟,心跳间隔时间可以设置为1到2分钟或30秒。
配置中心支持配置自动服务器注册参数,平台部署的服务器,如选择docker容器,请降低docker销毁频率或者优先将docker设置固定ip地址,否则频繁注册服务器容易导致服务不可达问题,毕竟心跳保活有时间间隔。
直接调用DBKey根服务和平台基础根服务,依赖日志平台、监控平台和配置中心,不需要向注册中心注册服务器和接口信息。日志平台和监控平台是可选的,可按需要进行配置。
因为对内网关和注册中心的紧密关系,对内网关又被称为注册中心网关。
和对内网关类似,对外网关直接调用DBKey根服务和平台基础根服务,依赖日志平台、监控平台和配置中心,不需要向注册中心注册服务器和接口信息。日志平台和监控平台是可选的,可按需要进行配置。
但是,对外网关对安全性要求极高,必须严格授权访问的服务接口,添加安全审计日志,而且通过对外网关调用的接口必须要进行签名和token校验,否则网关会直接报错。
对内和对外网关主要业务逻辑差不多,对外网关多了些安全审计需求,通过配置中心的SecureGateway配置可以轻松切换对内或对外网关控制。
支付网关是一种特别典型的对外网关,大中型电商系统几乎都会有完备的支付网关解决方案和实现,在移动互联网时代,支付网关更加不可或缺。
通常根据公司的业务需要,API服务网关可以拆分为线上(对外)网关和线下(对内)网关。
一个非常经典的示例,外部商户系统和内部财务系统都需要调用支付服务,根据实际业务需要,可能外部商户系统走公网通过线上网关调用支付接口,而内部财务系统则走内网通过线下网关调用支付接口。
线上网关可以根据业务规模,继续进行拆分为PC网关、移动端网关、第三方应用网关等。如果业务数据量不大,终端调用也不复杂,可能一个线上网关就足够应对业务需求。
如果业务逻辑复杂,调用量很大,终端类型很多,每种终端的业务逻辑差异也较大,线上网关可能就需要继续拆分。
有些公司还需要为开放平台或者合作商户等开发专用的安全网关,网关选择就更复杂更丰富了。
除了通过网关进行API服务调用,也可以在配置中心配置客户端形式的直接服务调用,支持主流的服务鉴权、负载均衡、黑白名单、限流、熔断等功能。
上图简单展示了业务微服务通过客户端方式调用基础设施微服务,同理,业务微服务之间的调用或者基础设施微服务之间互调也适用。
对于内网应用服务或者追求更高性能的应用服务,推荐使用客户端形式的直接服务调用。
心跳检查主要有推模式和拉模式两种,PowerDotNet两种心跳模式都支持,通过服务端BroadCast广播进行健康检查性能较差,注册中心不采用此方案。
每一个接入注册中心的应用API服务都会被自动赋予一个心跳检查框架服务方法,这个心跳检测根服务仅限内部调用。
应用服务器会主动向注册中心发送心跳包,实现心跳健康检查功能,对于调用客户端或网关而言,从注册中心查询到的心跳正常的应用服务器部署列表被认为是正常可用的。
如果部署的应用服务器返回心跳停止,网关会尝试调用API服务器的心跳接口,超时时间默认为2秒,如果没有返回正常心跳结果,就认为服务真的下线,移除缓存中的部署信息。
如果网关将检测下线的部署服务器移除后,发现所有的部署信息都不存在了,重新读取远程部署服务器信息,做兜底尝试,这个过程都是异步完成,整体性能没有太大影响。
如果网关调用API服务器心跳成功,则网关会调用平台基础心跳接口,代替某个具体的API服务发送一次心跳,这个逻辑主要是为了防止某些Web服务器因为环境或心跳时间间隔不当导致的保活滞后。
注册中心直接依赖对内网关、日志平台和监控平台,间接调用DBKey根服务和平台应用基础根服务。
注册中心基础服务,通过对内网关进行应用服务器、API接口的注册、查询和下线,支持Power.Apix、WebApi、WebService、WCF、Thrift、gRPC和.Net Remoting等形式的RPC接口。
注册中心专门开发了客户端,支持自动注册实体类,实体类的集合类型建议使用具体类型,而不是接口,比如推荐使用List而不是IList。
注册中心强烈建议接口开发过程中不要再使用Hashtable、ArrayList、DataTable和DataSet等类型,也不要使用指代不明的字典和dynamic类型,API接口类型越具体越好。
注册中心客户端目前支持集成Power.Apix、WebApi、WebService、WCF、Thrift、gRPC和.Net Remoting等形式的接口并进行统一网关调用或直接远程调用,减少客户端各种服务调用配置。
注意:注册中心基础服务不需要通过注册中心客户端自动注册服务器和API接口信息,因为这样容易造成循环依赖,虽然注册中心基础服务也是接口。
在PowerDotNet和PowerDotNetCore中,开发API接口服务,可通过ApiCallClassAttribute和ApiCallMethodAttribute两个特性,自动定位唯一服务方法。
但是接口中调用实际方法并不是通过ApiCallMethodAttribute来定位,而是根据注册时反射的服务方法,注册服务的时候有唯一别名和方法名,通常这两个都是相同的。
注册中心的服务治理支持白名单和黑名单功能,黑名单目前已经实现了IP、用户、系统、APP黑名单功能,这些都需要元数据支持,服务治理平台可以配置出万能黑名单功能。
心跳健康检查的优点很明显,通用且实现简单,但在SOA和微服务架构下,服务间通常都是跨进程调用,网络通信往往会面临着各种问题,比如微服务正常,但是网络分区发生故障,导致心跳检查失败。
默认情况下,如果注册中心在60秒内没有接收到某个服务实例的心跳,会自动注销下线该服务实例。为什么是60秒心跳失败就自动下线呢?
因为PowerDotNet和PowerDotNetCore注册中心的默认心跳间隔为15秒,心跳支持3次重试,加上网络延迟和重试等待时间,60秒是一个较为合适易记的数字。
在配置中心我们可以配置心跳健康检查时间间隔(默认15秒),所以60秒只是默认情况,实际的时长应该是应用配置的心跳健康检查间隔时间的4倍。
注册中心除了通过应用心跳健康检查实现服务可用性,也可以配置(配置分组SafeGuard)自我保护机制(参考了Eureka),防止因为网络分区故障心跳健康检查误判导致的服务不可用问题。
因为网络问题导致固定时间内大量服务实例被注销下线,可能会严重威胁整个SOA或微服务架构的可用性,我们宁可将现有的服务节点都保留,也不能盲目注销下线任何健康的服务,这就是所谓的兜底思维。
PowerDotNet注册中心开发的自我保护机制主要逻辑如下:
(1)注册中心在运行期间会去统计15分钟(可配置)内应用服务心跳失败比例,如果心跳失败比例低于80%(可配置),注册中心即会进入自我保护机制(可在配置中心配置开关控制);
(2)进入自我保护机制后,通常认为现在注册列表中的应用服务节点都是稳定可靠的,哪怕是长时间没收到心跳而应该过期的服务节点,也不会再去主动注销移除并下线;
(3)注册中心仍然能够接受新服务的注册和查询请求,并通过ETCD尝试同步数据,但不会强制要求这些新增的服务被全部同步(默认ETCD来同步)到其它节点上,保证当前节点依然可用;
(4)当网络稳定心跳健康检查恢复以后,当前实例新的注册信息会被全部同步到其它节点中,也就是达到注册服务的最终一致性,这时候注册中心自动关闭自我保护机制。
特别注意,如果在自我保护开启后在保护期内刚好有某个服务提供者非正常下线,服务消费者就会有一个无效的服务实例,此时调用这个实例的服务就会失败,服务消费者要有一些容错机制,比如重试等。
目前PowerDotNet和PowerDotNetCore服务消费客户端支持简单重试(默认3次,可配置)和自动切换可用服务实例尝试,如果所有服务实例都调用不通,客户端抛出异常,自动异步发出心跳检查尝试。
在分布式系统中,重复故障可能会导致雪球效应并使整个系统瘫痪。为了限制操作的持续时间,我们可以使用超时机制,因为超时可以防止挂起操作并保持系统响应。
但是,在微服务中合适的超时设置是不可能精确到每个接口方法的,根据个人开发经验,系统里通常都是配置一个大概的全局超时时间或框架默认超时时间。
系统处于高度动态的环境下,一段时间内,某些接口调用多,某些接口调用少,网络带宽占用也随着接口调用而改变,超时时间不可能随着环境和资源变化而动态改变。
有些公司会通过配置中心来自动适配超时时间,但是哪怕可以通过配置中心动态配置超时时间,开发和业务又不可能随时随地修改发布合适的超时配置来适应环境变化。
为了解决静态超时机制的不足,我们可以使用断路器来处理错误,相对灵活动态应对环境变化。
断路器(Circuit Breaker)以现实世界的电子元件命名,因为它们的作用是相同的。断路器的主要工作原理是:
(1)、当特定类型的错误在短时间内多次发生时,断路器会被打开;
(2)、开路的断路器可以防止进一步的请求,就像我们平时所说的电路跳闸一样;
(3)、断路器通常在一定时间后关闭,在这期间可以为底层服务提供足够的空间来恢复。
一些断路器也具有半开状态。在这种状态下,服务发送第一个请求以检查系统可用性,同时让其他请求失败。如第一个请求成功,它将使断路器恢复到关闭状态并使流量流动。否则,它保持打开。
总的来说,断路器的核心功能主要就是三大块:
(1)、调用数据度量统计,比如接口异常或者超时次数等
(2)、维护断路器自身的状态,包括Closed(关闭)、Open(打开)和Half Open(半开)三种状态
(3)、基于前两点保护包裹在断路器中执行的调用
目前PowerDotNet和PowerDotNetCore实现的默认断路器按照接口消费者接口调用异常和超时总次数,进行断路器状态的变更及快速失败返回处理,个人认为这是最简单的断路器实现。
想给接口消费者应用添加调用接口断路器功能,手动在配置中心点点,配置好三个参数发布后就可以正常使用了,可任意控制断路器开关启停,极致的便利。
PowerDotNet和PowerDotNetCore的服务治理平台早期支持RPC和REST两种常见风格的API命名,随着开发迭代积累,越来越发现REST相对RPC没有任何优势,多数情况下反而成为开发和管理的负担。
信奉REST教条的老学究们在口头理论上说的头头是道,开发者却为想URL名、写对接文档以及返回code而苦不堪言,这些明明都是可以通过看RPC接口名和说明就能分分钟搞定的事情。
PowerDotNet和PowerDotNetCore的服务治理平台目前已经从REST邪路回归到RPC正途,尤其是所有基础设施服务做了微服务改造后,极大地降低了开发者心智负担,显著减少了API对接工作量。
RPC风格的API命名也很有讲究和规律,最直接最推崇的命名方式是【公司.产品线.系统.子系统.服务类名.服务方法】或者【公司.系统.子系统.服务类名.服务方法】,看公司规模大小,按需选择接口命名方式。
基础设施即服务。PowerDotNet和PowerDotNetCore现有的基础设施服务已完成微服务改造,在稳定性可靠性高可用性最大程度得到保障的前提下,部署运维方便程度也有了极大提升。
将PowerDotNet和PowerDotNetCore依赖的所有基础设施服务抽象并进行统一管理,能够最大限度的复用,降低开发运营成本,提升开发效率。
下面列举下PowerDotNet和PowerDotNetCore主要应用和服务的依赖关系和启动顺序。
最稳定的接口服务,零依赖。
仅依赖Power.DataX的DBKey服务, 如日志服务使用队列,还需启动队列消费者Power.XLogger.MQConsumer。
依赖Power.DataX的DBKey服务和Power.XLogger,如监控使用队列 ,还需启动队列消费者Power.XMonitor.MQConsumer。
依赖Power.DataX的DBKey服务、Power.XLogger和Power.XMonitor,这个应用包含了应用基础服务和配置中心相关服务。
依赖Power.DataX的DBKey服务、Power.XLogger、Power.XMonitor和Power.Platform
依赖Power.SGS.Gateway、Power.DataX的DBKey服务、Power.XLogger、Power.XMonitor和Power.Platform
PowerDotNet和PowerDotNetCore开发的其他常用框架服务,比如消息队列、缓存、数据同步、定时任务等等,这些应用就无所谓顺序了。
以上可以认为是PowerDotNet和PowerDotNetCore的平台基础设施,搭建好环境并启动服务以后可以按需开发很多种形式的应用。
强烈建议将PowerDotNet和PowerDotNetCore的平台基础设施服务以非Web宿主的形式部署运行起来,这样便于后续编写启动脚本来控制启动顺序。
目前可以通过bat脚本启动这些基础设施服务,对于多机器多容器多集群部署,总体复杂度可控,不过随着外部依赖和部署复杂度的增加,脚本复杂度必然也会随之增加。
PowerDotNet基础设施服务看上去有点多,但如果你折腾过Dubbo、Nacos(Apollo)、Envoy、Redis等自建微服务环境或直接使用SpringCloud全家桶,PowerDotNet实在是太易用太人性化了。
从基础设施服务可以看出,目前PowerDotNet和PowerDotNetCore还是属于轻量级侵入式的名字服务范畴,不支持目前大厂比较流行的非侵入性的服务网格(Service Mesh)。
注:服务网格(Service Mesh)是一个对于业务开发而言“非侵入性”的基础设施层,通常采用边车(SideCar)模式,用于处理服务间通信。典型代表包括Istio和Linkerd,还有后起之秀Dapr。
PowerDotNet和PowerDotNetCore核心功能已成熟稳定,且性能良好,能满足绝大多数业务场景,对于服务网格,正如奥卡姆剃刀原理所说,如无必要,勿增实体,目前没有进化到这个阶段的迫切需求。
PowerDotNet和PowerDotNetCore基础设施在通用性、可用性和易用性方面已经得到了充分验证,遵循KISS原则,简单即是美,反对恐龙设计,坚持自我,我就是我,是颜色不一样的烟火,咩哈哈。
保证基础设施的高可用是PowerDotNet开发的重中之重,目前主要技术选型都有完善的后台管理和监控工具,也有兜底解决方案,比如添加备用节点排除单点,添加可启停配置开关等,这些都是管理后台点点按钮的事情。
网络和IO是计算机上最典型的两个瓶颈,尤其是网络,在互联系统中网络通常是最容易爆出问题的节点,同时也是各个大中小厂工程师们甩锅的万能借口^_^。
PowerDotNet源于SOA架构,主要基础设施原来是两个单体服务,现在也按照微服务架构彻底拆分了,但是相比单体服务,拆分后出现事件的概率反而极低。
原因我猜可能是网络问题有了极大改善,因为千兆网卡对中小公司可以算是标配,现在中大型公司自建IDC多数都是万兆网卡,土豪公司用RDMA网卡也不稀奇。
PowerDotNet和PowerDotNetCore虽然内部基础设施服务较多,调用关系复杂,调用链路冗长,但是在服务治理平台治理下,已经在实践中证明能够保证服务的稳定可靠。
基础设施服务除了要求稳定可靠之外,还要求像普通业务逻辑型API服务一样支持无感横向扩容,服务治理平台支持所有API服务的无感横向扩容,对于分布式场景下的稳定性保障非常有意义。
这部分偏重于框架的工具服务能极大提升开发者工作效率,减少重复建设,比如定时任务调度平台、数据同步平台、缓存平台、消息平台、文件平台等等。
框架工具建议技术选型优先选择成熟稳定用户众多资料齐全的,不迷信大厂或所谓大牛的新作品,当然私下自己学习参考这些新作品毫无问题,但一定要谨慎在生产环境推广使用,否则出现问题自己体会吧。
这些框架工具服务当前都是可选或者可扩展的,默认技术选型都是主流技术产品,预留出可扩展的接口定义,对于丰富PowerDotNet和PowerDotNetCore的技术选型大有裨益。
PowerDotNet和PowerDotNetCore的自动代码生成工具主要包括基于DBKey和自研ORM一键前后端代码生成、服务代理自动生成、配置文件生成和自动心跳集成工具等。
有了这些辅助代码生成工具,对于日常开发工作而言,可以至少减少百分之八十的工作量,解放开发者的双手,让开发人员将更多时间和精力集中放在更有价值的事情上。
我们开发的绝大多数业务逻辑型应用服务天生就会产生依赖关系(比如对保持高度稳定的基础设施产生依赖),尤其是流行的SOA或者微服务架构,有时候调用链的复杂程度非常恐怖。
举例来说,比如支付系统中的信用卡服务,在支付系统内部,信用卡服务依赖支付基础、风控等服务,在公司内部可能还依赖个人用户服务,在公司外部还依赖银行、银联、清算中心等等。
为了编排服务的启动顺序,PowerDotNet参考了网上很多文章,比如Docker-compose、Docker Swarm、Helm、Kustomize、Apache Mesos和Google Kubernetes(K8S)等等解决方案。
经过权衡对比后,还是认为这些解决方案太重太复杂,单单一个重写以支持容器部署就有不少的工作量,更不要说还需要人工写很多易错的启动脚本,和PowerDotNet的初衷背道而驰。
服务编排是PowerDotNet和PowerDotNetCore需要解决的一大技术难题,我个人所服务过的公司没有一家有完美的解决方案,也许K8S是个不错的选择,或者土豪一把直接使用云服务。
所谓傻人有傻福,笨人有笨方法,对于调用关系复杂的应用部署,PowerDotNet提供了简易自检程序,可以在启动服务前,在自检小程序中输入服务名称检测服务是否可达。
同时服务治理平台Power.RegistryCenter提供了快速调用服务助手,可以通过切换应用服务器地址进行心跳健康检查来达到检测服务是否可用的目的,对于一般应用也堪堪够用。
乱花渐欲迷人眼,CRUD特别繁。现在的应用程序越来越呈现出依赖项多,业务流程冗长,调用链复杂等技术特点,由此也产生了很多依赖复杂的开发套件和工具,也出现了Serverless等新的架构模式。
和很多流行的全家桶式开发套件有异曲同工之妙,通过PowerDotNet和PowerDotNetCore的基础设施、框架工具和自动代码生成工具,开发人员可以快速无障碍的流水线式开发业务逻辑型应用程序。
本文不直接比较流行的全家桶套件和PowerDotNet(PowerDotNetCore)的优缺点,看前面本系列的介绍你应该能感受到PowerDotNet和PowerDotNetCore的易用性。
下面简单演示下在PowerDotNet和PowerDotNetCore环境中如何快速开发接入一个新应用,让你直观理解到开发业务逻辑型应用程序是多么快速而幸福的事情。
系统应用平台负责创建系统和应用。
新增应用归属哪个产品线哪个系统,需要和业务部门负责人沟通好,应用开发和负责人都是必填的,后续监控预警发送邮件等都需要这些人员信息。
应用密钥是必须的,对于内网应用,调用非敏感接口通常可以放行,但是如果服务治理中心勾选了验证签名,应用密钥是最重要的验签参数,所以需要应用开发者妥善保管。
如果应用密钥因为安全需要必须进行变更,需要业务部门负责人审核才能修改,否则密钥修改而应用端没有及时更新造成大面积签名失败事件。
系统应用平台可直接初始化应用配置。
所有需要接入配置中心的新应用,都可以在系统应用平台初始化应用配置,系统应用平台提供快捷工具分组拷贝或者导入配置,非常方便。
如果是前端、客户端、APP等不需要接入配置中心的应用,可跳过自动初始化应用配置这一步,当然某些特殊情况下也支持客户端通过网关自动获取配置中心配置。
初始化的配置中,除了通用配置参数,心跳,服务治理,DBKey、RPC、缓存、文件、日志、监控等配置参数应有尽有,开发人员通常点点按钮改几个配置参数就好。
如果配置中心的配置直接复制于同系统下的相似应用,绝大多数情况下一个参数都不用改动,对于开发人员而言简直摸鱼偷懒神器。
特别提醒,日志数据库的DBKey约定都以LogDB_开头,比如:LogDB_Writer_MySQL,LogDB_Writer_PostgreSQL,LogDB_Writer_MongoDB,LogDB_Writer_ElasticSearch。
对于不需要应用自己直接写日志数据库记录日志的情况,可以在配置中心配置日志服务地址,间接通过日志平台记录日志,这也是PowerDotNet推荐的做法。
PowerDotNet有很多脚手架模板,包括WebForms、MVC、Winform、RF、Android、VUE、React、WebApi、WebService、Apix、WCF、.NET Remoting、Hessian、gRPC、Thrift等。
这些脚手架内置了接入PowerDotNet或PowerDotNetCore的默认配置,绝大多数应用只需要对默认配置稍作修改,开箱即用,大大降低了开发人员搭建环境的时间成本。
通过PowerDotNet和PowerDotNetCore,最多5分钟就能创建一个自动集成网关、配置中心、注册中心、缓存、日志、监控等服务的新应用,想想用SpringCloud写个HelloWorld搭建环境要折腾多久。
有了PowerDotNet和PowerDotNetCore基础设施服务和框架工具的强有力的支撑,开发人员写一个两个应用,十个八个应用,几十上百个应用甚至成千上万个应用都不在话下。
(1)、后端应用
我们以一个典型的WebApi服务来举例,新增一个应用,名称叫Power.BaseData.WebApi,那么这个应用的默认配置文件如下:
<?xml version="1.0" encoding="utf-8" ?> <appSettings> <add key="SystemCode" value="BaseData" /> <add key="AppName" value="Power.BaseData.WebApi" /> <!--配置是否本地优先--> <add key="LocalFirst" value="0" /> <!--网关相关配置 支持多个 以;或,分隔开--> <add key="GatewayURL" value="网关1;网关2;网关3"/> <!--API相关配置--> <!--自动推送Api接口服务实体--> <add key="App.AutoPushClazz" value="true" /> <!--服务负载均衡类型 Random表示随机 Polling表示轮询--> <add key="App.LoadBalance" value ="Random"/> <!--服务接口是否为本地部署 调试选项用到 对应的是远程服务器部署--> <add key="App.UseLocal" value ="false"/> <!--应用入口URL 由协议、主机或域名及端口号构成 80或443端口可以省略--> <add key="App.HostUrl" value="http://{ServerIP}:{Port}" /> <!--实际部署宿主应用类型 参考:WebApi、WebService、WCF、WindowsService、WinForm等--> <add key="App.DeployAppType" value="WebApi" /> <!--数据库相关配置--> <add key="DBType" value="MySQL"/> <!--启用的数据库类型 目前支持SQLServer、MySQL和PostgreSQL--> <add key="BaseDataDB_Writer_MySQL" value="BaseDataDB_Writer_MySQL"/> <!--日志数据库类型 目前支持SQLServer、MySQL、PostgreSQL、MongoDB和ElasticSearch--> <add key="LogDB_Writer_MongoDB" value="LogDB_Writer_MongoDB"/> </appSettings>
在系统应用平台我们可以对IP、端口、域名等进行申请和绑定操作。
上面的配置中,App.HostUrl可以通过占位符自动解析应用服务器IP(当然也可以自己直接手动绑定IP或域名),端口号则需要在系统应用平台指定,防止应用程序端口冲突,端口分配必须规范有序。
在开发环境中,我们可以将是否为本地部署App.UseLocal配置为true,这样服务治理平台就知道这是一个调试服务器,暂时不拉人集群给其他服务调用,对开发调试排除干扰非常有用。
一个后端服务,配置文件通常只有上面这么多。其实示例中API相关配置(App.HostUrl必选)、数据库相关配置都是可选的,这些都可以在配置中心处理。也就是说一个后端新应用,配置可精简到只有5个。
如果某些应用的App.HostUrl就没有,PowerDotNet自定义了一套规则,可以像下面这样配置:
<add key="App.HostUrl" value="none://{ServerIP}:noport" />
归根结底,一个新应用,最多只需要SystemCode、AppName、LocalFirst、GatewayURL最后再加一个App.HostUrl这5个配置,不能再多了,这样就能享受到PowerDotNet开发的便利,咩哈哈。
(2)、客户端应用
在PowerDotNet和PowerDotNetCore中,所有非后端应用都必须通过服务治理平台的网关间接调用接口完成交互,所有非后端应用都不能直接调用敏感接口,如DBKey、支付、财务、结算、人员信息等接口。
以一个WinForm程序举例,应用名称叫Power.BaseData.ApixTool,这是一个调用Apix接口的WinForm程序,它的默认配置可能是下面这样的:
<?xml version="1.0" encoding="utf-8" ?> <appSettings> <add key="SystemCode" value="BaseData" /> <add key="AppName" value="Power.BaseData.ApixTool" /> <!--配置是否本地优先--> <add key="LocalFirst" value="0" /> <!--网关相关配置 支持多个 以;或,分隔开--> <add key="GatewayURL" value="网关1;网关2;网关3"/> <!--自动推送Api接口服务实体--> <add key="AppSecret" value="应用密钥" /> </appSettings>
客户端程序默认配置只是把App.HostUrl换成AppSecret,AppSecret内容可以是明文也可以是密文,如果是内网客户端,被调用的服务都没有勾选验证签名和token,AppSecret配置也不是必须的。
(3)、前端应用
最典型的就是Angular、VUE、React、Svelte等SPA单页应用,配置好网关、系统、应用名和应用密钥,通过Axios模板方法调用网关就可以间接通过服务治理平台和后端服务进行数据交互。
上面示例是React应用,对于后端接口,除了验签,可能还会有登录token校验逻辑,PowerDotNet和PowerDotNetCore都支持。
(4)、其他应用
其他应用包括安卓、RF、Pad、iOS等等形式的应用,和客户端应用非常类似,配置好网关、系统、应用名和应用密钥,就可以通过网关间接和后端服务进行数据交互,简直神速。
如果新增的应用是后台服务接口,可通过配置中心自动注册服务接口,也可以在服务治理平台管理后台人工注册服务接口,默认注册的新接口都会自动加入服务消费白名单。
新增应用如果需要调用并消费其他应用的API服务,分两种情况进行处理:
(1)、网关间接调用接口,要看服务治理中心授权的服务是否勾选验签和验证token,如果是外网网关,必须构造签名和token验证,否则接口消费验证不通过。
(2)、客户端直接调用接口,也要看服务治理中心授权的服务是否勾选验签和验证token,当然如果是内网不敏感接口,直接调用即可。
系统应用平台可进行大规模集群管理。
所有后端应用,都可以在系统应用平台将应用拉入某个数据中心的集群,便于集群管理,当然对于一些不需要集群管理的后台管理系统,这不是必须的。
对于所有后端服务接口应用,注册应用API接口后,必须在系统应用平台将应用拉入某个数据中心的集群,进行集群管理,发布时可通过拉入拉出集群控制服务器是否可达。
系统应用进行集群化管理可以实现应用的优雅上线和下线功能,软件可以控制的事情就不要让硬件来做,PowerDotNet能完成的事情就不要让其他软件来做。
如果新增应用在配置中心接入了日志平台,登录日志平台管理后台,自动同步DBKey即可在日志平台查询日志。
如果新增应用在配置中心接入了监控平台,登录监控平台管理后台,可以看到监控收集到的数据,尤其是对于后端应用,监控平台能及时预警发现问题。
我们平常所开发的大部分业务逻辑型应用程序都是数据密集型(data-intensive)而非计算密集型(compute-intensive),也就是说系统的瓶颈通常都来自于对数据的处理而非CPU。
数据库、消息队列、缓存、文件等中间件或软件工具都可以被统称为数据系统,随着技术的不断发展,它们之间的界限也越来越模糊。
比如某些NoSQL数据存储软件可以被当成消息队列用(参考Redis),而消息队列则带有类似数据库的持久保证(比如RabbitMQ、RocketMQ和Kafka等)。
PowerDotNet和PowerDotNetCore平台化软件的设计与实现偏重于数据处理,对主流的技术选型都做了大量深度开发,简化运维部署的复杂度,提高数据系统的高可用性。
同时业务功能模型选择也有很多讲究,比如支付和财务平台系统的压力非常大,所以系统互联互通的时候,优先推荐拉模式,而不是支付和财务平台主动推送模式,虽然支付财务主动推送是标配。
对于经典的发布-订阅模式,建议采用消息总线进行统一管理,PowerDotNet和PowerDotNetCore不建议接入应用方直接使用各种中间件的发布订阅功能,否则高并发下容易产生性能问题。
PowerDotNet和PowerDotNetCore已经开发出了典型的数据处理为主的公共服务平台,如支付、财务、结算、CRM等系统,后续文章会讲讲支付平台、财务平台等系统的架构设计与开发。