程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • 微服务基础

    • 了解SpringCloud微服务架构
    • 启动多个微服务项目
    • Eureka 服务注册
      • 1.案例准备
        • 1.1 案例说明
        • 1.2 数据库准备
        • 1.3 导入案例
        • 1.4 实现服务拆分-远程调用:
        • 1.4 启动微服务模块测试
        • 1.5 案例代码问题分析
      • 2. Eureka服务注册中心
        • 2.1 关于服务注册中心
        • 2.1.1注册中心实现原理
        • 2.1.2主流服务中心对比
        • 2.2 服务注册中心组件 Eureka
        • 2.3 搭建单例Eureka Server服务注册中心
        • 1、创建新模块EurekaServer,引入eureka-server依赖
        • 2. 在启动类添加@EnableEurekaServer 注解
        • 3. 在yml文件中配置Eureka server服务端口,服务名等信息
        • 4. 启动服务访问测试
        • 5. 将用户服务和订单服务注册到Eureka注册中心
        • 2.4 搭建Eureka Server 高可用集群
        • 2.5 eureka服务发现-服务拉取
        • 2.6 Eureka细节详解
        • 2.6.1 Eureka元数据详解
        • 2.6.2 Eureka客户端详解
        • 2.6.3 Eureka服务端详解
    • Ribbon 负载均衡
    • Nacos 下载与安装
    • Nacos 服务注册
    • Nacos 配置管理
    • openFeign 远程调用
    • OpenFeign 最佳实践
    • Gateway 网关
    • Gateway 进阶使用
    • Sentinel 服务保护
    • Sentinel 整合 Feign
    • 分布式事务 Seata
    • 什么是分布式
    • 什么是RPC框架
  • 微服务之DDD架构思想

  • 微服务
  • 微服务基础
scholar
2024-08-24
目录

Eureka 服务注册

# Eureka 服务注册

前言

在上一节 [初步了解SpringCloud微服务架构], 我们初步学习了微服务架构的体系结构,对SpringCloud也有一个简单的了解。这一节我们来进一步认识SpringCloud的一些核心组件,在此之前我们先来一个案例练练手。

# 1.案例准备

# 1.1 案例说明

本部分我们按照普通方式模拟一个微服务之间的调用,后续我们将一步步使用Spring Cloud的组件对案例进行改造

需求:

  • 订单微服务和用户微服务都必须有各自的数据库,相互独立

  • 订单服务和用户服务都对外暴露Restful的接口

  • 订单服务如果需要查询用户信息,只能调用用户服务的Restful接口,不能查询用户数据库

    完整流程图

下载 (1)

cloud-demo:父工程,管理依赖

  • order-service:订单微服务,负责订单相关业务
  • user-service: 用户微服务,负责用户相关业务

# 1.2 数据库准备

本次笔者数据库使用的是MySQL8

订单表

