02.Eureka

1.关于Eureka

  Eureka提供基于REST的服务,在集群中用于服务管理。Eureka提供了一个客户端组件,该组件实现了负载均衡的功能,为业务组件的集群部署提供了条件。

  Eureka架构

  基本的Eureka架构是由一个Eureka服务器以及若干个Eureka客户端组成。其中,Eureka客户端中,有多个服务提供者,一个服务调用者组成。

  02.Eureka

  服务器端

1.对于注册到服务端的服务组件,Eureka没有提供后台的存储,注册的服务信息保存在内存的注册中心,通过心跳保持最新的状态;

2.对于客户端,注册信息同样也保存在内存中,这样有助于提升Eureka的性能,每次服务请求都不需要经过服务器端的注册中心

  服务提供者

  服务提供者,主要有以下工作:

1.向服务器端注册服务;

2.发送心跳给服务器

3.从服务器获取注册列表

  服务调用者

  服务调用者也作为Eureka客户端,主要职责是发现服务和调用服务。

2.第一个Eureka应用

  版本说明

1.jdk 1.8
2.spring boot 2.1.3
2.1 搭建一个父级工程

  由于开发工具使用的是IDEA,为方便开发,将Eureka服务器项目,服务提供者,服务调用者都放在同一个父级工程里。

  新建一个spring boot工程,不引用任何模块。父级工程的pom.xml文件设置如下:

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

    <groupId>com.kingdom</groupId>
    <artifactId>first-springcloud-app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>first-springcloud-app</name>
    <description>Spring Cloud学习,第一个应用</description>
    <packaging>pom</packaging>

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

    <!--模块-->
    <modules>
        <module>first-springcloud-eureka</module>
        <module>first-springcloud-provider</module>
        <module>first-springcloud-invoker</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
2.2 新建Eureka Server工程模块

  新建spring boot工程,引入选择spring cloud server模块,pom.xml文件设置如下:

    <parent>
        <groupId>com.kingdom</groupId>
        <artifactId>first-springcloud-app</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.kingdom</groupId>
    <artifactId>first-springcloud-eureka</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>first-springcloud-eureka</name>
    <description>Spring Cloud学习,第一个应用,Eureka</description>
    <packaging>jar</packaging>

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

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

  添加配置文件application.yml

server:
  port: 8761
# Eureka Server设置为不到Eureka服务器获取注册信息
eureka:
  client:
    register-with-eureka: false   # 服务自身不需要注册信息
    fetch-registry: false         # 不用去获取注册信息

  注意register-with-eurekafetch-registry 这两个属性默认值为true。如果按照默认值,会将该应用自身注册到服务器端,而自身本身又是服务器端。所以需要将这两个属性设置为false。否则,Eureka Server启动时候会抛出异常

  抛出异常如下:

  
02.Eureka

  在启动类加上@EnableEurekaServer注解

2.3 新建服务提供者

  新建spring boot项目,添加spring cloud netflix client,config,spring boot web模块,pom.xml配置文件如下:

    <parent>
        <groupId>com.kingdom</groupId>
        <artifactId>first-springcloud-app</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.kingdom</groupId>
    <artifactId>first-springcloud-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>first-springcloud-provider</name>
    <description>Spring Cloud学习,第一个应用,Provider</description>
    <packaging>jar</packaging>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</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>
    </dependencies>

  有个问题需要注意: 必须要引入spring boot web模块,否则启动之后程序又会自行停掉。报错的截图如下:

  02.Eureka

  在application.yml配置文件中需要指定:

1.自身应用的端口,server.port
2.应用名称,用于注册到eureka服务器,spring.application.name
3.指定eureka server的路径,eureka.client.service-url.default-zone
server:
  port: 8090

spring:
  application:
    name: first-springcloud-provider  # 设置应用名

eureka:
  instance:
    hostname: localhost # 设置主机名
  client:
    service-url:
      defaule-zone: http://localhost:8761/eureka/  # 服务地址

  在启动类上加上@EnableEurekaClient注解

2.4 新建服务消费者模块

  新建spring boot工程,引入模块与服务提供者一样之外,另外还引入了ribbon,用于分布式处理调用

    <parent>
        <groupId>com.kingdom</groupId>
        <artifactId>first-springcloud-app</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.kingdom.springcloud</groupId>
    <artifactId>first-springcloud-invoker</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>first-springcloud-invoker</name>
    <description>Spring Cloud学习,第一个应用,invoker调用者</description>
    <packaging>jar</packaging>

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

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

        <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-netflix-ribbon</artifactId>
        </dependency>

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

  application.yml文件配置

server:
  port: 9090

spring:
  application:
    name: first-springcloud-invoker

eureka:
  instance:
    hostname: localhost
  client:
    service-url:
      default-zone: http://localhost:8761/eureka/

  服务提供者测试controller

package com.kingdom.springcloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RequestMapping("/test/provider")
@RestController
public class TestProviderController {

