SpringCloud与Docker微服务架构实战读书笔记(三) - Go语言中文社区

SpringCloud与Docker微服务架构实战读书笔记(三)


微服务注册与发现

服务发现简介

通过前面的讲解,我们知道硬编码提供者地址的方式有不少问题。要想解决这些问题,服务消费者需要一个强大的服务发现机制,服务消费者使用这种机制获取服务提供者的网络信息。不仅如此,即使服务提供者的信息发生变化,服务消费者也无需修改配置文件。

服务发现组件提供这种能力。在微服务架构中,服务发现组件是一个非常关键的组件。

使用服务发现组件后的架构图,如下:

ZzG81S.png

服务提供者、服务消费者、服务发现组件这三者之间的关系大致如下;

  • 各个微服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现组件会存储这些信息。
  • 服务消费者可从服务发现组件查询服务提供者的网络地址,并使用该地址调用服务提供者的接口。
  • 各个微服务与服务发现组件使用一定机制(例如心跳)通信。服务发现组件如长时间无法与某微服务实例通信,就会注销该实例。
  • 微服务网络地址发生变更(例如实例增减或者IP端口发生变化等)时,会重新注册到服务发现组件。是用这种方式,服务消费者就无须人工修改提供者的网络地址了。

综上,服务发现组件应具备以下功能:

  • 服务注册表:是服务发现组件的核心,它用来记录各个微服务的信息,例如微服务的名称、IP、端口等。服务注册表提供查询API和管理API,查询API用于查询可用的微服务实例,管理API用于服务的注册和注销。
  • 服务注册与服务发现:服务注册是指微服务在启动时,将自己的信息注册到服务发现组件上的过程。服务发现是指查询可用微服务列表及其网络地址的机制。
  • 服务检查:服务发现组件使用一定机制定时检测已注册的服务,如发现某实例长时间无法访问,就会从服务注册表中移除该实例。

综上,使用服务发现的好处是显而易见的。SpringCloud提供了多种服务发现组件的支持,例如Eureka、Consul和ZooKeeper等。

Eureka简介

Eureka是Netflix开源的服务发现组件,本身是一个基于REST的服务。它包含Server和Client两部分。SpringCloud将它集成在子项目Spring Cloud Netflix中,从而实现微服务的注册与发现。

Eureka原理

在分析Eureka的原理之前,先来了解一下Region和Availability Zone,如下图所示:

ZztocT.png

Region和Availability Zone均是AWS的概念。其中,Region表示AWS中的地理位置,每个Region都有多个Availability Zone,各个Region之间完全隔离。AWS通过这种方式实现了最大的容错和稳定性。

SpringCloud默认使用的Region是us-east-1,在非AWS环境下,可以将Availability Zone理解成机房,将Region理解为跨机房的Eureka集群。

下面分析一下Eureka的原理,Eureka架构如下图所示。

eS5y6K.png

  • Application Service相当于服务提供者
  • Application Client相当于服务消费者
  • Make Remote Call,可以理解成调用RESTful API的行为
  • us-east-1c、us-east-1d等都是zone,它们都属于us-east-1这个region

由上图可知,Eureka包含两个组件:Eureka Server和Eureka Client,它们的作用如下:

  • Eureka Server提供服务发现的能力,各个微服务启动时,会向Eureka Server注册自己的信息(例如IP、端口、微服务名称等),Eureka Server会存储这些信息。
  • Eureka Client是一个Java客户端,用于简化与Eureka Server的交互。
  • 微服务启动后,会周期性(默认30秒)地向Eureka Server发送心跳以续约自己的“租期”。
  • 如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。
  • 默认情况下,Eureka Server同时也是Eureka Client。多个Eureka Server实例,互相之间通过复制的方式,来实现服务注册表中数据的同步。
  • Eureka Client会缓存服务注册表中的信息。这种方式有一定的优势——首先,微服务无须每次请求都查询Eureka Server,从而降低了Eureka Server的压力;其次,即使Eureka Server所有节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者并完成调用。

