服务治理可以说是微服务架构中最为核心和基础的模块, 它主要用来实现各个微服务 实例的自动化注册与发现。 为什么我们在微服务架构中那么需要服务治理模块呢?微服务 系统没有它会有什么不好的地方吗?

​ 在最初开始构建微服务系统的时候可能服务并不多, 我们可以通过做一些静态配置来 完成服务的调用。 比如,有两个服务 A 和 B, 其中服务 A 需要调用服务 B 来完成一个业务 操作时, 为了实现服务 B 的高可用, 不论采用服务端负载均衡还是客户端负载均衡, 都需 要手工维护服务 B 的具体实例清单。

​ 但是随着业务的发展, 系统功能越来越复杂, 相应的 微服务应用也不断增加, 我们的静态配置就会变得越来越难以维护。 并且面对不断发展的业务, 我们的集群规模、 服务的位置 、 服务的命名等都有可能发生变化, 如果还是通过手 工维护的方式, 那么极易发生错误或是命名冲突等问题。 同时, 对于这类静态内容的维护 也必将消耗大量的人力。

​ 为了解决微服务架构中的服务实例维护问题, 产生了大量的服务治理框架和产品。 这 些框架和产品的实现都围绕着服务注册与服务发现机制来完成对微服务应用实例的自动化管理。

服务注册

​ 在服务治理框架中, 通常都会构建一个注册中心, 每个服务单元向注册 中心登记自己提供的服务, 将主机与端口号、 版本号、 通信协议等一些附加信息告 知注册中心, 注册中心按服务名分类组织服务清单。

​ 比如, 我们有两个提供服务A 的进程分别运行于 192.168.0.100:8000和192.168.0.101:8000位置上, 另外还有三个 提供服务B的进程分别运行千192.168.0.100:9000 、 192.168.0.101:9000、 192.168.0.102:9000位置上。 当这些进程均启动, 并向注册中心注册自己的服务之后, 注册中心就会维护类似下面的一个服务清单。 另外, 服务注册中心还需要以心跳的方式去监测清单中的服务是否可用, 若不可用 需要从服务清单中剔除, 达到排除故障服务的效果。

服务名 位置
服务A 192.168.0.100:8000、192.168.0.101:8000
服务B 192.168.0.100:9000、192.168.0.101:9000、192.168.0.102:9000

服务发现

​ 由于在服务治理框架下运作, 服务间的调用不再通过指定具体的实例地 址来实现, 而是通过向服务名发起请求调用实现。 所以, 服务调用方在调用服务提 供方接口的时候, 并不知道具体的服务实例位置。 因此, 调用方需要向服务注册中 心咨询服务, 并获取所有服务的实例清单, 以实现对具体服务实例的访问。

​ 比如, 现有服务C希望调用服务A, 服务C就需要向注册中心发起咨询服务请求, 服务注 册中心就会将服务A的位置清单返回给服务C, 如按上例服务A的情况,C便获得 了服务A的两个可用位置 192.168.0.100:8000和192.168.0.101:8000。 当服务C要发起调用的时候, 便从该清单中以某种轮询策略取出一 个位置来进行服 务调用, 这就是后续我们将会介绍的客户端负载均衡。 这里我们只是列举了一种简 单的服务治理逻辑, 以方便理解服务治理框架的基本运行思路。 实际的框架为了性 能等因素, 不会采用每次都向服务注册中心获取服务的方式, 并且不同的应用场景 在缓存和服务剔除等机制上也会有一些不同的实现策略。

Spring Cloud Eureka使用Netflix Eureka来实现服务注册与发现, 它既包含了服务端组件,也包含了客户端组件,并且服务端与客户端均采用Java编写,所以Eureka主要适用 于通过Java实现的分布式系统,或是与NM兼容语言构建的系统。但是, 由于Eureka服 务端的服务治理机制提供了完备的RESTfulAPL所以它也支持将非Java语言构建的微服 务应用纳入Eureka的服务治理体系中来。只是在使用其他语言平台的时候,需要自己来实 现Eureka的客户端程序。不过庆幸的是,在目前几个较为流行的开发平台上,都已经有了 一些针对Eureka 注册中心的客户端实现框架, 比如.NET平台的 Steeltoe、 Node.js 的 eureka-js-client等。

