负载均衡Ribbon和Feign的使用与区别

news2024/12/27 21:11:53

Ribbon 的介绍

Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。主要功能是提供客户端的软件负载均衡和服务调用。Ribbon 客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer 后面的所有的及其,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用 Ribbon 实现自定义的负载均衡算法

Feign 的介绍

Feign 和 Ribbon 是 Spring Cloud 的 Netflix 中提供的两个实现软负载均衡的组件,Ribbon 和 Feign 都是用于调用其他服务的,方式不同,Feign 则是在 Ribbon 的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建 Http 请求,不过要注意的是抽象方法的注解,方法名要和提供服务的方法对应上。简单点说,Feign 是对 Ribbon 的封装,而且 Feign 和 Ribbon 的作用位置不同。

负载均衡

Ribbon 和 Feign 都是负载均衡技术,那么什么是负载均衡呢?简单点说负载均衡就是将用户的请求平摊的分配到多个服务上,从而达到系统的高可用。

Nginx 服务端负载均衡和 Ribbon 本地负载均衡的区别

Nignx 是服务器负载均衡,客户端所有的请求都会交给 Nginx ,然后由 Nginx 实现转发请求,即负载均衡是由服务端实现的。

Ribbon 本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到 VM 本地,从而在本地实现 RPC 远程服务调用技术。

Ribbon 和 Feign 的区别

  • 启动类使用的注解不同,Ribbon 用的是 @RibbonClient,Feign 用的是 @EnableFeignClients 。
  • 服务的指定位置不同,Ribbon 是在 @RibbonClient 注解上声明,Feign 则是在定义抽象方法的接口中(service 层的接口上)使用 @FeignClient 声明。
  • 调用方式不同,Ribbon 需要自己构建 http 请求,模拟 http 请求然后使用 RestTemplate 发送给其他服务,步骤相当繁琐,Feign 是直接通过接口方式调用。

Ribbon 的使用

项目是建立在springcloud技术篇一 Nacos 的基础上进行的。Ribbon 只是一个客户端的负载均衡器工具,实现起来非常简单,我们只需要注入 RestTemplate 的 Bean 上加上 @LoadBalanced 就可以了,内容如下:

@Configuration
public class WebConfig {
	public RestTemplate restTemplate() {
		@LoadBalanced//负载均衡,默认使用轮询规则
		@Bean
		return new RestTemplate();
	}
}

补充:在早期版本中,spring-cloud-starter-netflix-eureka-client 依赖已经引入了 Ribbon,则我们可以直接使用,但是因为自从 SpringCloud2020.0.1.0 版本是已经不需要 netflix 了,所以如果我们使用的是最新版本的 springcloud,则需要手动在服务消费方导入 spring-cloud-starter-loadbalancer 依赖支持

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-loadbalancer</artifactId>
	<version>3.1.1</version>
</dependency>

启动一个消费方,多个服务放进行测试

  1. 先去修改 springcloud-alibaba-microservice-consumer 工程中的 UserController,然后启动即可
@RestController
@RequestMapping("user-consumer")
public class UserController {
	@Autowired
	private DiscoveryClient discoveryClient;//服务发现
	
	@Autowired
	private RestTemplate restTemplate;//用于发送网络请求
	
	// 服务方应该调用生产方的服务
	@RequestMapping("getUsers")
	public JsonResult getUsers() {
		// 由于在 WebConfig 中设置了轮询规则,这里通过服务的名称来发送网络请求
		String url = "http://micro-service-provider/user-provider/findAll";
		JsonResult jsonResult = restTemplate.getForObject(url,JsonResult.class);
		System.out.println(jsonResult);
		return jsonResult;
	}
}
  1. 修改 springcloud-alibaba-microservice-provider 工程中的 UserController
@RestController
@RequestMapping("user-provider")
public class UserController {
 
	@RequestMapping("findAll")
	public JsonResult findAll() {
		// 使用并联启动的方式,启动多个服务提供方进行测试
		// 先输出 7070,然后修改 application.yml 配置文件,端口号设置为 7070 启动
		// System.out.println("7070")
		// 在输出 7071,然后修改 application.yml 配置文件,端口号设置为 7071 启动
		// System.out.println("7071")
		// 在输出 7072,然后修改 application.yml 配置文件,端口号设置为 7072 启动
		// System.out.println("7072")
		
		// 模拟数据库数据
		List<User> users = Arrays.asList(
		new User(1001, "张三", "123"),
		new User(1002, "李四", "456"),
		new User(1003, "王五", "789"),
		);
		JsonResult jsonResult = JsonResult.ok();
		jsonResult.setData(users);
		
		return jsonResult;
	}
}
  1. 设置 springcloud-alibaba-microservice-provider 工程多次启动

