Spring Cloud 地址硬编码问题解决(第三天)

一、服务发现原理深入

服务发现组件后的架构图,如图所示。

图-服务发现-02

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

  • 各个微服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现组件会存储这些信息;

  • 服务消费者可从服务发现组件查询服务提供者的网络地址,并使用该地址调用服务提供者的接口;

  • 各个微服务与服务发现组件使用一定机制(例如心跳)通信。服务发现组件如长时间无法与某微服务实例通信,就会自动注销(即:删除)该实例;

  • 当微服务网络地址发生变更(例如实例增减或者IP端口发生变化等)时,会重新注册到服务发现组件;

  • 客户端缓存:各个微服务将需要调用服务的地址缓存在本地,并使用一定机制更新(例如定时任务更新、事件推送更新等)。这样既能降低服务发现组件的压力,同时,即使服务发现组件出问题,也不会影响到服务之间的调用。

1.2 服务发现组件应具备以下功能:

  • 服务注册表:服务注册表是服务发现组件的核心(其实就是类似于上面的registry表),它用来记录各个微服务的信息,例如微服务的名称、IP、端口等。服务注册表提供查询API和管理API,查询API用于查询可用的微服务实例,管理API用于服务的注册和注销;

  • 服务注册与服务发现:服务注册是指微服务在启动时,将自己的信息注册到服务发现组件上的过程。服务发现是指查询可用微服务列表及其网络地址的机制;

  • 服务检查:服务发现组件使用一定机制定时检测已注册的服务,如发现某实例长时间无法访问,就会从服务注册表中移除该实例。

参考文章:http://www.itmuch.com/spring-cloud/finchley-4/

二、Eureka入门

2.1 Eureka简介

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

2.2 编写Eureka Server

2.2.1 创建microservice-discovery-eureka模块

2.2.2 配置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">
    <!--<parent>
        <artifactId>cloud-study</artifactId>
        <groupId>com.qhr.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>-->
    <groupId>com.qhr.cloud</groupId>
    <modelVersion>4.0.0</modelVersion>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <artifactId>microservice-discovery-eureka</artifactId>

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

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

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

    <!-- 引入spring cloud的依赖 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</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>

2.2.3 配置application.yml

server:
  port: 8761
eureka:
  client:
    # 是否要注册到其他Eureka Server实例
    register-with-eureka: false
    # 是否要从其他Eureka Server实例获取数据
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:8761/eureka/

2.2.4 配置启动类EurekaApplication

package com.qhr.cloud.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 15:32 2020/7/1
 * @Modified By :
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

2.2.5 复制microservice-simple-provider-user和microservice-simple-consumer-movie子模块,并修改模块名称为microservice-provider-user和microservice-consumer-movie

2.2.6 分别在两个微服务,添加依赖

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

2.2.7 修改启动类

package com.qhr.cloud.study;

import com.qhr.cloud.study.entity.User;
import com.qhr.cloud.study.repository.UserRepository;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.math.BigDecimal;
import java.util.stream.Stream;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 15:49 2020/7/1
 * @Modified By :
 */
@SpringBootApplication
public class ProviderUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderUserApplication.class, args);
    }

    /**
     * 初始化用户信息
     * 注:Spring Boot2不能像1.x一样,用spring.datasource.schema/data指定初始化SQL脚本,否则与actuator不能共存
     * 原因详见:
     * https://github.com/spring-projects/spring-boot/issues/13042
     * https://github.com/spring-projects/spring-boot/issues/13539
     *
     * @param repository repo
     * @return runner
     */
    @Bean
    ApplicationRunner init(UserRepository repository) {
        return args -> {
            User user1 = new User(1L, "account1", "张三", 20, new BigDecimal(100.00));
            User user2 = new User(2L, "account2", "李四", 28, new BigDecimal(180.00));
            User user3 = new User(3L, "account3", "王五", 32, new BigDecimal(280.00));
            Stream.of(user1, user2, user3)
                    .forEach(repository::save);
        };
    }
}
package com.qhr.cloud.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 16:01 2020/7/1
 * @Modified By :
 */
@SpringBootApplication
public class ConsumerMovieApplication {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(ConsumerMovieApplication.class, args);
    }
}

注:早期的版本(Dalston及更早版本)还需在启动类上添加注解@EnableDiscoveryClient 或@EnableEurekaClient ,从Edgware开始,该注解可省略。

2.2.8 修改microservice-provider-user的application.yml配置文件

server:
  port: 8000
spring:
  application:
    # 指定注册到eureka server上的服务名称
    name: microservice-provider-user
  jpa:
    # 让hibernate打印执行的SQL
    show-sql: true
logging:
  level:
    root: INFO
    # 配置日志级别,让hibernate打印出执行的SQL参数
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
management:
  endpoints:
    web:
      exposure:
        # 开放所有监控端点
        include: '*'
  endpoint:
    health:
      # 是否展示健康检查详情
      show-details: always