/*
 Navicat Premium Data Transfer

 Source Server         : local
 Source Server Type    : MySQL
 Source Server Version : 50622
 Source Host           : localhost:3306
 Source Schema         : heima

 Target Server Type    : MySQL
 Target Server Version : 50622
 File Encoding         : 65001

 Date: 06/09/2023 14:57:18
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_order
-- ----------------------------
DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id',
  `user_id` bigint(20) NOT NULL COMMENT '用户id',
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品名称',
  `price` bigint(20) NOT NULL COMMENT '商品价格',
  `num` int(10) NULL DEFAULT 0 COMMENT '商品数量',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `username`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of tb_order
-- ----------------------------
INSERT INTO `tb_order` VALUES (101, 1, 'Apple 苹果 iPhone 12 ', 699900, 1);
INSERT INTO `tb_order` VALUES (102, 2, '雅迪 yadea 新国标电动车', 209900, 1);
INSERT INTO `tb_order` VALUES (103, 3, '骆驼(CAMEL)休闲运动鞋女', 43900, 1);
INSERT INTO `tb_order` VALUES (104, 4, '小米10 双模5G 骁龙865', 359900, 1);
INSERT INTO `tb_order` VALUES (105, 5, 'OPPO Reno3 Pro 双模5G 视频双防抖', 299900, 1);
INSERT INTO `tb_order` VALUES (106, 6, '美的(Midea) 新能效 冷静星II ', 544900, 1);
INSERT INTO `tb_order` VALUES (107, 2, '西昊/SIHOO 人体工学电脑椅子', 79900, 1);
INSERT INTO `tb_order` VALUES (108, 3, '梵班(FAMDBANN)休闲男鞋', 31900, 1);

SET FOREIGN_KEY_CHECKS = 1;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

用户表

/*
 Navicat Premium Data Transfer

 Source Server         : local
 Source Server Type    : MySQL
 Source Server Version : 50622
 Source Host           : localhost:3306
 Source Schema         : heima

 Target Server Type    : MySQL
 Target Server Version : 50622
 File Encoding         : 65001

 Date: 01/04/2021 14:57:18
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '收件人',
  `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES (1, '柳岩', '湖南省衡阳市');
INSERT INTO `tb_user` VALUES (2, '文二狗', '陕西省西安市');
INSERT INTO `tb_user` VALUES (3, '华沉鱼', '湖北省十堰市');
INSERT INTO `tb_user` VALUES (4, '张必沉', '天津市');
INSERT INTO `tb_user` VALUES (5, '郑爽爽', '辽宁省沈阳市大东区');
INSERT INTO `tb_user` VALUES (6, '范兵兵', '山东省青岛市');

SET FOREIGN_KEY_CHECKS = 1;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

cloud-user表中初始数据如下:

image-20230609113947136

cloud-order表中初始数据如下:

image-20230609114012494

cloud-order表中持有cloud-user表中的id字段。

# 1.3 导入案例

链接:https://pan.baidu.com/s/10lAMkdL4tqJiXlD0mWlKsA?pwd=e8gi 提取码:e8gi

image-20240123200934332

目前这个Demo案例还没有实现服务远程调用,也就是在查订单表时,无法查到用户信息

img

# 1.4 实现服务拆分-远程调用:

要想实现微服务根据业务模块拆分,做到单一职责,这时一个业务模块对应一个数据库,当我订单需要查询是对应哪个用户,就需要跨库查询,那么该如何实现跨库查询呢?

image-20240123203843143

image-20240123203737622

先确立思路,在order模块中的pojo实体类封装了userId和user,可以通过userId到数据库中查询到该user的数据并封装,最后返回该订单信息

实现步骤: 第一步:创建RestTemplate并注入Spring容器

@Configuration
public class WebConfiguration {

    /**
     * 创建 RestTemplate 注入Spring容器
     * @return
     */
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }   
}
1
2
3
4
5
6
7
8
9
10
11
12

image-20240123202637125

第二步:在order的service层中,找到查询的方法,利用restTemplate的getForObject(url,User.class)发送请求,去获取User服务的信息,并封装返回!

参数一:传递地址,查询的请求,查询到该用户

image-20240123202445406

参数二:传递返回值对象的字节码

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);

        // 2.利用RestTemplate发送http请求,查询用户
        // 2.1.url路径
        String url = "http://localhost:8081/user/" + order.getUserId();

        // 2.2.发送http请求,实现远程调用
        User user = restTemplate.getForObject(url, User.class);

        // 3.封装user到Order
        order.setUser(user);

        // 4.返回
        return order;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

两步轻松搞定 重点在于restTemplate的getForObject方法

image-20240123202807244

# 1.4 启动微服务模块测试

image-20240123201425688

访问order服务看看效果 :

image-20240123201639455

到这里就说明我们订单微服务远程调用了用户微服务,nice!

总结

跨服务的远程调用其实就是发送一次http请求,首先是在Spring容器里面注入RestTemplate对象,然后在你发送请求的类里面自动装配这个RestTemplate对象,并且在方法里面调用这个RestTemplate对象,第一个参数是路径,第二个参数是你想拿到什么类型的数据

# 1.5 案例代码问题分析

我们在订单微服务中使用RestTemplate调用商品微服务的商品状态接口时(Restful API 接口)。在微服务分布式集群环境下会存在什么问题呢?怎么解决?

存在的问题:

  1. 在服务消费者中,我们把url地址硬编码到代码中,不方便后期维护。
  2. 服务提供者只有一个服务,即便服务提供者形成集群,服务消费者还需要自己实现负载均衡。
  3. 在服务消费者中,不清楚服务提供者的状态。
  4. 服务消费者调用服务提供者时候,如果出现故障能否及时发现不向用户抛出异常页面?
  5. RestTemplate这种请求调用方式是否还有优化空间?能不能类似于Dubbo那样玩?
  6. 这么多的微服务统一认证如何实现?
  7. 配置文件每次都修改好多个很麻烦!?

上述分析出的问题,其实就是微服务架构中必然面临的一些问题:

  1. 服务管理:自动注册与发现、状态监管
  2. 服务负载均衡
  3. 熔断
  4. 远程过程调用
  5. 网关拦截、路由转发
  6. 统一认证
  7. 集中式配置管理,配置信息实时自动更新

