第三篇:SpringCloud 构建微服务系统之服务注册和发现(Eureka)

前面我们已经介绍过consul和nacos作为SpringCloud 构建微服务系统之服务注册和发现的组件 ,今天我们介绍的是Eureka的使用。

SpringCloud Eureka是SpringCloud Netflix服务套件中的一部分,它基于Netflix Eureka做了二次封装,主要负责完成微服务架构中的服务治理功能。

1、什么是 Eureka ?

Eureka 是 Netflix 的一个子模块,也是核心模块之一。Eureka 是一个基于 REST(REpresentational State Transfer) 的服务,用于定位服务,以实现云端中间层服务器的负载均衡和故障转移。Eureka还附带了一个基于java的客户端组件——Eureka Client,它使得与服务的交互更加容易。Eureka Client 还有一个内置的负载均衡器,可以进行基本的循环负载均衡,在 Netflix,一个更加复杂的负载均衡器封装了 Eureka,可以根据流量、资源的使用情况、错误条件等因素根据自定义的权重来实现负载均衡,从而提供更好的弹性服务。

对于服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。

2、Eureka 基本架构原理

Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,则使用 Eureka Client 连接到 Eureka Server 并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他子模块(例如 Gateway)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的业务逻辑。一个 Eureka 的高可用架构图如下(后面会专门的一篇文章来描述如何实现高可用):
在这里插入图片描述

上面图描述了 Eureka 是如何部署在 Netflix 上的,这是 Eureka 典型的使用案例。每个区域都有一个 Eureka 集群,而这个 Eureka 集群只知道该区域中的实例。每个区域至少有一个 Eureka 服务器来处理该区域故障(故障转移)。

Spring Cloud Netflix 在设计 Eureka 时就紧遵AP原则。Eureka Server 也可以运行多个实例来构建集群,解决单点问题,但不同于 ZooKeeper 的选举 leader 的过程,Eureka Server 采用的是Peer to Peer 对等通信。这是一种去中心化的架构(参看:微服务与微服务架构思想与原则),无 master/slave 之分,每一个 Peer 都是对等的。在这种架构风格中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。每个节点都可被视为其他节点的副本。

在集群环境中如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点上,当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会在节点间进行复制(replicate To Peer)操作,将请求复制到该 Eureka Server 当前所知的其它所有节点中。

当一个新的 Eureka Server 节点启动后,会首先尝试从邻近节点获取所有注册列表信息,并完成初始化。Eureka Server 通过 getEurekaServiceUrls() 方法获取所有的节点,并且会通过心跳契约的方式定期更新。默认情况下,如果 Eureka Server 在一定时间内没有接收到某个服务实例的心跳(默认周期为30秒),Eureka Server 将会注销该实例(默认为90秒,如果某个 eureka.instance.lease-expiration-duration-in-seconds 进行自定义配置)。当 Eureka Server 节点在短时间内丢失过多的心跳时,那么这个节点就会进入自我保护模式(后面有文章会谈及关于 Eureka Server 的自我保护机制)。

3、构建Eureka Server

新建一个springboot项目:eureka-server,其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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lidong</groupId>
    <artifactId>eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.RC2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <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-web</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>
        </repository>
    </repositories>

</project>

编写启动类

package com.lidong.provider;

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

/**
 * 开启服务发现
 */
@EnableEurekaServer
@SpringBootApplication
public class SpringCloudRegister {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudRegister.class, args);
    }
}

application.yml文件配置

server:
  port: 8761#提供者的端口
spring:
  application:
    name: eureka-server
eureka:
  instance:
    hostname: localhost
    lease-renewal-interval-in-seconds: 1
    lease-expiration-duration-in-seconds: 2
  client:
    register-with-eureka: false #不是用eureka进行服务注册
    fetch-registry: false #不在本地缓存注册信息
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    wait-time-in-ms-when-sync-empty: 5 #在服务器接收请求之前等待初始时间
    enable-self-preservation: false #关闭自我保存

启动服务

在浏览器输入 http://localhost:8761/,
可以看到启动成,
在这里插入图片描述

4、构建服务提供方

新建一个springboot项目:spring-cloud-eureka-producer,其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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lidong</groupId>
    <artifactId>spring-cloud-eureka-producer</artifactId>
    <version>1.0.0</version>
    <name>spring-cloud-eureka-producer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.RC2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>
        </repository>
    </repositories>

</project>

提供者添加配置(application.yml)

server:
  port: 9011 #提供者的端口
spring:
  application:
    name: spring-cloud-eureka-producer

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

新建服务
新建 EurekaProducerController,提供 sayHello 接口, 返回一个hello—>字符串。

