负载均衡 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

相关推荐
吉小雨24 分钟前
Playwright 自动化验证码教程
运维·数据库·python·自动化
Xiao Tong33325 分钟前
linux第二课(docker的安装使用)
linux·运维·服务器
Shenqi Lotus36 分钟前
??Nginx实现会话保持_Nginx会话保持与Redis的结合_Nginx实现四层负载均衡
运维·redis·nginx·负载均衡
烟雨国度40 分钟前
Spring Boot 执行流程已经 负载均衡执行流程图
spring boot·负载均衡·流程图
JeasonTly1 小时前
WebRTC服务器搭建
运维·服务器·webrtc
明月心9521 小时前
docker+docker-compose+gitlab
java·spring cloud·eureka
哲讯智能科技1 小时前
SAP B1 Web Client & MS Teams App集成连载二:安装Install/升级Upgrade/卸载Uninstall
大数据·运维
xianjie03182 小时前
几种修改docker默认存储位置的方法
spring cloud·docker·eureka
Shenqi Lotus2 小时前
??Ansible——ad-hoc
运维·架构
cyt涛2 小时前
微服务保护之熔断降级
java·运维·微服务·熔断·降级·雪崩·保护