这些问题,Spring Cloud 体系都有解决方案,后续我们会逐个学习。

# 2. Eureka服务注册中心

今天我们学习第一个SpringCloud一代组件,Eureka,虽然现在他已经被Nacos替代了,但是我们还是需要对他有一定的了解,对于我们后面学习Nacos做下铺垫

常用的服务注册中心:Eureka、Nacos、Zookeeper、Consul

# 2.1 关于服务注册中心

​ 注意:服务注册中心本质上是为了解耦服务提供者和服务消费者。

服务消费者 --> 服务提供者

服务消费者 --> 服务注册中心 --> 服务提供者

​ 对于任何一个微服务,原则上都应存在或者支持多个提供者(比如商品微服务部署多个实例),这是由微服务的分布式属性决定的。

​ 更进一步,为了支持弹性扩、缩容特性,一个微服务的提供者的数量和分布往往是动态变化的,也是无法预先确定的。因此,原本在单体应用阶段常用的静态LB机制就不再适用了,需要引入额外的组件来管理微服务提供者的注册与发现,而这个组件就是服务注册中心。

# 2.1.1注册中心实现原理
image-20200921173149497

​ image-20201001204956251

​ 分布式微服务架构中,服务注册中心用于存储服务提供者地址信息、服务发布相关的属性信息,消费者通过主动查询和被动通知的方式获取服务提供者的地址信息,而不再需要通过硬编码方式得到提供者的地址信息。消费者只需要知道当前系统发布了那些服务,而不需要知道服务具体存在于什么位置,这就是透明化路由。

  1. 服务提供者启动

  2. 服务提供者将相关服务信息主动注册到注册中心

  3. 服务消费者获取服务注册信息:

    1. pull模式:服务消费者可以主动拉取可用的服务提供者清单
    2. push模式:服务消费者订阅服务(当服务提供者有变化时,注册中心也会主动推送更新后的服务清单给消费者
  4. 服务消费者直接调用服务提供者

    另外,注册中心也需要完成服务提供者的健康监控,当发现服务提供者失效时需要及时剔除;

# 2.1.2主流服务中心对比
  • Zookeeper

    ​ Dubbo + Zookeeper

    ​ Zookeeper它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。

    ​ 简单来说zookeeper本质 = 存储 + 监听通知。

    ​ Zookeeper 用来做服务注册中心,主要是因为它具有节点变更通知功能,只要客户端监听相关服务节点,服务节点的所有变更,都能及时的通知到监听客户端,这样作为调用方只要使用 Zookeeper 的客户端就能实现服务节点的订阅和变更通知功能了,非常方便。另外,Zookeeper 可用性也可以,因为只要半数以上的选举节点存活,整个集群就是可用的,最少节点数为3。

  • Eureka

    ​ 由Netflix开源,并被Pivatal集成到SpringCloud体系中,它是基于 RestfulAPI 风格开发的服务注册与发现组件。

  • Consul

    ​ Consul是由HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册服务软件, 采用Raft算法保证服务的一致性,且支持健康检查。

  • Nacos

    ​ Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。简单来说 Nacos 就是 注册中心 + 配置中心的组合,帮助我们解决微服务开发必会涉及到的服务注册 与发现,服务配置,服务管理等问题。Nacos 是 Spring Cloud Alibaba 核心组件之一,负责服务注册与发现,还有配置。

组件名 语言 CAP 对外暴露接口
Eureka Java AP(自我保护机制,保证可用) HTTP
Consul Go CP HTTP/DNS
Zookeeper Java CP 客户端
Nacos Java 支持AP/CP切换 HTTP

CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得。

P:分区容错性:分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用性的服务(一定的要满足的)

C:数据一致性:all nodes see the same data at the same time

A:高可用:Reads and writes always succeed

CAP不可能同时满足三个,要么是AP,要么是CP

# 2.2 服务注册中心组件 Eureka

​ 服务注册中心的一般原理、对比了主流的服务注册中心方案,目光聚焦Eureka。

  • Eureka 基础架构

    image-20200921173905946

    image-20201002105445180

  • Eureka 交互流程及原理

image-20200921174025653

image-20201002110004098

Eureka 包含两个组件:Eureka Server 和 Eureka Client,Eureka Client是一个Java客户端,用于简化与Eureka Server的交互;Eureka Server提供服务发现的能力,各个微服务启动时,会通过Eureka Client向Eureka Server 进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息;

  • 图中us-east-1c、us-east-1d,us-east-1e代表不同的区也就是不同的机房
  • 图中每一个Eureka Server都是一个集群。
  • 图中Application Service作为服务提供者向Eureka Server中注册服务,Eureka Server接受到注册事件会在集群和分区中进行数据同步,Application Client作为消费端(服务消费者)可以从Eureka Server中获取到服务注册信息,进行服务调用。
  • 微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒,默认Eureka Server 90S会将还没有续约的给剔除)以续约自己的信息
  • Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒)
  • 每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务注册列表的同步
  • Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者