综上,Eureka通过心跳检查、客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用性。

编写Eureka Server

本节来编写一个Eureka Server。

  1. 创建一个ArtifactId是microservice-discovery-eureka的Maven工程,并为项目添加以下依赖。
<dependency>  
    <groupId>org.springframework.cloud</groupId>   
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
  1. 编写启动类,在启动类上添加@EnableEurekaServer注解,声明这是一个Eureka Server。
package com.itmuch.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {    
    public static void main(String[] args) {   
        SpringApplication.run(EurekaApplication.class, args); 
    }
}
  1. 在配置文件application.yml中添加如下内容。
server:  
	port: 8761
eureka:  
	client:    
		registerWithEureka: false    
		fetchRegistry: false    
		serviceUrl:      
			defaultZone: http://localhost:8761/eureka/

简要讲解一下application.yml中的赔指数型:

  • eureka.client.registerWithEureka:表示是否将自己注册到Eureka Server,默认为true。由于当前应用就是Eureka Server,故而设为false。
  • eureka.client.fetchRegistry:表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。
  • eureka.client.serviceUrl.defaultZone:设置与Eureka Server交互的地址,查询服务与注册服务都需要依赖这个地址。默认是http://localhost:8761/eureka/;多个地址可使用‘,’分隔。

这样一个Eureka Server就编写完成了。

测试

启动Eureka Server,访问http://localhost:8761/,可看到如下图所示的界面。

eSIWuT.png

由图可知,Eureka Server的首页展示了很多信息,例如当前实例的系统状态、注册到Eureka Server上的服务实例、常用信息、实例信息等。显然,当前还没有任何微服务实例被注册到Eureka Server上。

将微服务注册到Eureka Server上

本节将之前编写的用户微服务注册到Eureka Server上。

  1. 复制项目microservice-simple-provider-user,将ArtifactId修改为microservice-provider-user。
  2. 在pom.xml中添加以下依赖。
<dependency>  
    <groupId>org.springframework.cloud</groupId>   
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
  1. 在配置文件application.ynl中添加以下配置。
spring:  
	application:
		name: microservice-provider-user
eureka:  
	client: 
		serviceUrl:      
			defaultZone: http://localhost:8761/eureka/
	instance:
		prefer-ip-address: true

其中,spring.application.name用于指定注册到Eureka Server上的应用名称;eureka.instance.prefer-ip-address = true表示将自己的IP注册到Eureka Server。如不配置该属性或将其设置为false,则表示注册微服务所在操作系统的hostname到Eureka Server。

  1. 编写启动类,在启动类上添加@EnableDiscoveryClient注解,声明这是一个Eureka Client。
package com.itmuch.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderUserApplication {  
    public static void main(String[] args) { 
        SpringApplication.run(ProviderUserApplication.class, args);  
    }
}

也可以使用@EnableEurekaClient注解替代@EnableDiscoveryClient。在SpringCloud中,服务发现组件有多种选择,例如ZooKeeper、Consul等。@EnableDiscoveryClient为各种服务组件提供了支持,该注解是spring-cloud-commons项目的注解,是一个高度的抽象;而@EnableEurekaClient表明是Eureka的Client,该注解是spring-cloud-netfix项目中的注解,只能与Eureka一起工作。当Eureka在项目的classpath中时,两个注解没有区别。

这样就可以将用户微服务注册到Eureka Server上了。同理,将电影微服务也注册到Eureka Server上,配置电影微服务的spring.application.name为microservice-consumer-movie。

相关文件如下:

MovieController.java

package com.itmuch.cloud.study.user.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.itmuch.cloud.study.user.entity.User;
@RestControllerpublic class MovieController { 
    @Autowired  
    private RestTemplate restTemplate;  
    @GetMapping("/user/{id}")  
    public User findById(@PathVariable Long id) { 
        return this.restTemplate.getForObject("http://localhost:8000/" + id, User.class); 
    }
}

User.java

package com.itmuch.cloud.study.user.entity;
import java.math.BigDecimal;
public class User { 
    private Long id; 
    private String username; 
    private String name; 
    private Integer age; 
    private BigDecimal balance; 
    