在这里插入图片描述
在这里插入图片描述

修改端口号,启动多个 provider,然后启动 consumer,访问浏览器进行测试

负载均衡的策略

Ribbon 提供了一个很重要的接口叫做 IRule,其中定义了很多的负载均衡策略,默认的是轮询的方式,一下是 Ribbon 的负载均衡策略

在这里插入图片描述

改变 Ribbon 的均衡策略(随机方式):

@Configuration
public class WebConfig {
	@LoadBalanced//负载均衡
	@Bean
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}
	
	// 创建对象实现改变 Ribbon 的负载均衡策略,随机规则
	@Bean
	public IRule getRule() {
		return new RandomRule();
	}
}

自定义方式的均衡策略:

自定义的负载均衡策略需要继承 AbstractLoadBalancerRule 这个类,然后重写 choose 方法,然后将其注入到容器中。

创建 ServerInfo 类

public class ServerInfo {
	private Server server;
	private int num;
	
	public ServerInfo() {
    }
 
    public ServerInfo(Server server, int num) {
        this.server = server;
        this.num = num;
    }
 
    public Server getServer() {
        return server;
    }
 
    public void setServer(Server server) {
        this.server = server;
    }
 
    public int getNum() {
        return num;
    }
 
    public void setNum(int num) {
        this.num = num;
    }
}

创建 CustomizeRule 类:

// 自定义规则,每个服务最多访问 5 次,然后再继续访问下一个
public class CustomizeRule extends AbstractLoadBalancerRule {
	private int limit = 3;
	
	// map 的 key 是服务的名字,value 是该服务调用的次数
	private Map<String, ServerInfo> map = new ConcurrentHashMap<>();
	
	@Override
	public void initWithNiwsConfig(IClientConfig iClientConfig){}
	
	// 返回值的意思是,当该方法返回什么的时候,那么 Ribbon 或者 Feign 就调用谁。
	@Override
	public Server choose(Object key) {
		Server finalServer = null;
		ILoadBalancer loadBalancer = getLoadBalancer();
		// 获取所有的服务
		List<Server> servers = loadBalancer.getAllServers();
		// 获取所有的可用的服务
		List<Server> reachableServers = loadBalancer.getReachableServers();
		int allServiceSize = servers.size(); // 获取所有服务的长度
		int upCount = reachableServers.size(); // 获取所有的可用的服务的长度
		if(0 == allServicesSize || 0 == upCount) {
			return null;
		}
		
		for(int i = 0; i < allServiceSize; i++) {
			Server server = servers.get(i);//获取当前遍历的 server
			String instanceId = server.getMetaInfo().getInstanceId();
			String providerName = instanceId.split("@@")[1];//获取服务名
			ServerInfo serverInfo = map.get(providerName);//获取对应服务
			// 首次调用
			if(null == serverInfo) {
				serverInfo = new ServerInfo(server, 1);
				map.put(providerName, serverInfo);
				finalServer = server;
				break;
			} else {
				// 不为空,表示之前肯定调用过
				// 当前遍历的 server 与正在调用的 server 是同一个 server
				if(serverInfo.getServer().getId().equals(server.getId())) {
					// 如果没有满 3 次,接着走该服务。
					// 如果满了 3 次,接着下个
					int num = serverInfo.getNum();//获取已经调用的次数
					if(num >= limit) {
						// 超出了 3 次
						// 超出次数,要走下一个,需要判断是否有下一个,需要判断是否有下一个,如果没有下一个,就回到第一个
						if(i == (allServiceSize - 1)) {
							Server firstServer = servers.get(0);//如果为最后一个就拿第一个
							ServerInfo firstServerInfo = new ServerInfo(firstServer, 1);
							map.put(providerName, firstServerInfo);
							finalServer = firstServer;
						} else {
							Server nextServer = servers.get(i + 1);
							map.put(providerName, nextServerInfo);
							finalServer = nextServer;
						}
						break;
					} else {
						serverInfo.setNum(++num);
						finalServer = server;
						break;
					}
				}
			}
		}
		return finalServer;
	}
}

