1 说明
因为 SpirngBoot, SpringCloud 的各个版本之间差异还是挺大的, 所以在参照本博客进行学习时, 有可能出现因为版本不一致, 而出现不同的问题。
如果可以和本项目使用的环境保持一致, 即使不一致, 也尽可能不要跨大版本。
环境清单
框架 | 版本 |
---|---|
JDK | 1.8 |
Spring Boot | 2.1.4.RELEASE |
Spring Cloud | Greenwich.SR1 |
2 准备
- 先建立一个 Maven 的父模块, 也就是整个项目里面只有一个 pom 文件
- 在父 pom 里面添加一些共用的配置, 比如 SpringBoot, SpringCloud 的依赖
<?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>
<!-- 手动引入 spring boot 版本依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>lcn29.github.io</groupId>
<artifactId>spring-cloud-eureka</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<!-- 手动引入 spring cloud 对应的配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 手动 引入 spring cloud 的基础模块-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
</dependencies>
<!-- 省略一些打包相关的配置 -->
</project>
3 建立一个单机的 Eureka 服务端 (注册中心)
- 在父模块里面新建一个子模块, 模块名
eureka-server-one
- 在子模块引入 Eureka 服务端需要的依赖
<dependencies>
<!-- eureka 服务端的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
- 在启动类上加上注解
@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerOne {
public static void main(String[] args) {
SpringApplication.run(EurekaServerOne.class, args);
}
}
- 项目这时会一直在报错, 因为当前应用会把自己当做一个服务注册到服务端, 此处需要停止它的这个行为, 在 SpringBoot 的配置文件
application.yml
(Maven 模块是没有这个文件的, 需要在 src/main/ 下面建立一个 resources 文件夹, 然后在里面创建这个文件), 加上这 2 个配置
# 应用的名字
spring:
application:
name: eureka-server-one
eureka:
client:
# 禁止把自己注册到 eureka 的服务端
register-with-eureka: false
# 不从 eureka 服务端拉取节点信息
fetch-registry: false
# eureka 的服务端的注册地址, 告诉客户端服务的地址(让其知道去哪注册)
service-url:
defaultZone: http://localhost:8080/eureka/
通过浏览器访问
http://localhost:8080/
, 就能看到 eureka 的管理页面这时候虽然程序运行起来的, 但是如果你查看页面的 General Info 项里面的 unavailable-replicas 会发现我们的 eureka 服务端显示为不可用的, 但实际它是可用的。
自此, 我们的 Eureka 的服务端就可以了。
4 建立一个 Eureka 客户端
- 在父模块里面建立一个子模块, 模块名
eureka-client-one
- 在子模块引入 Eureka 客户端需要的依赖
<!-- eureka 服务端的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- web 功能的支持, 没有这个 客户端就会启动完就结束程序 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 然后在启动类上加上注解
@EnableEurekaClient
@EnableEurekaClient
@SpringBootApplication
public class EurekaClientOne {
public static void main(String[] args){
SpringApplication.run(EurekaClientOne.class, args);
}
}
- 在
application.yml
中添加配置
spring:
application:
name: eureka-client-one
server:
port: 9091
eureka:
client:
service-url:
# 服务端的注册地址
defaultZone: http://localhost:8080/eureka/
- 打开 eureka 的服务端界面, 可以看到
Instances currently registered with Eureka
项里面有刚刚启动的客户端的ApplicationName, 同时状态为 UP, 说明客户端已经注册到服务端了。
5 Eureka 客户端间服务调用
在父模块里面再建立一个子模块, 模块名
eureka-client-two
, 作为服务的调用方依赖, 启动类, 配置和
eureka-client-one
一样。 记得把application.yml
中的 applicationName 修改为eureka-client-two
, 端口修改为另一个没有使用的, 这样我们就有 2 个客户端了让
eureka-client-one
作为服务的提供方, 提供一个 Rest Api 接口@RestController public class MyServerController { @GetMapping("/server/{id}") public String server(@PathVariable("id") int id) { return "收到请求Id : " + id + ", 结束"; } }
服务调用方
eureka-client-two
引入 Feign, 作为服务间调用的方式
<!--远程调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 服务的调用方 (这里为 eureka-client-two), 创建一个接口
// name 是服务提供方的应用名
@FeignClient(name = "eureka-client-one")
public interface RemoteServer {
// 调用的 Rest api, 入参, 出参, 请求路径方式和远程的 Rest Api 一样
@GetMapping("/server/{id}")
String server(@PathVariable("id")int id);
}
- 在调用方
eureka-client-two
的实例中, 注入刚声明的接口
@RestController
public class FeignController {
@Resource
private RemoteServer remoteServer;
@GetMapping("/feign/{id}")
public String feign(@PathVariable("id")int id) {
String result = remoteServer.server(id);
return result;
}
}
- 在调用方
eureka-client-two
的启动类上加上注解@EnableFeignClients
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class EurekaClientTwo {
public static void main(String[] args){
SpringApplication.run(EurekaClientTwo.class, args);
}
}
- 依次启动
eureka-server
,eureka-client-one
,eureka-client-two
, 然后在浏览器输入http://localhost:{eureka-client-two设置的端口}/feign/123
, 可以看到收到请求Id : 123, 结束
就是成功了
6 Eureka 服务端集群配置
Eureka 作为注册中心, 一旦挂了, 基本整个系统就可能无法使用了 (如果 Eureka 是在运行过一段时间后才挂的, 同时各个客户端之间都有其他客户端的缓存, 还是能通信的, 就是无法加入新节点), 所以无特殊情况, Eureka 服务端都是以集群的形式部署的。
同样的依照
eureka-server-one
, 新建一个eureka-server-two
Maven 模块, 启动类, 配置, 依赖都一样。修改
eureka-server-one
和eureka-server-two
的配置
# 这个是 eureka-server-one 的配置
spring:
application:
name: eureka-server
server:
port: 8081
eureka:
client:
service-url:
# 向另外一个服务端注册自己, 如果有多个服务端, 通过逗号分隔就行了
defaultZone: http://localhost:8082/eureka/
# 这个是 eureka-server-two 的配置
spring:
application:
name: eureka-server
server:
port: 8082
eureka:
client:
service-url:
# 向另外一个服务端注册自己, 如果有多个服务端, 通过逗号分隔就行了
defaultZone: http://localhost:8081/eureka/
因为是集群配置, 所以 2 个服务端的应用名都是一样的
启动项目 (第一个启动的会报错, 因为找不到需要的注册中心, 但你把第二个注册中心启动了, 就不会报错了) 依次访问
http://localhost:8081
和http://localhost:8082
都可以访问, 但是你会发现你的服务端都是unavailable-replicas
的。 这是因为eureka.client.serviceUrl.defaultZone 配置项的地址, 不能使用 localhost, 要使用域名
如果要解决服务端的
unavailable-replicas
状态, 可以临时通过修改 hosts 处理, 在电脑的 hosts 文件里面添加这 2 行
127.0.0.1 eureka-8081.com
127.0.0.1 eureka-8082.com
- 修改配置文件的
defaultZone
项, 同时增加Instances
的配置, 其他的都不用修改
# 这个是 eureka-server-one 的配置
eureka:
instance:
# 当前服务的域名
hostname: eureka-8081.com
# 实例的名字, 上面的applicationName可以看出一个组, 而这里是说明当前的服务是组中的哪一个
instance-id: ${spring.application.name}:${server.port}
client:
service-url:
# 向另外一个服务端注册自己, 如果有多个服务端, 通过逗号分隔就行了
defaultZone: http://eureka-8082.com:8082/eureka/
# 这个是 eureka-server-two 的配置
eureka:
instance:
# 当前服务的域名
hostname: eureka-8082.com
# 实例的名字, 上面的applicationName可以看出一个组, 而这里是说明当前的服务是组中的哪一个
instance-id: ${spring.application.name}:${server.port}
client:
service-url:
# 向另外一个服务端注册自己, 如果有多个服务端, 通过逗号分隔就行了
defaultZone: http://eureka-8081.com:8082/eureka/
- 最终的效果