    public Long getId() {  
        return this.id;  
    } 
    public void setId(Long id) {
        this.id = id; 
    }  
    public String getUsername() {  
        return this.username; 
    }  
    public void setUsername(String username) {  
        this.username = username; 
    }  
    public String getName() {  
        return this.name; 
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public Integer getAge() { 
        return this.age;  
    } 
    public void setAge(Integer age) { 
        this.age = age; 
    }  
    public BigDecimal getBalance() {
        return this.balance; 
    }  
    public void setBalance(BigDecimal balance) {  
        this.balance = balance;  
    }
}

ConsumerMovieApplication.java

package com.itmuch.cloud.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerMovieApplication {
    @Bean  
    public RestTemplate restTemplate() { 
        return new RestTemplate(); 
    } 
    public static void main(String[] args) { 
        SpringApplication.run(ConsumerMovieApplication.class, args); 
    }
}

application.yml

server:  
	port: 8010
spring:  
	application:    
		name: microservice-consumer-movie
eureka:  
	client:    
		serviceUrl:      
			defaultZone: http://localhost:8761/eureka/  
	instance:    
		prefer-ip-address: true

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.itmuch.cloud</groupId>
  <artifactId>microservice-consumer-movie</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <!-- 引入spring boot的依赖 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.3.RELEASE</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

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

  <!-- 引入spring cloud的依赖 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Camden.SR4</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <!-- 添加spring-boot的maven插件 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

测试

  1. 启动microservice-discovery-eureka。
  2. 启动microservice-provider-user。
  3. 启动microservice-consumer-movie。
  4. 访问http://localhost:8761/,可看到如图所示界面。

eSHdRs.png

由图可知,此时用户微服务、电影微服务已经被注册到Eureka Server上了。

Eureka Server的高可用

有分布式应用开发经验的读者应该能够看出,前文编写的单节点的Eureka Server并不适合线上生产环境。Eureka Client会定时连接Eureka Server,获取服务注册表中的信息并缓存到本地。微服务在消费远程API时总是使用本地缓存中的数据。因此一般来说,即使Eureka Server发生宕机,也不会影响到服务之间的调用。但如果Eureka Server宕机时,某些微服务也出现了不可用的情况,Eureka Client中的缓存若不被更新,就可能会影响到微服务的调用,甚至影响到整个应用系统的高可用性。因此,在生产环境中,通常会部署一个高可用的Eureka Server集群。

Eureka Server可以通过运行多个实例并相互注册的方式实现高可用部署,Eureka Server实例会彼此增量地同步信息,从而确保所有节点数据一致。事实上,节点之间相互注册是Eureka Server的默认行为,还记得前文编写单节点Eureka Server时,额外配置了eureka.client.registerWithEureka=false、eureka.client.fetchRegistry=false吗?

本节在前文的基础上,构建一个双节点Eureka Server集群。

  1. 复制项目microservice-discovery-eureka,将ArtifactId修改为microservice-discovery-eureka-ha。

  2. 配置系统的hosts,Windows系统的hosts文件路径是C:WindowsSystem32driversetchosts;Linux及Mac OS等系统的文件路径是/etc/hosts。

    127.0.0.1 peer1 peer2

  3. 将application.yml修改如下:让两个节点的Eureka Server相互注册。

    spring: 
    	application:  
    		name: microservice-discovery-eureka-ha
    ---
    spring: 
    	# 指定profile=peer1
    	profiles: peer1
    server: 
    	port: 8761
    eureka: 
    	instance:  
    		# 指定当profile=peer1时,主机名是peer1 
    		hostname: peer1
    	client:  
    		serviceUrl: 
            	#将自己注册到peer2这个Eureka上面去
    			defaultZone: http://peer2:8762/eureka/
    ---
    spring: 
    	profiles: peer2
    server: 
    	port: 8762
    eureka: 
    	instance:  
    		hostname: peer2 
    	client:  
    		serviceUrl:   
    			defaultZone: http://peer1:8761/eureka/
    

