01. Spring Cloud--服务发现

1.1 简介

什么是服务发现?
在任何分布式架构中,都需要找到机器所在的物理地址。这个概念自分布式计算开始出现就已经存在,并且被正式称为服务发现。

服务发现对于微服务和基于云的应用程序至关重要,主要原因有两个。

  • 它为应用团队提供了一种能力,可以快速地对在环境中运行的服务实例数量进行水平伸缩。通过服务发现,服务消费者能够将服务的物理位置抽象出来。由于服务消费者不知道实际服务实例的物理位置,因此可以从可用服务池中添加或移除服务实例。
  • 它有助于提高应用程序的弹性,当微服务实例变得不健康或者不可用的时候,大多数服务发现引擎将从内部可用服务列表中一处该实例。由于服务发现引擎会在路由服务时绕过不可用服务,因此能够使不可用服务造成的损害降至最小。

1.2 服务发现的特点

基于云的微服务环境的解决方案是使用服务发现机制,这一机制具有以下特点。

  • 高可用 – 服务发现需要能够支持“热”集群环境,在服务发现集群中可以跨多个节点共享服务查询。如果一个节点变得不可用,集群中的其他节点应该能够接管工作。
  • 点对点 – 服务发现集群中的每一个节点共享服务实例的状态。
  • 负载均衡 – 服务发现需要在所有服务实例之间动态地对请求进行负载均衡,以确保服务调用分布在由它管理的所有服务实例上。在许多方面,服务发现取代了许多早期Web应用程序实现中使用的更静态的、手动管理的负载均衡器。
  • 有弹性 – 服务发现的客户端应该在本地“缓存”服务信息。本地缓存允许服务发现功能逐步降级,这样,如果服务发现本身变得不可用的时候,应用程序仍然可以基于本地缓存中维护的信息来运行和定位服务。
  • 容错 – 服务发现需要检测出服务实例什么时候是不健康的,并从可以接收客户端请求的可用服务列表中移除该实例。服务发现应该在没有人为干预的情况下,对这些故障进行检测,并采取行动。

1.3 服务发现架构

架构图:
服务发现架构图

1.3.1 服务注册

当服务实例启动时,它们将通过一个或多个服务发现实例来注册他们可以访问的物理位置、路径和端口。虽然每个服务实例都具有唯一的IP地址和端口,但是每个服务实例都将以相同的服务ID进行注册。服务ID是唯一标识一组相同服务实例的键。

服务通常只在一个服务发现实例中进行注册。大多数服务发现的实现使用数据传播的点对点模型,每个服务实例的数据都被传递到服务发现集群中的所有其他节点。

最后,每个服务实例将通过服务发现服务去推送服务实例的状态,或者服务发现服务从服务实例拉取状态。任何未能返回良好的健康检查信息的服务都将从可用服务实例池中删除。

1.3.2 负载均衡

服务在向服务发现服务进行注册之后,这个服务就可以被需要使用这项服务功能的应用程序或者其他服务使用。客户端可以使用不同的方式来发现服务。

在每次调用服务时,客户端可以只依赖于服务发现引擎来解析服务位置。这种方式被称为服务端负载均衡。使用这种模式,每次调用注册的微服务实例时,服务发现引擎就会被调用。但是,这种方法很脆弱,因为客户端完全依赖于服务发现引擎来查找和调用服务。

还有一种更加健壮的方法就是使用客户端负载均衡

在这个模式下,当服务消费者需要调用一个服务时:

  1. 它将联系服务发现服务,获取它请求的所有服务实例,然后在服务消费者的机器上本地缓存数据。
  2. 每当客户端需要调用该服务时,服务消费者将从缓存中查找该服务的位置信息。通常,客户端缓存将使用简单的负载均衡算法,如“轮询”,以确保服务调用分布在多个服务实例之间。
  3. 然后,客户端将定期与服务发现服务进行联系,并刷新服务实例的缓存。如果在调用服务的过程中,服务调用失败,那么本地的服务发现缓存失效,服务发现客户端将尝试再次访问服务发现服务并刷新本地数据。

1.4 构建Spring Eureka服务端

代码清单:添加依赖到pom.xml文件中

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.spring</groupId>
  <artifactId>eureka-server</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>eureka-server</name>
  <description>Demo project for Spring Boot</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.6.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.SR2</spring-cloud.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
</project>

代码清单:在application.properties文件中创建Eureka配置

spring.application.name=eureka-server
server.port=9000 #Eureka服务器将要监听的端口
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false #不要将自己注册到Eureka服务列表中
eureka.client.fetch-registry=false #不要在本地缓存注册表信息
eureka.server.enable-self-preservation=false #关闭自我保护机制
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

自我保护机制:
详见以下网址 – Spring Cloud Eureka 自我保护机制

代码清单:标注引导类以启用Eureka服务器

@SpringBootApplication
@EnableEurekaServer //在Spring服务中启用Eureka服务器
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

至此,我们就完成了Spring Eureka服务端的构建。我们可以通过mvn spring-boot:run命令来启动服务。

1.5 构建Spring Eureka客户端

代码清单:添加依赖到pom.xml文件中

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

代码清单:在application.properties文件中创建Eureka配置

server.port=端口号
spring.application.name=服务名 #将使用Eureka注册的服务的逻辑名称
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
eureka.instance.prefer-ip-address=true #注册服务的IP,而不是服务器名称

每一个通过Eureka注册的服务都会有连个与之相关的组件:应用程序ID实例ID。应用程序ID用于表示一组服务实例。在基于Spring Boot的微服务中,应用程序ID使用是由spring.application.name属性设置的值。而实例ID是一个随机数,用于代表单个服务实例。

eureka.instance.prefer-ip-address属性告诉Eureka,要将服务的IP地址而不是服务的主机名注册到Eureka。
为什么偏向于IP地址?
在默认情况,Eureka在尝试注册服务的时候,将会使用主机名让外界与它们进行联系。这种方式在基于服务器的环境中运行良好,在这样的环境中,服务会被分配到一个DNS支持的主机名。但是,在基于容器的部署(如Docker)中,容器将以随机生成的主机名启动,并且该容器没有DNS记录。

eureka.client.service-url.defaultZone属性包含客户端用于解析服务位置的Eureka服务的列表,该列表以逗号进行分割。

代码清单:标注引导类以启用Eureka客户端

@SpringBootApplication
@EnableEurekaClient #激活Eureka客户端
@EnableFeignClients #启用Fegin客户端
public class UserEurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserEurekaApplication.class, args);
    }
}

代码清单:Eureka客户端调用其他服务

@FeginClient("product-eureka")
public interface DataClient {
    @GetMapping("/product/{userId}")
    List<Product> getProductByUserId(@PathVariable("userId") String userId);
}

以上代码表示:调用product-eureka服务提供的/product/{userId}接口。

猜你喜欢

转载自blog.csdn.net/hushukang/article/details/83714924
今日推荐