package com.lidong.provider.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

/**
 * 创建服务
 */
@RestController
public class EurekaProducerController {

    @Value("${server.port}")
    private Integer port;

    /**
     * 服务接口
     * @param name
     * @return
     */
    @RequestMapping("/hello")
    public String sayHello(@RequestParam("name")String name) {
        return "hello ---> "+name+" port -->"+port;
    }
}

配置启动类

package com.lidong.provider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;

/**
 * 开启服务发现
 */


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

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

}


运行应用
我们会在Eureka的控制台上看到应用名,证明服务提供者已经成功注册在Eureka上了
在这里插入图片描述

5、构建服务消费方

新建一个springboot项目:spring-cloud-eureka-consumer,其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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lidong</groupId>
    <artifactId>spring-cloud-eureka-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud-eureka-consumer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.RC2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </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>
        </repository>
    </repositories>

</project>

消费者添加配置(application.yml)

server:
  port: 9012 #提供者的端口
spring:
  application:
    name: spring-cloud-eureka-consumer

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

创建ConsumerController

package com.lidong.consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * 创建服务的消费者
 */
@RestController
public class ConsumerController {

    private static final String SERVICE_NAME = "spring-cloud-eureka-producer";

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 获取所有服务
     */
    @RequestMapping("/services")
    public Object services() {
        return discoveryClient.getInstances(SERVICE_NAME);
    }

    /**
     * 调用服务
     */
    @RequestMapping("/callSayHello")
    public String services(@RequestParam("name") String name) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("name",name);
        String callServiceResult = restTemplate.getForObject("http://"+SERVICE_NAME+"/hello?name={name}", String.class,paramMap);
        System.out.println(callServiceResult);
        return callServiceResult;
    }
}

配置启动类

package com.lidong.consumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;


@SpringBootApplication
public class SpringCloudEurekaConsumerApplication {

    @Autowired
    private RestTemplateBuilder builder;

    @Bean
    @LoadBalanced  // 添加负载均衡支持,很简单,只需要在RestTemplate上添加@LoadBalanced注解,那么RestTemplate即具有负载均衡的功能,如果不加@LoadBalanced注解的话,会报java.net.UnknownHostException:springboot-h2异常,此时无法通过注册到Eureka Server上的服务名来调用服务,因为RestTemplate是无法从服务名映射到ip:port的,映射的功能是由LoadBalancerClient来实现的。
    public RestTemplate restTemplate() {
        return builder.build();
    }


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

}


消费者注册成功
在这里插入图片描述
获取服务列表的结果

[{"host":"192.168.10.103","port":9011,"metadata":{"management.port":"9011","jmx.port":"56141"},"secure":false,"uri":"http://192.168.10.103:9011","instanceId":"vip-PC:spring-cloud-eureka-producer:9011","serviceId":"SPRING-CLOUD-EUREKA-PRODUCER","instanceInfo":{"instanceId":"vip-PC:spring-cloud-eureka-producer:9011","app":"SPRING-CLOUD-EUREKA-PRODUCER","appGroupName":null,"ipAddr":"192.168.10.103","sid":"na","homePageUrl":"http://192.168.10.103:9011/","statusPageUrl":"http://192.168.10.103:9011/actuator/info","healthCheckUrl":"http://192.168.10.103:9011/actuator/health","secureHealthCheckUrl":null,"vipAddress":"spring-cloud-eureka-producer","secureVipAddress":"spring-cloud-eureka-producer","countryId":1,"dataCenterInfo":{"@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo","name":"MyOwn"},"hostName":"192.168.10.103","status":"UP","overriddenStatus":"UNKNOWN","leaseInfo":{"renewalIntervalInSecs":30,"durationInSecs":90,"registrationTimestamp":1548660640476,"lastRenewalTimestamp":1548661751127,"evictionTimestamp":0,"serviceUpTimestamp":1548660143567},"isCoordinatingDiscoveryServer":false,"metadata":{"management.port":"9011","jmx.port":"56141"},"lastUpdatedTimestamp":1548660640476,"lastDirtyTimestamp":1548660639779,"actionType":"ADDED","asgName":null},"scheme":null}]

测试请求的url
http://localhost:9012/callSayHello?name=9012
消费的结果

hello ---> 9012 port -->9011

到这里的话,Eureka在SpringCloud 构建微服务系统之服务注册和发现使用基本说明白,后面我将会将consulnacoseureka三种在服务注册与发现中做一个对比,分析各种优缺点等。

发布了140 篇原创文章 · 获赞 278 · 访问量 81万+

猜你喜欢

转载自blog.csdn.net/u010046908/article/details/86678822
今日推荐