    注意:配置application.yml时,各节点的缩进不要使用tab,而应使用空格,否则可能解析配置信息失败

    如上,使用连字符(—)将该application.yml文件分为三段。第二段和第三段分别为spring.profiles指定了一个值,该值表示它所在的那段内容应用在哪个Profile里。第一段由于并未指定spring.profiles,因此这段内容会对所有Profile生效。

    经过以上分析,不难理解,我们定义了peer1和peer2这两个Profile。当应用以peer1这个Profile启动时,配置该Eureka Server的主机名为peer1,并将其注册到http://peer2:8762/eureka/;反之,当应用以profile=peer2时,Eureka Server会注册到peer1节点的Eureka Server。

    测试

    1. 打包项目,并使用以下命令启动两个Eureka Server节点。

      java -jar microservice-discovery-eureka-ha-1.0-SNAPSHOT.jar --spring.profiles.active=peer1
      java -jar microservice-discovery-eureka-ha-1.0-SNAPSHOT.jar --spring.profiles.active=peer2
      

      通过spring.profiles.active指定使用哪个profile启动。

    2. 访问http://peer1:8761/,会发现“registered-replicas”中已有peer2节点;同理,访问http://peer2:8762/,也能发现其中的“registered-replicas”有peer1节点。

      epiKJI.png

    将应用注册到Eureka Server集群上

    以microservice-provider-user项目为例,只须修改eureka.client.serviceUrl.defaultZone,配置多个Eureka Server地址,就可以将其注册到Eureka Server集群了。示例:

    eureka:  
    	client:    
    		serviceUrl:      
    			defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
    

    这样就可以将服务注册到Eureka Server集群上了。

    当然,微服务即使只配置Eureka Server集群中的某个节点,也能正常注册到Eureka Server集群,因为多个Eureka Server之间的数据会相互同步。例如:

    eureka:  
    	client:    
    		serviceUrl:      
    			defaultZone: http://peer1:8761/eureka/
    

    正常情况下,这种方式与配置多个Server节点的效果是一样的。不过为适应某些极端场景,建议在客户端配置多个Eureka Server节点。

    启动microservice-provider-user项目,可以看到其已注册到Eureka Server集群中了。

    epMIXj.png

为Eureka Server添加用户认证

在前面的示例中,Eureka Server是允许匿名访问的,本节来构建一个需要登录才能访问的Eureka Server。

  1. 复制项目microservice-discovery-eureka,将ArtifactId修改为microservice-discovery-eureka-authenticating。

  2. 在pom.xml中添加spring-boot-starter-security的依赖,该依赖为Eureka Server提供用户认证的能力。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
  3. 在application.yml中添加以下内容:

    security:
      basic:
        #开启基于HTTP basic的认证
        enabled: true
      user:
        #配置登录账号
        name: user
        #配置登录密码
        password: password123
    

    这样就为Eureka Server添加了基于HTTP basic的认证。如果不设置这段内容,账号默认是user,密码是一个随机值,该值会在启动时打印出来。

测试

  1. 启动microservice-discovery-eureka-authenticating。

  2. 访问http://localhost:8761/,需要身份验证的对话框,如下图所示。

    eplsot.png

  3. 输入账号密码,就可登录并访问Eureka Server。

将微服务注册到需认证的Eureka Server

如何才能将微服务注册到需认证的Eureka Server上呢?答案非常简单,只须将eureka.client.serviceUrl.defaultZone配置为http://user:password@EUREKA_HOST:EUREKA_PORT/eureka/ 这种形式,就可以将HTTP basic认证添加到Eureka Client了。因此,只须稍作修改,就可以将应用注册到本例的Eureka Server了:

eureka:
  client:
    serviceUrl:
      #defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
      defaultZone: http://user:password123@localhost:8761/eureka/

对于更复杂的需求,可创建一个类型为DiscoveryClientOptionalArgs的@Bean,并向其中注入ClientFilter。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/yphust/article/details/96711357
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