【Spring Cloud】Eureka服务注册发现与LoadBalancer负载均衡

文章目录

  • 一、服务注册与发现原理
    • [1.1 认识服务注册与服务发现](#1.1 认识服务注册与服务发现)
    • [1.2 CAP理论](#1.2 CAP理论)
    • [1.3 主流注册中心](#1.3 主流注册中心)
  • 二、搭建注册中心Eureka
    • [2.1 搭建Eureka‑Server](#2.1 搭建Eureka‑Server)
    • [2.2 服务提供者注册(product‑service)](#2.2 服务提供者注册(product‑service))
    • [2.3 服务消费者实现服务发现(order‑service)](#2.3 服务消费者实现服务发现(order‑service))
  • 三、负载均衡LoadBalance详解
    • [3.1 什么是负载均衡](#3.1 什么是负载均衡)
    • [3.2 手写简易轮询负载均衡](#3.2 手写简易轮询负载均衡)
    • [3.3 Spring Cloud LoadBalancer](#3.3 Spring Cloud LoadBalancer)
    • [3.4 负载均衡策略](#3.4 负载均衡策略)
    • [3.5 LoadBalancer底层原理](#3.5 LoadBalancer底层原理)
  • 四、Linux服务器部署
  • 五、总结

一、服务注册与发现原理

1.1 认识服务注册与服务发现

类比:

  • 服务提供者(机构):把自己的号码上报给114(注册)
  • 114查号台:注册中心,保存所有机构号码
  • 服务消费者(普通用户):需要通话时,先查询114获取号码,再拨打电话(服务发现)

映射到微服务:

  1. 服务注册:服务提供者启动,向注册中心上报自己IP、端口、服务名称,定期发送心跳证明存活。
  2. 服务发现:服务消费者调用远程接口前,从注册中心拉取服务实例列表,拿到真实地址发起调用。

注册中心三大角色:

  1. 服务提供者Server:被其他微服务调用,对外提供接口。
  2. 服务消费者Client:调用其他微服务接口。
  3. 注册中心Registry:存储服务注册信息,监控服务心跳,实例下线自动更新列表。

1.2 CAP理论

CAP理论:分布式系统无法同时满足一致性( C )、可用性(A)、分区容错性( P ) 三者,最多同时满足两个。

  • 一致性C :所有节点同一时刻拥有完全一样的数据(强一致)
    • 强一致性:无论何时,主库和从库对外提供的服务是一致的
    • 弱一致性:随时间推移,最终数据一致,但是存在时间差
  • 可用性A:每个请求都可以得到响应,允许返回旧数据
  • 分区容错P :发生网络分区故障,系统仍然可以对外工作

分布式环境网络故障不可避免,P必须保证,只能在C和A二选一,分为CP架构、AP架构。

  • CP:优先保证数据强一致,网络故障时拒绝响应
  • AP:优先保证服务可用,允许返回旧数据(最终一致性)

1.3 主流注册中心

注册中心 CAP模式 说明
Zookeeper CP 集群中分为Leader和follower,是主从关系,追求强一致,Leader宕机需要重新选举,集群短暂不可用
Eureka AP 集群中所有节点都一样,优先可用性,允许读到旧实例,适合高并发业务,2.0官方停止维护
Nacos 可切换CP/AP,默认AP 阿里SpringCloudAlibaba组件,同时支持注册中心+配置中心

对于电商大促场景,宁可拿到旧实例信息,也不能整个服务不可用,优先选择AP模式。

二、搭建注册中心Eureka

Eureka分为两部分:

  1. Eureka Server:注册中心服务端,独立微服务
  2. Eureka Client:所有业务微服务,分为服务提供者、服务消费者

2.1 搭建Eureka‑Server

  1. 创建Maven子模块 eureka‑server
  2. 引入pom依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring‑cloud‑starter‑netflix‑eureka‑server</artifactId>
</dependency>
  1. 启动类,增加注解@EnableEurekaServer开启注册中心
java 复制代码
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}
  1. application.yml配置
yml 复制代码
server:
  port: 10010
spring:
  application:
    name: eureka‑server
eureka:
  instance:
    hostname: localhost
  client:
    fetch‑registry: false # 单点server,不需要拉取注册列表
    register‑with‑eureka: false # 不要把自己注册到注册中心
    service‑url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  1. 启动项目,访问页面:http://127.0.0.1:10010/,Eureka管理面板启动成功。

2.2 服务提供者注册(product‑service)

  1. pom引入eureka‑client依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring‑cloud‑starter‑netflix‑eureka‑client</artifactId>
</dependency>
  1. application.yml配置
yml 复制代码
spring:
  application:
    name: product‑service #服务名称,注册中心唯一标识
eureka:
  client:
    service‑url:
      defaultZone: http://127.0.0.1:10010/eureka
  1. 启动product‑service,刷新Eureka页面,看到product‑service实例注册成功。

2.3 服务消费者实现服务发现(order‑service)

order‑service作为消费者,调用product‑service接口。同样引入eureka‑client依赖,配置eureka地址。

基础写法:通过DiscoveryClient手动拉取服务实例

java 复制代码
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;

public OrderInfo selectOrderById(Integer orderId){
    OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
    // 根据服务名称获取全部实例
    List<ServiceInstance> instances = discoveryClient.getInstances("product‑service");
    // 获取第一个实例
    ServiceInstance instance = instances.get(0);
    String url = instance.getUri()+"/product/"+orderInfo.getProductId();
    ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
    orderInfo.setProductInfo(productInfo);
    return orderInfo;
}

上面代码永远取列表第一个实例,当product‑service启动多个实例,流量很有可能全部打在同一个节点,无法分担压力,这就需要负载均衡

三、负载均衡LoadBalance详解

3.1 什么是负载均衡

负载均衡LB,当服务多实例部署,按照一定算法,把请求分发到不同实例,分摊服务器压力,提升系统吞吐量与可用性。

分为两大类:

  1. 服务端负载均衡 :代表负载均衡器是Nginx。请求统一先到Nginx,Nginx做算法转发。服务清单保存在Nginx服务器。

  2. 客户端负载均衡 :代表负载均衡器Spring Cloud LoadBalancer(旧版Ribbon)。服务清单保存在调用方本地 ,客户端从注册中心拿到实例列表,本地执行算法选实例,再向服务器发起请求。

SpringCloud 2020.0.1版本移除Ribbon,官方使用Spring Cloud LoadBalancer作为客户端负载均衡组件。

3.2 手写简易轮询负载均衡

借助AtomicInteger计数器实现轮询,把请求轮流分发到不同实例:

java 复制代码
private List<ServiceInstance> instances;  

@PostConstruct  
public void init() {  
    // 从eureka中获取服务列表  
    instances =  discoveryClient.getInstances("product-service");  
}  
public OrderInfo getOrderById(Integer id) {  
    OrderInfo orderInfo = orderMapper.selectOrderById(id);  
    //String url = "http://127.0.0.1:9090/product/"+orderInfo.getProductId();  
  
    //获取服务实例的uri  
    //实现负载均衡,保证每一次调用按照计数器的顺序访问  
    int index = count.getAndIncrement() % instances.size();  
    String uri = instances.get(index).getUri().toString();  
    // 组装url  
    String url = uri+"/product/"+orderInfo.getProductId();  
    log.info("url: "+url);  
  
    ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);  
    orderInfo.setProductInfo(productInfo);  
    return orderInfo;  
}

手写可以实现效果,但生产不推荐,我们直接使用官方LoadBalancer组件。

3.3 Spring Cloud LoadBalancer

  1. 给RestTemplate Bean添加@LoadBalanced注解,开启负载均衡
java 复制代码
@Configuration
public class BeanConfig {
    @Bean
    @LoadBalanced //开启客户端负载均衡
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
  1. 远程调用直接写服务名称,不再写IP端口
java 复制代码
public OrderInfo selectOrderById(Integer orderId) {
    OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
    //直接写服务名 product‑service,不用写IP端口
    String url = "http://product‑service/product/"+ orderInfo.getProductId();
    ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
    orderInfo.setProductInfo(productInfo);
    return orderInfo;
}
  1. IDEA复制配置,启动多个product‑service实例(不同端口),多次访问接口,请求自动轮询分发到各个实例。

3.4 负载均衡策略

LoadBalancer内置两种策略:

  1. 轮询RoundRobin(默认):请求轮流分配给各个实例
  2. 随机Random:随机挑选实例执行请求

自定义随机策略示例:

java 复制代码
//该配置类不要加@Configuration
public class LoadBalancerConfig {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                                   LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(
                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
                name
        );
    }
}

在RestTemplate配置类增加注解,指定对哪个服务生效:

java 复制代码
@LoadBalancerClient(name = "product‑service",configuration = LoadBalancerConfig.class)
@Configuration
public class BeanConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

3.5 LoadBalancer底层原理

核心拦截器LoadBalancerInterceptor拦截RestTemplate的http请求:

  1. 解析url,取出host,也就是服务名product‑service
  2. 向注册中心获取该服务全部实例列表
  3. 使用负载均衡策略选择一个服务实例
  4. 将url中的服务名替换为真实IP端口,发起http请求

四、Linux服务器部署

  1. 使用maven对eureka‑server、order‑service、product‑service执行package打包,生成jar包。
  2. 将jar包上传Linux服务器,使用nohup后台启动服务:
bash 复制代码
#启动注册中心
nohup java -jar eureka‑server.jar >logs/eureka.log &

#订单服务
nohup java -jar order‑service.jar >logs/order.log &

#商品服务多个实例,指定不同端口
nohup java -jar product‑service.jar >logs/product‑9090.log &
nohup java -jar product‑service.jar --server.port=9091 >logs/product‑9091.log &
nohup java -jar product‑service.jar --server.port=9092 >logs/product‑9092.log &
  1. 在云服务器防火墙开放对应端口(10010、8080、9090‑9092)。
  2. 访问Eureka页面,确认所有服务实例成功注册,调用业务接口验证远程调用+负载均衡效果。

五、总结

  1. 注册中心解决硬编码痛点:服务启动注册,消费者查询服务列表,实现调用方与服务地址解耦。Eureka属于AP架构,优先可用性,适合高并发业务,官方2.0停止维护,新项目优先Nacos。
  2. CAP理论是注册中心选型的理论基础:网络分区不可避免,只能选择CP或者AP。
  3. 客户端负载均衡LoadBalancer:@LoadBalanced注解开启,直接使用服务名称调用,内置轮询、随机策略,支持自定义策略。底层通过拦截器完成服务名解析、实例选择。
  4. 流程完整链路:服务提供者注册到Eureka → 消费者拉取实例列表 → LoadBalancer选择实例 → 远程HTTP调用。
相关推荐
谢亮_vipxieliang2 小时前
ValidX vs Google Guava Preconditions:验证 vs 断言
java·spring boot·后端·spring cloud·hibernate·guava
重生之我来学Python17 小时前
Docker套装的简介、安装、超级详细教程
linux·docker·容器·eureka·github
夏天拐跑了西瓜20 小时前
Spring Cloud 微服务实战(八):Hystrix熔断降级——从服务雪崩到断路器模式
spring cloud·hystrix·微服务
2601_9667990420 小时前
酷嗨米J300:硬件级多通道分发采集设备,为矩阵直播打造独立信号通道
服务器·网络·负载均衡
起名真的太难了21 小时前
Docker 项目部署
docker·容器·eureka
码力斜杠哥1 天前
Docker Compose快速入门笔记
笔记·docker·eureka
一技安身1 天前
【Docker】Docker 和 docker‑compose 的核心区别 + 为什么要单独装 compose
docker·容器·eureka
重生之我是Java开发战士2 天前
【Spring Cloud】认识微服务与工程搭建
spring·spring cloud·微服务
隔窗听雨眠2 天前
无辅助损失函数的负载均衡:DeepSeek MoE架构的核心突破
运维·架构·负载均衡