Eureka服务端 ,我们也称为服务注册中心。 它同其他服务注册中心一样,支持高可用 配置。它依托于强一致性提供良好的服务实例可用性,可以应对多种不同的故障场景。 如 果Eureka以集群模式部署,当集群中有分片出现故障时,那么Eureka就转入自我保护模 式。它允许在分片故障期间继续提供服务的发现和注册,当故障分片恢复运行时, 集群中 的其他分片会把它们的状态再次同步回来。以在AWS 上的实践为例, Netflix推荐每个可 用的区域运行一个Eureka服务端,通过它来形成集群。不同可用区域的服务注册中心通过 异步模式互相复制各自的状态,这意味着在任意给定的时间点每个实例关于所有服务的状 态是有细微差别的。

Eureka客户端 ,主要处理服务的注册与发现。客户端服务通过注解和参数配置的方式, 嵌入在客户端应用程序的代码中,在应用程序运行时,Eureka客户端向注册中心注册自身 提供的服务并周期性地发送心跳来更新它的服务租约。同时,它也能从服务端查询当前注 册的服务信息并把它们缓存到本地并周期性地刷新服务状态。

下面我们来构建一些简单示例,学习如何使用Eureka构建注册中心以及进行注册与发 现服务。

搭建服务注册中心

首先,创建一个基础的Spring Boot工程,命名为eureka-server, 并在pom.xml 中引入必要的依赖内容, 代码如下:

<parent>        
    <groupId>org.springframework.boot</groupId>        
    <artifactId>spring-boot-starter-parent</artifactId>        
    <version>1.5.10.RELEASE</version>        
    <relativePath/> <!-- lookup parent from repository -->    
</parent>
 <dependency>          
     <groupId>org.springframework.cloud</groupId>           
     <artifactId>spring-cloud-starter-eureka-server</artifactId>           
     <version>1.4.4.RELEASE</version>        
 </dependency>
 <dependencyManagement>    
     <dependencies>            
         <dependency>                
             <groupId>org.springframework.cloud</groupId>                
             <artifactId>spring-cloud-dependencies</artifactId>                
             <version>Brixton.SR7</version>                
             <type>pom</type>                
             <scope>import</scope>            
         </dependency>        
     </dependencies>    
 </dependencyManagement>

通过@EnableEurekaServer 注解启动一个服务注册中心提供给其他应用进行对话。 这一步非常简单, 只需在一个普通的 Spring Boot 应用中添加这个注解就能开启此功能, 比 如下面的例子:

5b3075210001800107820526.jpg

在默认设置下, 该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们 需要禁用它的客户端注册行为, 只需在 application.properties 中增加如下配置:

5b308a540001042808990530.jpg

eureka.client.register-with-eureka: 由于该应用为注册中心,所以设置 为 false, 代表不向注册中心注册自己。

eureka.client.fetch-registry: 由于注册中心的职责就是维护服务实例, 它并不需要去检索服务, 所以也设置为 false。

在完成了上面的配置后,启动应用并访问 http://localhost: 8081/。可以看到Eureka 信息面板, 其中 Instances currently registered with Eureka 栏是空的, 说明该注册中心还没有注册任何服务。

5b30c3410001fd4f18750971.jpg

在完成了服务注册中心的搭建之后,接下来我们尝试将一个既有的 Spring Boot 应用加 入 Emeka 的服务治理体系中去。

  • 新建一个新的Spring Boot 项目

5b30cc9b0001e63204150329.jpg

  • 首先, 修改 pom.xml, 增加 Spring Cloud Eureka 模块的依赖, 具体代 码如下所示:

5b30ccdc0001e2eb07730761.jpg

  • 接着, 创建HelloContorller , /hello 请求处理接口, 通过注入 DiscoveryClient对象, 在日志中打 印出服务的相关内容。

5b30cd8900017d1108500500.jpg

  • 然后, 在主类中通过加上 @EnableDiscoveryClient 注解, 激活 Eureka 中的 DiscoveryClient 实现(自动化配置, 创建 DiscoveryClient 接口针对 Eureka 客户 端的 EurekaDiscoveryClient 实例), 才能实现上述 Controller 中对服务信息的输出。

5b30cde30001cb3f07390496.jpg

  • 最后 ,我们需要在 application. yml 配置文件中, 通 过 spring. application.name属性来为 服 务命名, 比如命名 为 hello-service。 再通过 eureka.client. serviceUrl.defaultZone属性来指定服务注册中心的地址, 这里 我们指定为之前构建的服务注册中心地址, 完整配置如下所示:

5b30ce2300019e5005930339.jpg

访问地址:http://localhost:8081/

5b30cf2c00016f6b10950082.jpg

https://img2.mukewang.com/5b30ce8300013a6618290903.jpg