文章目录
1. Eureka
1.1 Eureka 简介
- Eureka 是 Netflix 公司开源的一个服务注册与发现的组件 。
- Eureka 和其他 Netflix 公司的服务组件(例如负载均衡、熔断器、网关等) 一起,被 Spring Cloud 社区整合为 Spring-Cloud-Netflix 模块。
- Eureka 包含两个组件:Eureka Server (注册中心) 和 Eureka Client (服务提供者、服务消费者)。
1.2 Eureka 学习步骤
一、搭建 Provider 和 Consumer 服务。
-
打开 IDEA --> New Project --> Empty Project --> 填写 Project name
-
选择 JDK
-
File --> New Module --> Maven --> 填写父工程信息
-
删除 src 文件夹后,项目目录如下
-
在父工程下,创建子模块 Provider 和 Consumer
-
添加依赖
spring-cloud-parent 的 pom 添加 spring-boot 父工程:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> </parent>
eureka-consumer 和 eureka-provider 添加 spring-boot 依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
二、使用 RestTemplate 完成远程调用
RestTemplate 简介:
- Spring 提供的一种简单便捷的模板类,用于在 java 代码里访问 restful 服务。
- 其功能与 HttpClient 类似,但是 RestTemplate 实现更优雅,使用更方便。
步骤如下:
-
在 eureka-provider 中创建类
启动类
package com.zt.provider; @SpringBootApplication public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class, args); } }
Controller
package com.zt.provider.controller; /** * Goods Controller 服务提供方 */ @RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; @GetMapping("/findOne/{id}") public Goods findOne(@PathVariable("id") int id){ Goods goods = goodsService.findOne(id); return goods; } }
Service
package com.zt.provider.service; /** * Goods 业务层 */ @Service public class GoodsService { @Autowired private GoodsDao goodsDao; /** * 根据id查询 * @param id * @return */ public Goods findOne(int id){ return goodsDao.findOne(id); } }
dao
package com.zt.provider.dao; /** * 商品Dao */ @Repository public class GoodsDao { public Goods findOne(int id){ return new Goods(id,"华为手机",3999,10000); } }
domain
package com.zt.provider.domain; /** * 商品实体类 */ public class Goods { private int id; private String title;//商品标题 private double price;//商品价格 private int count;//商品库存 // 构造方法 和 getter and setter 省略 }
application.yml
server: port: 8000
启动 eureka-provider,打开浏览器访问如下 url
http://localhost:8000/goods/findOne/1
{"id":1,"title":"华为手机","price":3999.0,"count":10000}
-
在 eureka-consumer 中创建类
启动类
package com.zt.consumer; @SpringBootApplication public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class, args); } }
配置类
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
Controller
package com.zt.consumer.controller; /** * 服务的调用方 */ @RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @GetMapping("/goods/{id}") public Goods findGoodsById(@PathVariable("id") int id){ /* //远程调用Goods服务中的findOne接口 使用RestTemplate 1. 定义Bean restTemplate 2. 注入Bean 3. 调用方法 */ String url = "http://localhost:8000/goods/findOne/"+id; // 3. 调用方法 Goods goods = restTemplate.getForObject(url, Goods.class); return goods; } }
domain
package com.zt.provider.domain; /** * 商品实体类 */ public class Goods { private int id; private String title;//商品标题 private double price;//商品价格 private int count;//商品库存 // 构造方法 和 getter and setter 省略 }
application.yml
server: port: 9000
启动 eureka-consumer,打开浏览器访问如下 url
http://localhost:9000/order/goods/1
{"id":1,"title":"华为手机","price":3999.0,"count":10000}
eureka-consumer 成功远程调用 eureka-provider,但如果此时 eureka-provider 的 url 发生变化,eureka-consumer 也要跟着变化,所以我们需要使用注册中心来解决耦合的问题
三、搭建 Eureka Server 服务。
-
创建 eureka-server 模块
-
引入 SpringCloud 和 euraka-server 相关依赖
在父工程的 pom 文件中引入 Spring Cloud 依赖
<properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <spring-cloud.version>Hoxton.SR1</spring-cloud.version> </properties> <!--引入Spring Cloud 依赖--> <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>
在 euraka-server 的 pom 文件中引入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- eureka-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies>
-
完成 Eureka Server 相关配置
启动类
package com.zt.eureka; @SpringBootApplication @EnableEurekaServer public class EurekaApp { public static void main(String[] args) { SpringApplication.run(EurekaApp.class, args); } }
application.yml
server: port: 8761 # eureka 配置 # eureka 一共有 4 部分配置 # 1. dashboard:eureka 的 web 控制台配置,默认开启 true # 2. server:eureka的服务端配置 # 3. client:eureka的客户端配置 # 4. instance:eureka的实例配置 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要 fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
-
启动该模块,访问 Web 控制台
http://localhost:8761
Web 控制台组成部分:
-
System Status:系统状态信息
-
DS Replicas:Eureka 集群节点副本
-
Instances currently registered with Eureka:当前注册到 Eureka 的实例
-
General Info:一般信息
-
Instance Info:实例信息
-
四、改造 Provider 和 Consumer 称为 Eureka Client,下面以 Provider 为例,Consumer 同样。
-
引 eureka-client 相关依赖
<!-- eureka-client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
完成 eureka client 相关配置
启动类添加 EurekaClient 注解
package com.zt.provider; @SpringBootApplication @EnableEurekaClient public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class, args); } }
application.yml
server: port: 8000 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
-
启动测试
五、Consumer 服务 通过从 Eureka Server 中抓取 Provider 地址完成远程调用
-
修改 Controller
package com.zt.consumer.controller; /** * 服务的调用方 */ @RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @GetMapping("/goods/{id}") public Goods findGoodsById(@PathVariable("id") int id){ /* 动态从Eureka Server 中获取 provider 的 ip 和端口 1. 注入 DiscoveryClient 对象.激活 2. 调用方法 */ //演示discoveryClient 使用 List<ServiceInstance> instances = discoveryClient.getInstances("EUREKA-PROVIDER"); //判断集合是否有数据 if(instances == null || instances.size() == 0){ //集合没有数据 return null; } ServiceInstance instance = instances.get(0); String host = instance.getHost();//获取ip int port = instance.getPort();//获取端口 String url = "http://"+host+":"+port+"/goods/findOne/"+id; // 3. 调用方法 Goods goods = restTemplate.getForObject(url, Goods.class); return goods; } }
-
启动类添加服务发现注解
package com.zt.consumer; @SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class, args); } }
-
启动 eureka-consumer,打开浏览器访问如下 url
http://localhost:9000/order/goods/1
{"id":1,"title":"华为手机","price":3999.0,"count":10000}
1.3 Eureka 相关配置及特性
eureka 一共有 4 部分配置:
-
server : eureka 的服务端配置
eureka: server: #是否开启自我保护机制,默认true enable-self-preservation: #清理间隔(单位毫秒,默认是60*1000) eviction-interval-timer-in-ms:
-
client : eureka 的客户端配置
eureka: client: service-url: # eureka服务端地址,将来客户端使用该地址和eureka进行通信 defaultZone: register-with-eureka: # 是否将自己的路径 注册到eureka上。 fetch-registry: # 是否需要从eureka中抓取数据。
-
instance : eureka 的实例配置
eureka: instance: hostname: localhost # 主机名 prefer-ip-address: # 是否将自己的ip注册到eureka中,默认false 注册 主机名 ip-address: # 设置当前实例ip instance-id: # 设置控制台显示的实例id lease-renewal-interval-in-seconds: 30 # 每一次eureka client 向 eureka server发送心跳的时间间隔 lease-expiration-duration-in-seconds: 90 # 如果90秒内eureka server没有收到eureka client的心跳包,则剔除该服务
-
dashboard : eureka 的 web 控制台配置
eureka: dashboard: enabled: true # 是否启用eureka web控制台 path: / # 设置eureka web控制台默认访问路径
1.4 Eureka 自我保护机制
自我保护背景
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与 Eureka Server 之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
自我保护机制
官方对于自我保护机制的定义:自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使 Eureka 集群更加的健壮、稳定的运行。
自我保护机制的工作机制是:如果在 15 分钟内超过 85% 的客户端节点都没有正常的心跳,那么 Eureka 就认为客户端与注册中心出现了网络故障,Eureka Server 自动进入自我保护机制。
此时会出现以下几种情况:
- Eureka Server 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
- Eureka Server 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
- 当网络稳定时,当前 Eureka Server 新的注册信息会被同步到其它节点中。
因此 Eureka Server 可以很好的应对因网络故障导致部分节点失联的情况,而不会像 ZK 那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。
开发环境中如果要实现服务失效能自动移除,只需要修改以下配置。
-
注册中心关闭自我保护机制,修改检查失效服务的时间。
eureka: server: enable-self-preservation: false eviction-interval-timer-in-ms: 3000
-
微服务修改减短服务心跳的时间。
# 默认90秒 lease-expiration-duration-in-seconds: 10 # 默认30秒 lease-renewal-interval-in-seconds: 3
以上配置建议在生产环境使用默认的时间配置。
1.5 Eureka 高可用
高可用:服务一直对外提供,一直可用。单机肯定无法实现高可用,所以需要搭建 Eureka 集群才能实现高可用。
搭建高可用步骤:
-
准备两个 Eureka Server
-
分别进行配置,相互注册
eureka-server1:
server: port: 8761 eureka: instance: hostname: eureka-server1 # 主机名 client: service-url: defaultZone: http://eureka-server2:8762/eureka register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要 fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要 spring: application: name: eureka-server-ha
eureka-server2:
server: port: 8762 eureka: instance: hostname: eureka-server2 # 主机名 client: service-url: defaultZone: http://eureka-server1:8762/eureka register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要 fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要 spring: application: name: eureka-server-ha
修改 host 文件
# My hosts 127.0.0.1 eureka-server1 127.0.0.1 eureka-server2
分别访问 url,它们相互都是副本
http://localhost:8761/ http://localhost:8762/
-
Eureka Client 分别注册到这两个 Eureka Server 中
修改 Provider 的 application.yml
server: port: 8001 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://eureka-server1:8761/eureka,http://eureka-server2:8762/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
修改 Consumer 的 application.yml
server: port: 9000 eureka: instance: hostname: localhost # 主机名 client: service-url: defaultZone: http://eureka-server1:8761/eureka,http://eureka-server2:8762/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信 spring: application: name: eureka-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
-
启动后测试,如果功能正常,就任意关掉一个 eureka-server,再次测试功能是否正常,如果还是正常则高可用搭建成功。
2. Consul
2.1 Consul 简介
- Consul 是由 HashiCorp 基于 Go 语言开发的,支持多数据中心,分布式高可用的服务发布和注册服务软件,因为是软件,所以需要先进行安装。
- 用于实现分布式系统的服务发现与配置。
- 使用起来也较为简单。具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署 。
- 官网地址: https://www.consul.io
2.2 Consul 安装运行
-
进入官网下载 Consul,解压后就一个 consul.exe 执行文件
-
启动 Consul,打开 cmd 输入下面命令
.\consul.exe agent -dev
-
访问 Consul 控制台
2.3 Consul 快速入门
-
搭建 Provider 和 Consumer 服务,和上面未改造 Provider 和 Consumer 为 Eureka Client 之前的微服务保持一致。
-
将 Provider 服务注册到 Consul 中。
添加 Consul 依赖
<dependencies> <!--consul 客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</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.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
添加 application.yml
server: port: 8000 spring: cloud: consul: host: localhost # consul 服务端的 ip port: 8500 # consul 服务端的端口 默认8500 discovery: service-name: ${spring.application.name} # 当前应用注册到consul的名称 prefer-ip-address: true # 注册ip application: name: consul-provider # 应用名称
-
Consumer 服务通过从 Consul 中抓取 Provider 地址完成远程调用
添加 Consul 依赖和 application.yml 同上
使用 Consul 完成远程调用和 Eureka 中一模一样,代码如下:
package com.zt.consumer.controller; /** * 服务的调用方 */ @RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @GetMapping("/goods/{id}") public Goods findGoodsById(@PathVariable("id") int id){ System.out.println("findGoodsById..."+id); /* //远程调用Goods服务中的findOne接口 使用RestTemplate 1. 定义Bean restTemplate 2. 注入Bean 3. 调用方法 */ /* 动态从Eureka Server 中获取 com.zt.provider 的 ip 和端口 1. 注入 DiscoveryClient 对象.激活 2. 调用方法 */ //演示discoveryClient 使用 List<ServiceInstance> instances = discoveryClient.getInstances("CONSUL-PROVIDER"); //判断集合是否有数据 if(instances == null || instances.size() == 0){ //集合没有数据 return null; } ServiceInstance instance = instances.get(0); String host = instance.getHost();//获取ip int port = instance.getPort();//获取端口 System.out.println(host); System.out.println(port); String url = "http://"+host+":"+port+"/goods/findOne/"+id; // 3. 调用方法 Goods goods = restTemplate.getForObject(url, Goods.class); return goods; } }
-
启动 Provider 和 Consumer 服务,进行测试
打开浏览器访问如下 url
http://localhost:9000/order/goods/1
{"id":1,"title":"华为手机","price":3999.0,"count":10000}
3. Nacos
3.1 Nacos 简介
- Nacos(Dynamic Naming and Configuration Service) 是阿里巴巴2018年7月开源的项目。
- 它专注于服务发现和配置管理领域 致力于帮助您发现、配置和管理微服务。Nacos 支持几乎所有主流类型的“服务”的发现、配置和管理。
- 一句话概括就是Nacos = Spring Cloud注册中心 + Spring Cloud配置中心。
- 官网:https://nacos.io/
- 下载地址: https://github.com/alibaba/nacos/releases
3.2 Nacos 安装运行
-
进入官网下载 Nacos,解压后进到 bin 目录
D:\nacos-server\nacos\bin
-
打开 cmd 输入下面命令,以单机模式启动
startup.cmd -m standalone
-
访问 Nacos 控制台,登录名密码都为 Nacos
http://localhost:8848/nacos/
3.3 Nacos 快速入门
-
搭建 Provider 和 Consumer 服务,和上面未改造 Provider 和 Consumer 为 Eureka Client 之前的微服务保持一致。
-
将 Provider 服务注册到 Nacos 中。
添加 Nacos 依赖
<dependencies> <!--nacos--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.2.2.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>1.1.0</version> </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.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
添加 application.yml
server: port: 8000 spring: cloud: nacos: discovery: server-addr: localhost:8848 # 配置nacos 服务端地址 application: name: nacos-provider # 服务名称
-
Consumer 服务通过从 Nacos 中抓取 Provider 地址完成远程调用
添加 Nacos 依赖和 application.yml 同上
使用 Nacos 完成远程调用和 Eureka 中一模一样,代码如下:
package com.zt.consumer.controller; /** * 服务的调用方 */ @RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @GetMapping("/goods/{id}") public Goods findGoodsById(@PathVariable("id") int id){ System.out.println("findGoodsById..."+id); /* //远程调用Goods服务中的findOne接口 使用RestTemplate 1. 定义Bean restTemplate 2. 注入Bean 3. 调用方法 */ /* 动态从Eureka Server 中获取 com.zt.provider 的 ip 和端口 1. 注入 DiscoveryClient 对象.激活 2. 调用方法 */ //演示discoveryClient 使用 List<ServiceInstance> instances = discoveryClient.getInstances("nacos-provider"); //判断集合是否有数据 if(instances == null || instances.size() == 0){ //集合没有数据 return null; } ServiceInstance instance = instances.get(0); String host = instance.getHost();//获取ip int port = instance.getPort();//获取端口 System.out.println(host); System.out.println(port); String url = "http://"+host+":"+port+"/goods/findOne/"+id; // 3. 调用方法 Goods goods = restTemplate.getForObject(url, Goods.class); return goods; } }
-
启动 Provider 和 Consumer 服务,进行测试
打开浏览器访问如下 url
http://localhost:9000/order/goods/1
{"id":1,"title":"华为手机","price":3999.0,"count":10000}