    @GetMapping("/info/{id}")
    public Map info(@PathVariable String id) {
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("id", id);
        userMap.put("name", "Kingdom.Chen");
        userMap.put("phone", "18814095605");
        userMap.put("age", 24);

        return userMap;
    }
}

  在启动类加上服务发现的注解:@EnableDiscoveryClient

  服务调用的控制类

package com.kingdom.springcloud.controller;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@Configuration
@RestController
public class TestInvokerController {

    @Bean
    @LoadBalanced   // 加入这个注解,使得RestTemplate具备分布式访问的能力
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @GetMapping("/router")
    public String invoke() {
        RestTemplate restTemplate = getRestTemplate();
        // 根据服务名称进行调用
        String json = restTemplate.getForObject("http://first-springcloud-provider/test/provider/info/1", String.class);

        return json;
    }
}

  注意: RestTemplate类不具备分布式处理的能力,加上ribbon模块中的@LoadBalanced注解,使得RestTemplate调用服务具备分布式能力。

  启动服务、服务提供者、服务调用者

  02.Eureka

  02.Eureka

3.Eureka集群搭建

  在上面的示例中,只是讲到初步搭建基本Eureka服务与客户端,并没有体现出高可用的特点。

  通过运行两个Eureka Server,两个服务示例(一个调用者、一个提供者)来构建简单的集群示例。通过HttpClient去访问来测试。

  示例架构图为
02.Eureka

3.1 修改本地电脑host文件

  由于是本地Windows系统学习测试,为了体现多个地址,通过修改host文件来体现。

127.0.0.1 slave1 slave2
3.2 修改Eureka Server模块

  修改application.yml配置文件,通过profiles方式设置多个server以及eureka server

server:
  port: 8761
spring:
  application:
    name: first-springcloud-eureka
  profiles: slave1
eureka:
  instance:
    hostname: slave1
  client:
    service-url:
      default-zone: http://slave2:8762/eureka/

---

server:
  port: 8762
spring:
  application:
    name: first-springcloud-eureka
  profiles: slave2
eureka:
  instance:
    hostname: slave2
  client:
    service-url:
      default-zone: http://slave1:8761/eureka/

  说明 在配置文件中,配置了两个本地地址slave1、slave2相互注册,register-with-eureka 和 fetch-registry 这两个属性默认值为true,启动时候出现异常可以忽略。

  修改启动类

package com.kingdom.springcloud;

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

import java.util.Scanner;

@EnableEurekaServer
@SpringBootApplication
public class FirstSpringcloudEurekaApplication {

    public static void main(String[] args) {
        // 读取控制台输入,决定用哪个profiles
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入需要启动的profiles");
        String profiles = scanner.nextLine();
        new SpringApplicationBuilder(FirstSpringcloudEurekaApplication.class).profiles(profiles).run(args);
    }

}

  说明 启动时候,运行两个实例,在IDEA工具中可以设置(如下图),两个运行的实例分别运行profiles为slave1、slave2的实例。

  02.Eureka

3.3修改服务提供者

  服务提供者在配置文件中,设置default-zone有多个。配置如下

spring:
  application:
    name: first-springcloud-provider  # 设置应用名

eureka:
  instance:
    hostname: localhost # 设置主机名
  client:
    service-url:
      defaule-zone: http://salve1:8761/eureka/,http://slave2:8762/eureka/  # 服务地址

  由于模拟多个服务提供者,也运行多个实例。所以在启动类中,通过控制台输入实例的端口来动态启动实例。启动类如下:

package com.kingdom.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


@EnableEurekaClient
@SpringBootApplication
public class FirstSpringcloudProviderApplication {

    public static void main(String[] args) {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("输入端口 port:");
        try {
            String port = bufferedReader.readLine();
            new SpringApplicationBuilder(FirstSpringcloudProviderApplication.class)
                    .properties("server.port=" + port).run(args);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
3.4 修改服务调用者模块

  服务调用者模块只需要在default-zone添加另一个eureka server即可

eureka:
  instance:
    hostname: localhost
  client:
    service-url:
      default-zone: http://slave1:8761/eureka/, http://slave2:8762/eureka/
3.5 新增first-springcloud-rest-client模块

  使用HttpClient调用服务,测试查看效果

package com.kingdom.springcloud;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

/**
 * 测试调用
 */
public class TestHttpClient {

    public static void main(String[] args) {
        // 创建HttpClien
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 调用6次服务看输出结果
        for (int i = 1; i <= 6; i++) {
            HttpGet httpGet = new HttpGet("http://localhost:9090/router");
            try {
                HttpResponse response = httpClient.execute(httpGet);
                System.out.println("第" + i + "次调用结果为:" + EntityUtils.toString(response.getEntity()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  在HtppClient类中,模拟调用6次服务,观察控制台打印的调用情况

3.6 测试情况

  》先启动两个Eureka Server实例,分别输入slave1、slave2

  》启动两个服务提供者实例,分别输入8091、8092端口

  》启动服务调用者

  》运行HttpClient测试类main方法,情况如下:

  02.Eureka

  从上图可以看到,调用服务,实际分别调用了不同的服务提供者

  注意 first-springcloud-rest-client模块,pom.xml文件需要引入commons-logging,否则会出现以下错误

  02.Eureka