02.Eureka
1.关于Eureka
Eureka提供基于REST的服务,在集群中用于服务管理。Eureka提供了一个客户端组件,该组件实现了负载均衡的功能,为业务组件的集群部署提供了条件。
Eureka架构
基本的Eureka架构是由一个Eureka服务器以及若干个Eureka客户端组成。其中,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-eureka 和 fetch-registry 这两个属性默认值为true。如果按照默认值,会将该应用自身注册到服务器端,而自身本身又是服务器端。所以需要将这两个属性设置为false。否则,Eureka Server启动时候会抛出异常
抛出异常如下:
在启动类加上@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模块,否则启动之后程序又会自行停掉。报错的截图如下:
在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调用服务具备分布式能力。
启动服务、服务提供者、服务调用者
3.Eureka集群搭建
在上面的示例中,只是讲到初步搭建基本Eureka服务与客户端,并没有体现出高可用的特点。
通过运行两个Eureka Server,两个服务示例(一个调用者、一个提供者)来构建简单的集群示例。通过HttpClient去访问来测试。
示例架构图为
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的实例。
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方法,情况如下:
从上图可以看到,调用服务,实际分别调用了不同的服务提供者
注意 first-springcloud-rest-client模块,pom.xml文件需要引入commons-logging,否则会出现以下错误