负载均衡 Ribbon 与 Fegin 远程调用原理

文章目录

  • 一、什么是负载均衡
  • [二、Ribbon 负载均衡](#二、Ribbon 负载均衡)
    • [2.1 Ribbon 使用](#2.1 Ribbon 使用)
    • [2.2 Ribbon 实现原理 (★)](#2.2 Ribbon 实现原理 (★))
    • [2.3 Ribbon 负载均衡算法](#2.3 Ribbon 负载均衡算法)
  • [三、Feign 远程调用](#三、Feign 远程调用)
    • [3.1 Feign 简述](#3.1 Feign 简述)
    • [3.2 Feign 的集成](#3.2 Feign 的集成)
    • [3.3 Feign 实现原理 (★)](#3.3 Feign 实现原理 (★))

一、什么是负载均衡

《服务治理:Nacos 注册中心》 末尾提到了负载均衡,那什么是负载均衡呢?

负载均衡就是将负载(⼯作任务,访问请求)进⾏分摊到多个操作单元(服务器,组件)上进行执行。

根据负载均衡发⽣位置的不同,⼀般分为: 服务端负载均衡客户端负载均衡

  • 服务端负载均衡指的是发生在服务提供者一方,比如常见的 Nginx 负载均衡。
  • 客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求。

注:在微服务调⽤关系中⼀般会选择客户端负载均衡,也就是在服务调用的一方来决定服务由哪个提供者执行。


二、Ribbon 负载均衡

2.1 Ribbon 使用

Ribbon 是 Spring Cloud 的⼀个组件, 它可以让我们使用一个注解就能轻松的搞定负载均衡。

1、在 RestTemplate 的生成方法上添加 @LoadBalanced 注解

java 复制代码
@Bean
@LoadBalanced // 表示继承Ribbon进行负载均衡
public RestTemplate restTemplate() {
    return new RestTemplate();
}

2、修改服务调用的方法

java 复制代码
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private RestTemplate restTemplate;

    @Override
    public Order createOrder(Long productId, Long userId) {
        log.info("接收到{}号商品的下单请求,接下来调⽤商品微服务查询此商品信息", productId);
        // 远程调⽤商品微服务,查询商品信息
        String url = "http://product-service/product/" + productId;
        log.info("服务器地址: {}", url);
        
        // 远程调⽤商品微服务,查询商品信息
        Product product = restTemplate.getForObject(url, Product.class);
        
        log.info("查询到{}号商品的信息,内容是:{}", productId, JSON.toJSONString(product));
        // 创建订单并保存
        Order order = new Order();
        order.setUid(userId);
        order.setUsername("安秀岩");
        order.setPid(productId);
        order.setName(product.getName());
        order.setPrice(product.getPrice());
        order.setNumber(1);
        orderDao.save(order);
        log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
        return order;
    }
}

对比以下两种方式,差异显著:

java 复制代码
 //⾃定义规则实现随机挑选服务
 List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
 int index = new Random().nextInt(instances.size());
 ServiceInstance instance = instances.get(index);
 String url = instance.getHost() + ":" + instance.getPort();
 log.info("从nacos中获取到的微服务地址为:" + url);


简化成了以下一行


// Ribbon 直接使用需要远程调用的服务名称即可
String url = "http://product-service/";

那 Ribbon 底层原理又是什么呢?为什么可以使用服务名称就能远程调用该服务呢?


2.2 Ribbon 实现原理 (★)

现假设:两个商品服务做集群,向Nacos注册的IP分别是192.168.10.111:8081192.168.10.112:8081

step1 :当使用 RestTemplate 远程访问时,http://product-service/product/1 首先会将服务名称截取出来 product-service
step2 :并在本地缓存列表中获取到服务的 IP 集合,即{``{192.168.10.111:8081}, {192.168.10.112:8081}}
step3 :根据内部配置的 负载均衡算法,从集合中选取其中一个IP地址,如:192.168.10.112:8081
step4 :将原来的 url 替换成 http://192.168.10.112:8081/product/1,最终通过 RestTemplate 发起请求。


2.3 Ribbon 负载均衡算法

Ribbon 内置了多种负载均衡策略,内部负载均衡的顶级接口为 com.netflix.loadbalancer.IRule,具体的负载策略如下图所示:

可以通过修改配置来调整 Ribbon 的负载均衡策略,如在 order-server 项目的 application.yml 中增加如下配置:

yml 复制代码
product-service: # 调⽤的提供者的名称
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

三、Feign 远程调用

3.1 Feign 简述

为什么要使用 feign 呢?原来的调用的方式 String url = "http://product-service/product/" + productId; 是固定的字符串做拼接不够灵活,而且还存在 productId 参数校验问题等,因此 Feign 可解决这个问题。

Feign 是 Spring Cloud 提供的⼀个声明式的伪 Http 客户端,它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。Nacos 很好的兼容了 Feign,Feign 默认集成了 Ribbon,所以在 Nacos 下使用 Fegin 默认就实现了负载均衡的效果。


3.2 Feign 的集成

1、在shop-order-server项⽬的pom文件加入Fegin的依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2、在启动类上添加 Fegin 的扫描注解 @EnableFeignClients,注意扫描路径(默认扫描当前包及其子包)

java 复制代码
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // 会扫描当包及其子包下贴有@FeignClient注解的接口
public class OrderServer {
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication .class, args);
    }
}

3、在 shop-order-server 项目中新增接口 ProductFeignApi 和该接口的容错类 ProductFeignFallback

java 复制代码
@FeignClient(name = "product-service", fallback = ProductFeignFallback.class)
// 远程调用服务名称;fallback 指定返回兜底数据的类的字节码,即服务挂起时的降级类方法
public interface ProductFeignApi {
    @RequestMapping("/product/{pid}")
    // 路径要与 ProductController 的接口保持一致
    Product findByPid(@PathVariable("pid") Long pid);
}
java 复制代码
@Component // 该类的作用是返回兜底数据以防 "服务器雪崩"
public class ProductFeignFallback implements ProductFeignApi {
    @Override
    public Product findByPid(Long pid) {
        System.out.println("返回兜底数据");
        return new Product();
    }
}

4、修改服务调用的方法

java 复制代码
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private ProductFeignApi productFeignApi;

    @Override
    public Order createOrderFeign(Long productId, Long userId) {
    	// feign 调用
        Product product = productFeignApi.findByPid(productId);
        
        log.info("查询到{}号商品的信息,内容是:{}", productId, JSON.toJSONString(product));
        // 创建订单并保存
        Order order = new Order();
        order.setUid(userId);
        order.setUsername("叩丁狼教育");
        order.setPid(productId);
        order.setName(product.getName());
        order.setPrice(product.getPrice());
        order.setNumber(1);
        orderDao.save(order);
        log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
        return order;
    }
}

3.3 Feign 实现原理 (★)

Feign 实现的原理是基于动态代理和反射技术,并且内部还是使用 RestTemplate 实现的,具体详细流程如下:


文章参考:Java微服务商城高并发秒杀项目实战|Spring Cloud Alibaba真实项目实战+商城双11秒杀+高并发+消息+支付+分布式事物Seata

相关推荐
yunhuibin13 分钟前
Linux 7.0 调度器:C 语言面向对象(OOPC)的极致实践
linux·运维·c语言
尘世壹俗人20 分钟前
linux编译安装git
linux·运维·git
爱学习的小囧22 分钟前
ESXi/vCenter 批量开关虚拟机完整教程 | PowerCLI 一键 + 原生脚本循环,新手也能落地
运维·网络·数据库·esxi
xxjj998a30 分钟前
如何安装linux版本的node.js
linux·运维·node.js
qqty12171 小时前
springcloud springboot nacos版本对应
spring boot·spring·spring cloud
唐墨1231 小时前
linux kernel之设备树
linux·运维·服务器
huanmieyaoseng10031 小时前
centos 配置国内yum源2026新
linux·运维·centos
MinterFusion2 小时前
如何在VirtalBox中安装deepin操作系统虚拟机(v0.1.0)(下)
运维·虚拟机·deepin·virtualbox·国产操作系统·明德融创·虚拟技术
一个有温度的技术博主2 小时前
微服务技术选型:Dubbo、Spring Cloud与Spring Cloud Alibaba深度对比
spring cloud·微服务·dubbo
草莓熊Lotso2 小时前
Linux 线程同步与互斥(一):彻底搞懂线程互斥原理、互斥量底层实现与 RAII 封装
linux·运维·服务器·开发语言·数据库·c++