修改 WebConfig ,添加配置

@Configuration
public class WebConfig {
	@Bean
	@LoadBalanced//负载均衡,默认规则:轮询
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}
	// 自定义均衡负载服务器
	@Bean
	public IRule getRule() {
		return new CustomizeRule();
	}
}

Feign 的使用

在 springcloudalibaba-micro-service-consumer 的 pom.xml 中导入依赖

<!-- Feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

在启动类上加入 @EnableFeignClients 的注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
	public static void main(String[] args) {
		SpringApplication.run(ConsumerApplication.class,args);
	}
}

创建UserService

@Service
@FeignClient("micro-service-provider")
public interface UserService {
	@RequestMapping("/user-provider/findAll")
	public JsonResult findAll();
}

创建 FeignUserController

@RestController
@RequestMapping("feign")
public class FeignUserController {
	@Autowired
	private UserService userService;
	
	@RequestMapping("findAll)
	public JsonResult findAll() {
		return userService.findAll();
	}
}

启动多个 provider,然后启动 consumer,访问

http://localhost:8080/feign/findAll 进行测试

在 Feign 的基础上的服务之间的传参

在 springcloudalibaba-micro-service-provide 工程中的 UserController 添加 CRUD 方法

@RestController
@RequestMapping("user-provider")
public class UserController {
 
    @RequestMapping("findAll")
    public JsonResult findAll(){
 
        //使用并联启动的方式,启动多个服务提供方进行测试
        //先输出7070,然后修改application.yml配置文件,端口设置为7070启动
        //System.out.println("7070");
        //再输出7071,然后修改application.yml配置文件,端口设置为7071启动
        System.out.println("7071");
        //再输出7072,然后修改application.yml配置文件,端口设置为7072启动
        //System.out.println("7072");
 
        List<User> users = Arrays.asList(
                new User(1001, "张三", "123"),
                new User(1002, "李四", "456"),
                new User(1003, "王五", "789"));
 
        JsonResult jsonResult = JsonResult.ok();
        jsonResult.setData(users);
 
        return jsonResult;
    }
 
    //模拟数据库操作
    //查询单个
    @GetMapping("findById")
    public JsonResult findById(@RequestParam("id") Integer id){
 
        User user = new User(id, "jack", "123");
 
        JsonResult jsonResult = JsonResult.ok();
        jsonResult.setData(user);
 
        return jsonResult;
    }
 
    //删除单个-restful风格的开发
    @DeleteMapping("deleteById/{id}")
    public JsonResult deleteById(@PathVariable("id") Integer id){
        System.out.println("deleteById:"+id);
        return JsonResult.ok();
    }
 
    //添加
    @PostMapping("addUser")
    public JsonResult addUser(@RequestBody User user){
        System.out.println("addUser:"+user);
        return JsonResult.ok();
    }
 
    //修改  如果参数不一致 RequestParam(value = "")
    @PutMapping("updateUser")
    public JsonResult updateUser(@RequestParam Integer id,@RequestParam String username,@RequestParam String password){
        System.out.println("updateUser:"+id+"--"+username+"--"+password);
        return JsonResult.ok();
    }
}

在 springcloudalibaba-micro-service-consumer 工程中的 UserService 添加对应方法

@Service
@FeignClient("micro-service-provider")
public interface UserService {
 
    @RequestMapping("/user-provider/findAll")
    public JsonResult findAll();
 
    //模拟数据库操作
    //查询单个
    @GetMapping("/user-provider/findById")
    public JsonResult findById(@RequestParam("id") Integer id);
 
    //删除单个
    @DeleteMapping("/user-provider/deleteById/{id}")
    public JsonResult deleteById(@PathVariable("id") Integer id);
 
    //添加
    @PostMapping("/user-provider/addUser")
    public JsonResult addUser(@RequestBody User user);
 
    //修改
    @PutMapping("/user-provider/updateUser")
    public JsonResult updateUser(@RequestParam Integer id,@RequestParam String username,@RequestParam String password);
 
}

在 springcloudalibaba-micro-service-consumer 工程中的 FeignUserController 添加对应方法

@RestController
@RequestMapping("feign")
public class FeignUserController {
 
    @Autowired
    private UserService userService;
 