​ Eureka通过心跳检测、健康检查和客户端缓存等机制,提高系统的灵活性、可伸缩性和高可用性。

# 2.3 搭建单例Eureka Server服务注册中心

搭建EurekaServer一共三步

# 1、创建新模块EurekaServer,引入eureka-server依赖
<dependencies>
    <!--eureka服务端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>
1
2
3
4
5
6
7

在这里插入图片描述

# 2. 在启动类添加@EnableEurekaServer 注解
@EnableEurekaServer // 自动装配 (// 声明本项目是一个Eureka服务)
@SpringBootApplication
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class,args);
    }
}
1
2
3
4
5
6
7

在这里插入图片描述

# 3. 在yml文件中配置Eureka server服务端口,服务名等信息
server:
  port: 10086 # 服务端口

spring:
  application:
    name: eurekaserver #  eureka的服务名称

# eureka本身也是微服务,也会将自己注册到微服务上
eureka:
  client:
    service-url:
      # eureka的地址信息,有多个用逗号隔开,将来可能有多个eureka,组成eureka集群
      defaultZone: http://127.0.0.1:10086/eureka
1
2
3
4
5
6
7
8
9
10
11
12
13

在这里插入图片描述

# 4. 启动服务访问测试

访问 http://127.0.0.1:10086,如果看到如下页面(Eureka注册中心后台),则表明Eureka Server发布成功

image-20240123212057781

image-20201002120535024

# 5. 将用户服务和订单服务注册到Eureka注册中心

在我们的项目结构中,eureka是服务端,导入的依赖就是server,那么有服务端必然有客户端,其他的微服务就是一个个的客户端,所以导入的依赖是client,注意这个区别。

image-20240123212513685

实现步骤,一共两步,和服务端相比只是少加了一个启动类的注释而已:

第一步:在user-service的pom.xml导入客户端的依赖

<!--eureka客户端依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
1
2
3
4
5

第二步:在user-service微服务的application.yml中配置user-service的地址,第二步和前面配置eureka写的内容区别只是在于修改服务名称

#这边可能有同学复制粘贴后发现爆红,原因是yml中不能出现两段spring,合并他们就好了
spring:
   application:
     name: userservice #  userservice的服务名称
# 注册服务到eureka上
eureka:
  client:
    service-url:
      # eureka的地址信息,这边写的是将信息注册到哪?注册到 eureka 上
      defaultZone: http://127.0.0.1:10086/eureka
1
2
3
4
5
6
7
8
9
10

订单服务的注册到Eureka的步骤和上面一致,只需要改个服务名称即可

实现结果: 这两个服务注册完成以后,重启这三个服务,访问eureka看看服务有没有注册成功

image-20240123213928597

# 2.4 搭建Eureka Server 高可用集群

​ 在互联网应用中,服务实例很少有单个的。

​ 如果EurekaServer只有一个实例,该实例挂掉,正好微服务消费者本地缓存列表中的服务实例也不可用,那么这个时候整个系统都受影响。

​ 在生产环境中,我们会配置Eureka Server集群实现高可用。Eureka Server集群之中的节点通过点对点(P2P)通信的方式共享服务注册表。我们开启两台 Eureka Server 以搭建集群。

这里介绍IDEA一个简单快捷的方法,右键点击 copy Configuration

​ image-20240123214318710

image-20240123215045119

在VM选项中,添加-Dserver.port=端口号即可,

image-20240123215251380

启动新的实例去Eureka注册中心查看是否注册成功。

image-20240123222131840

笔记

  • 从大观上看,可以想象成一共就是两个微服务,eureka作为服务端,其他微服务都作为客户端,将自己的信息注册到eureka服务端上。同时,eureka本身也是一个微服务,所以也需要将自己的信息登记到eureka上。

  • 实际操作的时候,eureka搭建完后,只需要重复上述步骤完成注册即可!

  • 能够理解并操作起来,恭喜你已经学会服务注册了!

