【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调用。
相关推荐
Rain的Java大神之路6 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
需要8266 天前
MySQL 索引 B+ 树与“查询为什么变慢了
java·spring boot·spring·spring cloud·tomcat
需要8266 天前
Arthas 一个命令排查线上问题
java·jvm·spring·spring cloud
苏渡苇7 天前
Spring Insight 里如何把 Span 画成瀑布时间线
后端·spring·spring cloud·springboot·监控·apm
叶总没有会7 天前
Nacos—注册中心
spring cloud·微服务
苏渡苇8 天前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring·spring cloud·系统监控·apm
叶总没有会8 天前
微服务--分布式基础
java·spring·spring cloud·微服务
孙启超9 天前
【AI开发之Rust】第 7 课:错误处理 —— panic、Result 与 `?`
人工智能·分布式·后端·爬虫·spring cloud·架构·rust
行百里er10 天前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring cloud·监控
砍材农夫10 天前
spring|spring web|拦截器、过滤器、aop
java·spring boot·spring·spring cloud