Spring Cloud 入门到进阶 - 01 搭建Eureka服务环境
博主整理的SpringCloud系列目录:>>戳这里<<
内容关联篇:
Spring Cloud 入门到进阶 - 01 Eureka集群搭建(中)
Spring Cloud 入门到进阶 - 01 Eureka 服务实例的健康自检 (下)
一、Eureka介绍
Spring Cloud 集成了 Netflix OSS 多个项目,形成了 spring-cloud-netflix 项目。该项目包含多个子模块,这些子模块对集成的 Netflix 旗下的框架进行了封装,我们这里将讲述其中一个较为重要的服务管理框架: Eureka
1.1 关于 Eureka
Eureka 提供基于 REST 的服务,在集群中主要用于服务管理。Eureka 提供了基于 Java 语言 的客户端组件,客户端组件实现了负载均衡的功能,为业务组件的集群部署创造了条件。使用该框架,可以将业务组件注册到 Eureka 容器中,这些组件可进行集群部署,Eureka 主要维护这些服务的列表并自动检查它们的状态
1.2 Eureka 架构
一个简单的 Eureka 集群,包含一个 Eureka 服务器(注册中心)、若干个服务提供者、若干个服务调用者。如下图所示:
有两个Eureka服务器注册中心,服务器支持集器部署,每个服务器也可以作为对方服务器的客户端进行互相注册与复制。有三个Eureka客户端,两个用于发布,另一个用与服务调用。不管是服务器还是客户端,都可以部署多个实例,如此一来,很容易构建高可用的服务集群。
1.3 Eureka 服务端
对于注册到服务器端的服务组件,Eureka 服务器并没有提供后台的存储,这些注册的服务实例被保存在内存的注册中心,它们通过心跳来保持其最新状态,这些操作都可以在内存中完成。客户端存在着相同的机制,同样在内存中保存了注册表信息,这样的机制提升了 Eureka 组件的性能,每次服务的请求都不必经过服务器端的注册中心。
1.4 Eureka 服务提供者
作为 Eureka 客户端存在的服务提供者,主要进行以下工作: 第一,向服务器注册服务;第二,发送心跳给服务器;第三 向服务器端获取注册列表。当客户端注册到服务器时,它将会提供一些关于自己的信息给服务器端,例如自己的主机、端口、健康检测连接等。
1.5 Eureka 服务调用者
对于发布到 Eureka 服务器的服务,服务调用者可对其进行服务查找与调用,服务调用者也是作为客户端存在的,但其职责主要是发现与调用服务。在实际情况中,有可能出现本身既是服务提供者,又是服务调用者的情况,例如在传统的企业应用三层架构中,服务层会调用数据访问层的接口进行数据操作,它本身也会提供服务给控制层使用。
二、第一个 Eureka 应用
这里我们将编写一个 Hello World 小程序。其中包括一个 Eureka 服务中心、两个服务提供者、一个服务调用者。
1、Eureka 服务器搭建
先创建一个名称为 first-ek-server
的 Maven 项目作为服务器,在 pom.xml 文件中加入 Spring Cloud 的依赖。
1.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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.swotxu</groupId>
<artifactId>first-ek-server</artifactId>
<version>1.0-SNAPSHOT</version>
<name>first-ek-server</name>
<description>This is the service</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 此依赖为了方便使用,非必须的 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
因为spring-cloud-starter-eureka-server
会自动引入spring-boot-starter-web
,因此只需要加入该依赖,我们的项目就具有 Web 容器的功能了。
接下来,编写一个最简单的启动类,启动我们的 Eureka 服务器。
1.2、服务启动类
package com.swotxu.firstekserver;
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;
/**
* 第一个 Eureka 服务
*
* @Date: 2020/6/25 20:32
* @Author: swotXu
*/
@SpringBootApplication
@EnableEurekaServer // 声明这是一个 Eureka 服务器
public class FirstEkServer {
public static void main(String[] args) {
/*
以下两种写法都可以,
1、SpringApplication.run(FirstEkServer.class, args)
内部会创建当前对象实例,并调用其run方法,源码如下:
(new SpringApplication(sources)).run(args);
2、new SpringApplicationBuilder(FirstEkServer.class).run(args);
在创建SpringApplicationBuilder对象的时候,我们查看源码会发现,它内部
其实依旧是创建了SpringApplication对象,源码如下:
new SpringApplication(sources);
当调用其run方法时,最终还是委托给了SpringApplication。
由此可见,虽然写法不同,但实际上殊途同归。
同时,SpringApplicationBuilder提供给了我们一定的扩展能力,这个咱们后面再说
*/
//SpringApplication.run(FirstEkServer.class, args);
new SpringApplicationBuilder(FirstEkServer.class).run(args);
}
}
由于本例中并没有配置服务器端口,因此默认端口为 8080 ,我们将端口配置为 8761,在 src/main/resources
目录下创建 application.yml 配置文件,内容如下:
server:
port: 8761
可以看到已经启动成功,端口为8761。启动过程中会出现部分异常信息,暂时不需要进行处理,下面再说。成功启动后,打开浏览器,输入 http://localhost:8761
,可以看到 Eureka 服务器控制台,如下图:
可以看到,服务实例列表为空。
- 服务器注册开关
上面提到过,在服务启动时,会出现如下两个异常信息:
这是由于在服务器启动时,服务器会把自己当作一个客户端,去注册 Eureka 服务器,并且会到 Eureka 服务器抓取注册信息,它自己本身只是一个服务器,而不是服务的提供者(客户端),因此可以修改 application. yml 文件,添加以下两个配置,即可避免异常信息。java.net.ConnectException: Connection refused: connect com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
1.3、配置 application.yml
server:
port: 8761
eureka:
client:
register-with-eureka: false # 是否将自己的信息注册到 Eureka 服务器
fetch-registry: false # 是否到 Eureka 服务器抓取注册信息
1.4、项目完整结构图 - first-ek-server
2、搭建服务提供者
这里我们使用 SpringBoot 创建一个名称为 first-ek-server-provider
的 Web 工程,并在里面编写一个 REST 服务。其中 pom.xml 依赖如下所示。
2.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 https://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.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.swotxu</groupId>
<artifactId>first-ek-server-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>first-ek-server-provider</name>
<description>This is the service provider</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 由于 eureka-client 并没有为我们引入 web 组件,因此需要我们手动引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 此依赖为了方便使用,非必须的 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2.2、配置 application.yml
在 src/main/resources
目录下创建 application.yml 配置文件,内容如下:
spring:
application:
name: first-ek-server-provider
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
以上配置中,将应用名称配置为 first-ek-server-provider
,该服务将会被注册到端口为 8761
的 Eureka 服务器,也就是前面搭建的服务器。另外,还使用了 eureka.instance.hostname
来配置该服务实例的主机名。下面,我们编写一个 Controller 类,并提供一个简单的 REST 服务。
2.3、服务提供类
package com.swotxu.firstekserverprovider.web;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @Date: 2020/6/25 20:50
* @Author: swotXu
*/
@RestController
public class FirstController {
@RequestMapping(value = "/user/{userId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public UserInfo findUser(@PathVariable("userId") Integer userId){
UserInfo userInfo = new UserInfo(userId, "swotxu", 18);
return userInfo;
}
}
// ------------------------------------------------
package com.swotxu.firstekserverprovider.web;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Date: 2020/6/25 20:51
* @Author: swotXu
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
private Integer id;
private String username;
private Integer age;
}
2.4、服务启动类
编写启动类,在启动类中 ,使用了@EnableEurekaClient
注解,声明该应用是一个 Eureka 客户端,代码如下:
package com.swotxu.firstekserverprovider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class FirstEkServerProviderApplication {
public static void main(String[] args) {
// SpringApplication.run(FirstEkServerProviderApplication.class, args);
new SpringApplicationBuilder(FirstEkServerProviderApplication.class).run(args);
}
}
完成后,先运行 first-ek-server
服务器,再运行 first-ek-server-provider
服务提供者,在浏览器中访问 Eureka 服务器控制台:http://localho st:8761
,可以看到我们的服务提供者已经注册到服务器了。
2.5、项目完整结构图 - first-ek-server-provider
接下来,我们编写服务调用者。
3、搭建服务调用者
服务被注册、发布到 Eureka 服务器后,需要有程序去发现它,并且进行调用。此处所说的调用者,是指同样注册到 Eureka 的客户端,来调用其他客户端发布的服务。
3.1、依赖 pom.xml
创建一个名称为 first-ek-server-invoker
的项目,在 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 https://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.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.swotxu</groupId>
<artifactId>first-ek-server-invoker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>first-ek-server-invoker</name>
<description>This is the service caller</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<!-- 由于 eureka-client 并没有为我们引入 web 组件,因此需要我们手动引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 此依赖为了方便使用,非必须的 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
</project>
因为spring-cloud-starter-eureka-client
会自动引入spring-cloud-starter-netflix-ribbon
,因此只需要加入该依赖,我们的项目就具有 Ribbon 相关功能了。
3.2、配置 application.yml
在 src/main/resources
目录下创建 application.yml 配置文件,内容如下:
server:
port: 9000
spring:
application:
name: first-ek-server-invoker
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
在配置文件中,将应用名称配置为 first-ek-server-invoker
,这个调用者的访问端口为 9000,需要注意的是,这个调用者本身也可以对外提供服务。与提供者一样,使用 eureka 的配置,将该调用者注册到 first-ek-server
上面。下面,我们编写一个控制器,让调用者对外提供一个测试的服务。
3.3、服务调用类
package com.swotxu.firstekserverinvoker;
import lombok.extern.slf4j.Slf4j;
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.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @Date: 2020/6/27 17:09
* @Author: swotXu
*/
@Slf4j
@Configuration
@RestController
public class lnvokerController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String router(){
RestTemplate restTemplate = getRestTemplate();
// 根据应用名称调用服务。其中 first-ek-server-provider 是服务提供者配置的服务名(spring.application.name)
String json = restTemplate.getForObject("http://first-ek-server-provider/user/1", String.class);
log.info("result: {}", json);
return json;
}
}
在控制器中,配置了 RestTemplate
的 Bean,RestTemplate
本来是 spring-web
模块下面的类,主要用来调用 REST 服务。本身并不具备调用分布式服务的能力,但是 RestTemplate
的 Bean 被 @LoadBalanced
注解修饰后,这个 RestTemplate
实例就具有访问分布式服务的能力了。关于该类的机制,我们后面再讲。
在控制器中,新建了一个 router 的测试方法,用来对外发布 REST 服务。该方法只起路由作用,实际上是使用 RestTemplate
来调用 first-ek-server-provider
(服务提供者)的服务。
3.4、服务启动类
接下来编写启动类,代码如下。
package com.swotxu.firstekserverinvoker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class FirstEkServerInvokerApplication {
public static void main(String[] args) {
SpringApplication.run(FirstEkServerInvokerApplication.class, args);
}
}
在启动类中,使用了 @EnableDiscoveryClient
注解来修改启动类,该注解使得服务调用者有能力去 Eureka 中发现服务。需要注意的是,@EnableEurekaClient
注解己经包含了 @EnableDiscoveryClient
的功能,也就是说,一个 Eureka 客户端,本身就具有发现服务的能力。
@EnableEurekaClient
和@EnableDiscoveryClient
区别
共同点:两者都够让注册中心能够发现,扫描到该服务。
不同点:@EnableEurekaClient
只适用于Eureka作为注册中心,@EnableDiscoveryClient
可以是其他注册中心。
3.5、项目完整结构图 - first-ek-server-invoker
4、运行测试
上面三个项目编写完成后,我们依次启动:
- 启动服务器(first-ek-server)
- 启动服务提供者(first-ek-server-provider)
- 启动服务调用者(first-ek-server-invoker)
全部启动完成后,在浏览器访问 http://localhost:8761/
,即可看到注册的客户信息:
接下来,我们在浏览器中访问服务调用者发布的 router 服务:http://localhost:9000/router
,可以看到在浏览器中输出如下信息:
根据输出可知,实际上调用了服务提供者的 /user/1
服务,第一个 Eureka 应用到此结束,下面对这个应用程序的结构进行简单描述。
5、程序结构
从上图可以看到,Eureka 服务为本例的 first-ek-server,服务提供者为 first-ek-service-provider,而调用者为 first-ek-service-invoker ,用户通过浏览器访问调用者的 9000 端口的 router 服务、 router 服务中查找服务提供者的服务并进行调用 。在本例中,服务调用有点像路由器的角色。
6、源码下载
项目完整结构图:
码云Gitee仓库地址:https://gitee.com/swotxu/Spring-Cloud-Study.git
项目路径:Spring-Cloud-Study/01/springcloud01
为了能演示 Eureka 高可用特性,下篇,我们将会以本案例为基础,搭建一个复杂一点集群。
Spring Cloud 入门到进阶 - 02 Eureka集群搭建(下)
别忘了点赞关注收藏~