eureka:
  client:
    service-url:
      # 指定eureka server通信地址,注意/eureka/小尾巴不能少
      defaultZone: http://localhost:8761/eureka/
  instance:
    # 是否注册IP到eureka server,如不指定或设为false,那就会注册主机名到eureka server
    prefer-ip-address: true


2.2.9 修改microservice-consumer-movie的application.yml配置文件

server:
  port: 8010
spring:
  application:
    # 指定注册到eureka server上的服务名称
    name: microservice-consumer-movie

eureka:
  client:
    service-url:
      # 指定eureka server通信地址,注意/eureka/小尾巴不能少
      defaultZone: http://localhost:8761/eureka/
  instance:
    # 是否注册IP到eureka server,如不指定或设为false,那就会注册主机名到eureka server
    prefer-ip-address: true

2.2.10 测试

      按照microservice-discovery-eureka、microservice-provider-user和microservice-consumer-movie的启动顺序,先后启动微服务。访问:http://localhost:8761/

如上:现在服务就已经注册成功。

参考文章:http://www.itmuch.com/spring-cloud/finchley-5/

三、Eureka深入

3.1 Eureka原理

图-Region And Availibility Zone

Netflix公司将他们的应用都部署在了AWS上,所以Eureka的架构使用到了AWS中的一些概念:

Region和Availability Zone均是AWS的概念。

  • Region表示AWS中的地理位置,例如us-east-1、us-east-2、eu-west-1等;
  • 每个Region都有多个Availability Zone,彼此内网打通
  • 各个Region之间完全隔离,彼此内网不打通
  • AWS通过这种方式实现了最大的容错和稳定性。

        Spring Cloud中,默认使用的Region是us-east-1 。非AWS环境下,可将将Region理解为内网没有打通的机房,将Availability Zone理解成相同机房的不同机架(内网打通)。

3.2 Eureka架构图

Eureka架构

 

3.2.1 节点信息

  • Application Service:服务提供者;

  • Application Client:服务消费者;

  • Make Remote Call调用RESTful API;

  • us-east-1c、us-east-1d等都是Availability Zone,它们都属于us-east-1这个region。

3.2.2 工作原理

  • 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 Server默认保证在90秒内,Eureka Server集群内的所有实例中的数据达到一致(从这个架构来看,Eureka Server所有实例所处的角色都是对等的,没有类似Zookeeper、Consul、Etcd等软件的选举过程,也不存在主从,所有的节点都是主节点。Eureka官方将Eureka Server集群中的所有实例称为“对等体(peer)”)

  • Eureka Client会缓存服务注册表中的信息。这种方式有一定的优势——首先,微服务无需每次请求都查询Eureka Server,从而降低了Eureka Server的压力;其次,即使Eureka Server所有节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者并完成调用。

3.3 案例代码

3.3.1 配置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">
    <!--<parent>
        <artifactId>cloud-study</artifactId>
        <groupId>com.qhr.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>-->
    <groupId>com.qhr.cloud</groupId>
    <version>1.0-SNAPSHOT</version>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>

    <artifactId>microservice-provider-user-ha</artifactId>

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

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

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

    <!-- 引入spring cloud的依赖 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</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>

3.3.2 配置application.yml文件

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

---
spring:
  profiles: peer2
server:
  port: 8762
eureka:
  instance:
    hostname: peer2
  client:
    serviceUrl:
      defaultZone: http://peer1:8761/eureka/

注:由于我们是通过profiles方式,在同一台电脑上配Eureka的高可用性,所以我们必须在本地主机名,配置方式如下:

vim /etc/hosts
# 添加如下内容
127.0.0.1 peer1 peer2

对于Windows系统,请修改C:\windows\system32\drivers\etc\hosts文件

3.3.3 配置启动文件EurekaApplication

package com.qhr.cloud.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * @Author : qhr
 * @Description :
 * @Date : Created in 10:21 2020/7/4
 * @Modified By :
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

3.3.4 启动

先将项目打成jar包,然后打开两个cmd在jar包目录下,先后运行一下两个命令:

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

注:第一个cmd一定会报错:com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect,但是不要紧,这是由于实例peer2还没运行所报的错,当peer2运行了就不会报错了。

访问:http://peer1:8761/

访问:http://peer2:8762/

3.3.5 将服务注册到Eureka集群

修改microservice-provider-user的application.yml配置文件(连接多节点配置):

eureka:
  client:
    service-url:
      # 指定eureka server通信地址,注意/eureka/小尾巴不能少
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/

或者是(连接单节点配置):

eureka:
  client:
    service-url:
      # 指定eureka server通信地址,注意/eureka/小尾巴不能少
      defaultZone: http://peer1:8761/eureka/

两种配置均可,因为eureka集群节点数据是共享的,但是为了系统的稳定性,一般采用多节点配置,这样可以避免一些极端的问题。

访问:http://peer1:8761/

可以发现microservice-provider-user微服务已经注册成功了。

参考文章:http://www.itmuch.com/spring-cloud/finchley-6/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章