# 2.5 eureka服务发现-服务拉取

第一步: 修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口,不再写死代码。

image-20240123221501677

第二步:给RestTemplate添加负载均衡注解:@LoadBalanced

image-20240123221751017

学会后简直不要太爽!访问了两次不同的id(http://localhost:8080/order/id),两个user-service都有查询,实现了负载均衡。

image-20240123222648747

image-20240123222704312

# 2.6 Eureka细节详解

# 2.6.1 Eureka元数据详解

Eureka的元数据有两种:标准元数据和自定义元数据。

​ 标准元数据 主机名、IP地址、端口号等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。

​ 自定义元数据 可以使用eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这些元数据可以在远程客户端中访问。

类似于

  instance:
    #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
    prefer-ip-address: true
    #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
    metadata-map:
      ip: 192.168.200.128
      port: 10000
      user: demo
      pwd: 123456
1
2
3
4
5
6
7
8
9
10
# 2.6.2 Eureka客户端详解

​ 服务提供者(也是Eureka客户端)要向Eureka Server注册服务,并完成服务续约等工作

服务注册详解(服务提供者)

​ 1)当我们导入了eureka-client依赖坐标,配置Eureka服务注册中心地址

​ 2)服务在启动时会向注册中心发起注册请求,携带服务元数据信息

​ 3)Eureka注册中心会把服务的信息保存在Map中。

服务续约详解(服务提供者)

​ 服务每隔30秒会向注册中心续约(心跳)一次(也称为报活),如果没有续约,租约在90秒后到期,然后服务会被失效。每隔30秒的续约操作我们称之为心跳检测

  • Eureka Client :30S续约一次,在Eureka Server更新自己的状态 (Client端进行配置)

  • Eureka Server: 90S还没有进行续约,将该微服务实例从服务注册表(Map)剔除 (Client端进行配置)

  • Eureka Client: 30S拉取服务最新的注册表并缓存到本地 (Client端进行配置)

​ 往往不需要我们调整这两个配置

#向Eureka服务中心集群注册服务
eureka:
  instance:
    # 租约续约间隔时间,默认30秒
    lease-renewal-interval-in-seconds: 30 
  	# 租约到期,服务时效时间,默认值90秒,服务超过90秒没有发生心跳,EurekaServer会将服务从列表移除
    lease-expiration-duration-in-seconds: 90 
1
2
3
4
5
6
7

​ 获取服务列表(服务注册表)详解(服务消费者)

​ 每隔30秒服务会从注册中心中拉取一份服务列表,这个时间可以通过配置修改。往往不需要我们调整

#向Eureka服务中心集群注册服务
eureka:
  client:
  	# 每隔多久拉取一次服务列表
    registry-fetch-interval-seconds: 30 
1
2
3
4
5
  1. 服务消费者启动时,从 EurekaServer服务列表获取只读备份,缓存到本地
  2. 每隔30秒,会重新获取并更新数据
  3. 每隔30秒的时间可以通过配置eureka.client.registry-fetch-interval-seconds修改
# 2.6.3 Eureka服务端详解

​ 服务下线:

  1. 当服务正常关闭操作时,会发送服务下线的REST请求给EurekaServer。
  2. 服务中心接受到请求后,将该服务置为下线状态

失效剔除:

​ Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进行检查,如果发现实例在在一定时间(此值由客户端设置的eureka.instance.lease-expiration-duration-in-seconds定义,默认值为90s)内没有收到心跳,则会注销此实例。

自我保护机制:

​ 自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。

​ 自我保护机制的工作机制是:如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:

  1. Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
  2. Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
  3. 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。

因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。

为什么会有自我保护机制?

​ 默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。

​ 服务中心页面会显示如下提示信息

image-20230609161513317

我们在单机测试的时候很容易满足心跳失败比例在 15 分钟之内低于 85%,这个时候就会触发 Eureka 的保护机制,一旦开启了保护机制(默认开启),则服务注册中心维护的服务实例就不是那么准确了,此时我们通过修改Eureka Server的配置文件来关闭保护机制,这样可以确保注册中心中不可用的实例被及时的剔除(不推荐)。

eureka:
  server:
    enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
1
2
3

经验:建议生产环境打开自我保护机制

今天就到这里了,下一节我们继续学习分享SpringCloud的相关组件,欢迎大家评论区留言讨论!

公众号封面

编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08
启动多个微服务项目
Ribbon 负载均衡

← 启动多个微服务项目 Ribbon 负载均衡→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式