    @RequestMapping("findAll")
    public JsonResult findAll(){
        return userService.findAll();
    }
 
    //模拟数据库操作
    //查询单个
    @GetMapping("findById")
    public JsonResult findById(@RequestParam("id") Integer id){
        return userService.findById(id);
    }
 
    //删除单个
    @DeleteMapping("deleteById/{id}")
    public JsonResult deleteById(@PathVariable("id") Integer id){
        return userService.deleteById(id);
    }
 
    //添加  使用requestbody注解前端需要传送JSON数据
    @PostMapping("addUser")
    public JsonResult addUser(User user){
        return userService.addUser(user);
    }
 
    //修改
    @PutMapping("updateUser")
    public JsonResult updateUser(@RequestParam Integer id,@RequestParam String username,@RequestParam String password){
        return userService.updateUser(id,username,password);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1234849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【python】Python生成GIF动图,多张图片转动态图,pillow

pip install pillow 示例代码&#xff1a; from PIL import Image, ImageSequence# 图片文件名列表 image_files [car.png, detected_map.png, base64_image_out.png]# 打开图片 images [Image.open(filename) for filename in image_files]# 设置输出 GIF 文件名 output_g…

深入理解JSON及其在Java中的应用

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a;每天一个知识点 ✨特色专栏&#xff1a…

【docker】虚拟化和docker容器概念

基础了解 IAAS&#xff1a; 基础设施服务&#xff0c;&#xff08;只提供基础设施&#xff0c;没有系统&#xff09; **SAAS&#xff1a; ** 软件即服务&#xff0c;&#xff08;提供基础设施和系统&#xff09; PAAS&#xff1a; 平台即服务&#xff0c;&#xff08;提供基…

哪个才是最适合你的 Web UI 自动化测试框架

最近&#xff0c;项目上出于系统性稳定性、减少测试工作量考虑&#xff0c;打算在 Web 前端引入 BDD。由于上一个项目写了一定的 Cucumber 代码&#xff08;BDD 测试框架之一&#xff09;&#xff0c;这个框架选型的责任便落到了我的肩膀上了。 在我们进行框架选型的时候&#…

前端uniapp生成海报绘制canvas画布并且保存到相册【实战/带源码/最新】

目录 插件市场效果如下图注意使用my-share.vue插件文件如下图片hch-posterutilsindex.js draw-demo.vuehch-poster.vue 最后 插件市场 插件市场 效果如下图 注意 主要&#xff1a;使用my-share.vue和绘制canvas的hch-poster.vue这两个使用 使用my-share.vue <template&…

使用kafka_exporter监控Kafka

prometheus 监控 kafka 常见的有两种开源方案,一种是传统的部署 exporter 的方式,一种是通过 jmx 配置监控, 项目地址: kafka_exporter:https://github.com/danielqsj/kafka_exporterjmx_exporter:https://github.com/prometheus/jmx_exporter本文将采用kafka_exporter方…

win11,无法修改文件的只读属性,解决办法

在尝试更改文件或文件夹的权限时&#xff0c;您可能经常会遇到错误 - 无法枚举容器中的对象访问被拒绝。 虽然作为管理员&#xff0c;您可以更改访问权限&#xff0c;但有时即使是管理员也可能会遇到相同的错误消息。 这是一个常见错误&#xff0c;通常由不同论坛上的用户提出…

Web前端—移动Web第三天(移动Web基础、rem、less、综合案例—极速问诊)

版本说明 当前版本号[20231120]。 版本修改说明20231120初版 目录 文章目录 版本说明目录移动 Web 第三天01-移动 Web 基础谷歌模拟器屏幕分辨率视口二倍图适配方案 02-rem简介媒体查询rem 布局flexible.jsrem 移动适配 03-less注释运算嵌套变量导入导出禁止导出 04-综合案例…

【SA8295P 源码分析 (三)】132 - GMSL2 协议分析 之 GPIO/SPI/I2C/UART 等通迅控制协议带宽消耗计算

【SA8295P 源码分析】132 - GMSL2 协议分析 之 GPIO/SPI/I2C/UART 等通迅控制协议带宽消耗计算 一、GPIO 透传带宽消耗计算二、SPI 通迅带宽消耗计算三、I2C 通迅带宽消耗计算四、UART 通迅带宽消耗计算系列文章汇总见:《【SA8295P 源码分析 (三)】Camera 模块 文章链接汇总 -…

nvm管理node版本过程记录

写在前面 今天记录一下windows电脑安装nvm同时使用nvm管理node版本的&#xff0c;为什么写windows版本的呢&#xff1f;因为mac版本的基本上是不需要进行记录的&#xff0c;相对windows的安装是简单很多的&#xff0c;行了废话不多说&#xff0c;我们直接开始 安装nvm nvm下载…

日志技术logback

一&#xff0c;日志概括 二&#xff0c;日志技术的特点 三&#xff0c;日志技术的体系 三&#xff0c;入门 四&#xff0c;案例 package XinZheng;import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class Main58 {//1,创建一个Logger日志对象public static fi…

PIL如何批量给图片添加文字水印?

PIL如何批量给图片添加文字水印&#xff1f; 1 简单引入2 关于PIL3 本文涉及的PIL的几个类4 实现原理5 实现过程5.1 原始图片5.2 导入相关模块5.3 初始化数据5.4 水印字体设置5.5 打开原始图片并新建存储对象5.6 计算图片和水印的大小5.7 选择性设置水印文字5.8 绘制文字并设置…

Vite - 配置 - 自动修改 index.html 中的title

需求描述 在Vue3项目的开发过程中&#xff0c;我们为了能区分正式环境和测试环境&#xff0c; 通常会进行环境配置文件的区分&#xff0c; 例如&#xff0c;开发环境一个配置文件、生产环境一个配置文件。因此&#xff0c;我们就希望 在项目的index.html 的 title 标签中&…

普冉PY32系列(十) 基于PY32F002A的6+1通道遥控小车I - 综述篇

目录 普冉PY32系列(一) PY32F0系列32位Cortex M0 MCU简介普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境普冉PY32系列(三) PY32F002A资源实测 - 这个型号不简单普冉PY32系列(四) PY32F002A/003/030的时钟设置普冉PY32系列(五) 使用JLink RTT代替串口输出日志普冉PY32…

Redis从入门到精通(三)-高阶篇

文章目录 0. 前言[【高阶篇】3.1 Redis协议(RESP )详解](https://blog.csdn.net/wangshuai6707/article/details/132742584)[【高阶篇】3.3 Redis之底层数据结构简单动态字符串(SDS)详解](https://blog.csdn.net/wangshuai6707/article/details/131101404)[【高阶篇】3.4 Redis…

GNSS技术在农业领域的创新应用

全球导航卫星系统&#xff08;GNSS&#xff09;技术在农业领域的广泛应用为现代农业带来了革命性的变革。从精准农业到农业机械自动化&#xff0c;GNSS技术为提高农业生产效率、减少资源浪费、实现可持续发展提供了关键支持。本文将深入探讨GNSS技术在农业领域的应用&#xff0…

【理解ARM架构】不同方式点灯 | ARM架构简介 | 常见汇编指令 | C与汇编

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《理解ARM架构》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f3c0;直接操作寄存器点亮LED灯&#x1f3c0;地址空间&#x1f3c0;ARM内部的寄存…

深度学习入门:自建数据集完成花鸟二分类任务

自建数据集完成二分类任务&#xff08;参考文章&#xff09; 1 图片预处理 1 .1 统一图片格式 找到的图片需要首先做相同尺寸的裁剪&#xff0c;归一化&#xff0c;否则会因为图片大小不同报错 RuntimeError: stack expects each tensor to be equal size, but got [3, 667…

5.基于飞蛾扑火算法(MFO)优化的VMD参数(MFO-VMD)

代码的使用说明 基于飞蛾扑火算法优化的VMD参数 优化算法代码原理 飞蛾扑火优化算法&#xff08;Moth-Flame Optimization&#xff0c;MFO&#xff09;是一种新型元启发式优化算法&#xff0c;该算法是受飞蛾围绕火焰飞行启发而提出的&#xff0c;具有搜索速度快、寻优能力强的…

git常常用命令

这篇文章中&#xff0c;一些简单的&#xff0c;大家都知道的git 命令我就不再赘述&#xff0c;我只写出来最近在项目中常用到的一些命令。这些命令可以帮助我更好的开发。 git stash 请大家设想下面的场景&#xff0c;你的本地有两个分支&#xff0c;develop,fix分支&#xf…