微服务如今是一个很火的话题,好像无论项目的大小,适用范围都在往微服务上去靠。这也使得如今若是不会微服务出去都无法和别人聊了。node
仅从我本身的工做经从来看,尽管咱们的项目也是微服务化了的。可是说实话在业务开发过程当中并无体会到微服务的开发和单体项目开发中存在很大的区别(仅业务开发这一块来讲)。python
微服务的学习单独的写几个demo并无啥用,如今的框架封装程度都很高,就算是代码跑起来来依旧是云里雾里。微服务的学习更多的是搞清楚微服务中每个组件究竟是什么?为何有这些组件?没有会怎么样?功能大概是怎么实现的?git
搞清楚来这些基本上就能够说对微服务有个大致的掌握了。算法
在微服务中首先须要面对的问题就是不一样的服务之间如何进行通讯呢?在单体服务中若是不一样的服务之间须要通讯,通常就是服务将接口暴露,而后其余的服务经过http进行请求,那么很明显在微服务中也能够这样。spring
但这里存在的问题在于单体服务中咱们须要请求的目标是咱们在请求的url中直接写死的,由于服务很少能够这样。而微服务中须要考虑的是存在大量服务时手动维护服务列表是否合适?若是服务横向扩展时如何通知其余的服务?服务宕机后,如何及时下线等等问题。缓存
注册中心的存在是为了更好更方便的管理应用中的每个服务,是各个分布式节点之间的纽带。注册中心主要提供如下核心功能:markdown
注册中心的主要功能就是保存服务的具体信息,而后由服务消费者读取这些信息。从总体的流程上来讲大概就是这样: 网络
除了将服务注册到注册中心,实际还存在服务的反注册架构
目前对注册中心的实现分为两种模式,分别为客户端(应用内注册)和服务端(应用外注册)。框架
目前客户端实现的注册中心比较有表明性的就是eureka,虽然eureka2.0版本在此前宣布闭源,但目前好像没有太大的影响。后续的话能够考虑阿里开源的nacos。
eureka是一个基于Java语言实现的费用与服务发现与注册的组件,包含服务端和客户端两部分。
服务端主要用来存储服务信息,定时进行服务检查。而客户端用于完成向服务端注册服务信息以及从服务端拉取服务信息等。
上图是eureka官给出的eureka的架构图。
从图中能够很明显的看到eureka的服务端也是做为一个服务部署,而客户端则是经过sdk的方式接入应用。客户端向服务端进行服务注册以及更新服务等,同时客户端从服务端获取服务信息,不一样服务之间根据得到的服务信息进行远程调用。
为了知足服务的高可用,经过移动多个eureka server服务,不一样eureka server相互注册实现服务的高可用。
eureka是由注册中心提供服务端和客户端的SDK,业务端经过引入注册中心提供的SDK,来实现服务的注册和发现。而服务端的注册中心则是经过应用外的组件来实现服务注册功能的。目前用的比较多的服务端注册中心组件如zookeeper、consul等。这里以zookeeper为例。
Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变动推送,适合做为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,是目前Dubbo官方主推搭配的注册中心。
上图是dubbo官网提供的zookeeper做为注册中心的基本原理。
以dubbo为根目录,建立一服务接口全限定名的目录,在其下建立存放服务提供者接口信息的providers目录、存放服务消费者接口信息的consumers目录、存放服务配置信息的configurators目录等。
服务提供者启动的时候在providers下建立一条服务信息,该信息能够被consumer获取。经过zk提供的watcher机制注册一个watcher在服务提供者的providers目录下,这样当服务出现扩容或者宕机的时候就能够当即被consumer消费。
在微服务中,注册中心做为一个存储全部服务中心的地方必然不多是单机的。由于若是注册中心没法使用,那么服务提供者就没法对外暴露本身的服务,消费者也没法获取本身须要调用的服务的具体地址,整个应用将会崩溃。首先须要保证的就是注册中心的高可用。
说到高可用,CAP理论是绕不过去的,CAP简单来讲就是咱们须要在服务的可用性和服务间的一致性进行一个抉择。是牺牲可用性来保证强一致性仍是保证可用性牺牲强一致性呢?
Zookeeper是一个典型的CP类型的高可用组件。zookeeper实现来paxos算法,zookeeper集群有一个节点做为leader,若是leader节点挂了zk会经过ZAB协议来进行leader选举,可是在选举的过程当中zk是不能对外提供服务的。
并且当由于网络分区致使zk集群出现脑裂问题时,因为ZAB协议须要半数以上的节点参与,因此可能会有部分或者所有的zk节点没法对外提供服务。可是zk能够保证全部可用节点都数据都是一致,即便节点崩溃,在恢复后也会和其余节点保持一致,这就是zk牺牲了可用性而保证的强一致性。
而相似与eureka的注册中心则是AP类型的。eureka实现高可用是经过启动多个eureka server服务,每个eureka server便是提供者也是消费者,每一个eureka server将本身做为服务注册给其余的eureka server,这样每一个eureka server都是其余server 的replica。eureka这种模式中每一个节点都是平等的,不存在leader、flower。
当集群中某一个server节点宕机,请求能够直接转移到其余节点。即便发生了网络分区,尽管不一样分区之间没法进行通讯,可是在每个分区内的节点是通讯而且能够正常对外提供服务,这样就保证了在server节点宕机的状况下的注册中心的可用性。
而问题在于因为不一样分区没法通讯,就致使可能一部分节点的数据和另外一部分有差异,这就是牺牲了强一致性来换取系统可用性。
首先咱们应该明确的是其实注册中心的存在与否应该是不能直接影响服务的调用的。服务之间的调用时经过http或者rpc等协议直接调用的,注册中心只是提供一个调用地址。服务是否能够正常使用不能直接靠注册中心节点信息来决定。
即便注册中心出现问题,只要服务自己没有宕机,理论上来讲仍是应该正常对外提供服务。因此很明显对于咱们来讲AP类型的注册中心是要优于CP类型的注册中心的。
固然在实际实现过程当中不少框架都会尽量的去修补这些问题,好比eureka会定时的同步各个节点的数据,尽量的作到数据一致性。dubbo的zk注册中心实现过程当中也会本地缓存服务信息并非彻底经过zk信息来决定是否剔除服务等。因此实际在选择时仍是须要根据自身实际以及是否还有其余需求来肯定。
在微服务中,每个服务都是一个独立的总体。各个服务之间并不向单体应用中的不一样模块大都要求是同一语言实现。现现在各大公司开发语言是多样化的,不一样的业务线开发的语言可能都不尽相同,混合语言是咱们必需要面对的一个问题,因此出现了多语言之间服务调用的问题。
对于像eureka这样的应用内的注册中心。因为服务的注册与发现都是依赖于SDK,因此若是要使用eureak那须要对应的语言实现的SDK,目前有很多语言都有提供如python、node.js。目前springclould的Sidecar组件也能够作到将其余语言归入到springclould体系中来。
对于应用外的注册中心,通常会由这些中间件提供客户端操做实现。而后由语言自己来实现服务注册和治理等功能。目前不少语言实际上也有相关的开源工程。
前面已经说过了保证CP类型的强一致性注册中心以及AP类型保证可用性的注册中心。而对于一个注册中心来讲最重要的就是管理其中的节点信息。服务启动的时候须要将服务信息记录,当服务出现问题的时候须要将服务摘除。
可是问题在于如何实现这个摘除的操做,目前注册中心的实现都是经过心跳检测来判断服务的健康状态。正常状况下若是服务宕机了,心跳检测没法和服务通讯判断该节点没法正常服务,将服务从注册中心摘除,这个过程是没有问题的。可是若是发生网络问题,好比网络抖动或者网络分区,这时候心跳检测确定是没法正常通讯的,但若是就所以直接将服务摘除那确定是有问题的,由于节点其实是可以正常提供服务的。因此须要有机制来确保这个过程不会粗暴的将节点从注册中心摘除。
目前不少的框架实现都会在本地缓存微服务的节点信息。实际上使用这些节点的是各个服务,服务是否能正常使用,这些节点才是最有发言权的。
客户端缓存了节点信息,当服务端断定服务出现问题后发出更新请求时,客户端并不马上删除,能够先作一个标识。后续的业务请求过来后,仍旧断定该服务时可用的,只有当发出的请求没法收到正常回应时才将该服务摘除。由客户端来决定节点是否可用,不过这须要容错机制来支持。
在网络频繁抖动的状况下,注册中心的节点信息会快速变化,也会给客户端发送大量的信息,当服务较多的时候可能会有大量的带宽被占用致使正常的请求都没法处理。
因此须要一种动态的调整注册中心心跳检测频率的机制,当检测到网络抖动频繁时,根据网络状况调整心跳检测的频率,好比调整为正常状况下的1/2,10/1等。在网络正常时心跳检测也恢复正常。
注册中心是微服务架构中一个很关键的组件,其保证来各个服务之间正常调用,以及服务的横向扩容等功能。在进行注册中心选型时须要考虑业务场景。