文章目录
- 一、服务注册与发现原理
-
- [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获取号码,再拨打电话(服务发现)
映射到微服务:
- 服务注册:服务提供者启动,向注册中心上报自己IP、端口、服务名称,定期发送心跳证明存活。
- 服务发现:服务消费者调用远程接口前,从注册中心拉取服务实例列表,拿到真实地址发起调用。
注册中心三大角色:
- 服务提供者Server:被其他微服务调用,对外提供接口。
- 服务消费者Client:调用其他微服务接口。
- 注册中心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分为两部分:
- Eureka Server:注册中心服务端,独立微服务
- Eureka Client:所有业务微服务,分为服务提供者、服务消费者
2.1 搭建Eureka‑Server
- 创建Maven子模块
eureka‑server - 引入pom依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring‑cloud‑starter‑netflix‑eureka‑server</artifactId>
</dependency>
- 启动类,增加注解
@EnableEurekaServer开启注册中心
java
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
- 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/
- 启动项目,访问页面:
http://127.0.0.1:10010/,Eureka管理面板启动成功。
2.2 服务提供者注册(product‑service)
- pom引入eureka‑client依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring‑cloud‑starter‑netflix‑eureka‑client</artifactId>
</dependency>
- application.yml配置
yml
spring:
application:
name: product‑service #服务名称,注册中心唯一标识
eureka:
client:
service‑url:
defaultZone: http://127.0.0.1:10010/eureka
- 启动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,当服务多实例部署,按照一定算法,把请求分发到不同实例,分摊服务器压力,提升系统吞吐量与可用性。
分为两大类:
-
服务端负载均衡 :代表负载均衡器是Nginx。请求统一先到Nginx,Nginx做算法转发。服务清单保存在Nginx服务器。

-
客户端负载均衡 :代表负载均衡器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
- 给RestTemplate Bean添加
@LoadBalanced注解,开启负载均衡
java
@Configuration
public class BeanConfig {
@Bean
@LoadBalanced //开启客户端负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 远程调用直接写服务名称,不再写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;
}
- IDEA复制配置,启动多个product‑service实例(不同端口),多次访问接口,请求自动轮询分发到各个实例。
3.4 负载均衡策略
LoadBalancer内置两种策略:
- 轮询RoundRobin(默认):请求轮流分配给各个实例
- 随机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请求:
- 解析url,取出host,也就是服务名
product‑service - 向注册中心获取该服务全部实例列表
- 使用负载均衡策略选择一个服务实例
- 将url中的服务名替换为真实IP端口,发起http请求
四、Linux服务器部署
- 使用maven对eureka‑server、order‑service、product‑service执行package打包,生成jar包。
- 将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 &
- 在云服务器防火墙开放对应端口(10010、8080、9090‑9092)。
- 访问Eureka页面,确认所有服务实例成功注册,调用业务接口验证远程调用+负载均衡效果。
五、总结
- 注册中心解决硬编码痛点:服务启动注册,消费者查询服务列表,实现调用方与服务地址解耦。Eureka属于AP架构,优先可用性,适合高并发业务,官方2.0停止维护,新项目优先Nacos。
- CAP理论是注册中心选型的理论基础:网络分区不可避免,只能选择CP或者AP。
- 客户端负载均衡LoadBalancer:@LoadBalanced注解开启,直接使用服务名称调用,内置轮询、随机策略,支持自定义策略。底层通过拦截器完成服务名解析、实例选择。
- 流程完整链路:服务提供者注册到Eureka → 消费者拉取实例列表 → LoadBalancer选择实例 → 远程HTTP调用。