Spring Cloud(二)--Ribbon

本文详细介绍了如何使用SpringCloud Ribbon实现负载均衡和重试机制,包括配置Ribbon依赖、RestTemplate的@LoadBalanced注解使用、服务ID路径设置及重试参数配置。
摘要由CSDN通过智能技术生成

IDEA导入项目

1 新建空工程

2 解压 01 到 06 到工程目录

在这里插入图片描述

3 IDEA 中,按两下shift,搜索 add maven projects

在这里插入图片描述

4 选择6个项目的pom.xml进行导入(按住ctrl多选)

在这里插入图片描述

四. Spring Cloud Ribbon

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。

Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续将要介绍的Feign,它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。

Ribbon 提供了负载均衡重试功能, 它底层是使用 RestTemplate 进行 Rest API 调用。

1 RestTemplate

RestTemplate 是StringBoot提供的,Spring Cloud使用的一个Rest远程调用工具。

常用方法:

  • getForObject("url",转换类型,提交的参数) – 执行get请求
  • postForObject("url",协议体数据,转换类型) --执行post请求

单独使用RestTemplate来执行远程调用

之前的系统结构是浏览器直接访问后台服务,现在我们通过一个demo演示项目来演示Spring Cloud远程调用。

  • 1) 新建Springboot项目–sp06-ribbon
    在这里插入图片描述
    在这里插入图片描述
  • 2) 编辑pom.xml文件,添加 sp01-commons 依赖
<dependency>
   <groupId>cn.tedu</groupId>
   <artifactId>sp01-commons</artifactId>
   <version>1.0-SNAPSHOT</version>
</dependency>
  • 3) 编辑application.yml
spring:
  application:
    name: ribbon # 指定向eureka注册时的名字

server:
  port: 3001

eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
  • 4) 编辑主程序Sp06RibbonApplication

创建 RestTemplate 实例,添加@EnableDiscoveryClient注解

package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }

    //创建 RestTemplate 实例,并交给 spring 容器管理
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

  • 5)编辑RibbonController
package cn.tedu.sp06.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;

@RestController
public class RibbonController {
    @Autowired
    private RestTemplate rt;

    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
        //向指定微服务地址发送 get 请求,并获得该服务的返回结果
        //{1} resttemplate工具定义的占位符格式,用 orderId 填充
        return rt.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId);
    }

    @PostMapping("/item-service/decreaseNumber")
    public JsonResult decreaseNumber(@RequestBody List<Item> items) {
        //发送 post 请求
        return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
    }

    /

    @GetMapping("/user-service/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId) {
        return rt.getForObject("http://localhost:8101/{1}", JsonResult.class, userId);
    }

    @GetMapping("/user-service/{userId}/score")
    public JsonResult addScore(
            @PathVariable Integer userId, Integer score) {
        return rt.getForObject("http://localhost:8101/{1}/score?score={2}", JsonResult.class, userId, score);
    }

    /

    @GetMapping("/order-service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId) {
        return rt.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId);
    }

    @GetMapping("/order-service")
    public JsonResult addOrder() {
        return rt.getForObject("http://localhost:8201/", JsonResult.class);
    }
}


  • 6) 启动sp06服务,访问测试
    注册表信息:
    在这里插入图片描述
    http://localhost:3001/item-service/35
    在这里插入图片描述
    以上响应结果是xml格式,这是因为eureka-client的jackson-dataformat-xml依赖项将响应的数据自动转成了xml格式,需要排除掉这个依赖项。

分别在sp02,sp03,sp04,sp06项目的 spring-cloud-starter-netflix-eureka-client 依赖中排除 jackson.dataformat-xml:

<exclusions>
 	<exclusion>
		<artifactId>jackson-dataformat-xml</artifactId>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
    </exclusion>
</exclusions>

在这里插入图片描述
其它测试:
http://localhost:3001/item-service/decreaseNumber
使用postman,POST发送以下格式数据:

[{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]

http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100

http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/

2 Ribbon 负载均衡和重试

在这里插入图片描述
在这里插入图片描述
Ribbon 的作用,解决的问题:

  • 负载均衡(微服务系统必须功能)

访问压力可以分散到多台服务器。

  • 重试(不是必须功能)

当第一次请求失败或等待超时,可以自动发起重试请求。

2.1 Ribbon负载均衡

1) 添加ribbon依赖

在 eureka-client 中已经包含。

2)RestTemplate 设置 @LoadBalanced

@LoadBalanced 负载均衡注解,会对 RestTemplate 实例进行封装,创建动态代理对象,并切入(AOP)负载均衡代码,把请求分发到集群中的服务器。对 RestTemplate功能进行增强。

package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }

    //创建 RestTemplate 实例,并交给 spring 容器管理
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

3)访问路径设置为服务id

在 RibbonController 中,请求的后台服务路径,改成 service-id,根据eureka地址表,可以获取这个服务的集群主机地址列表。

package cn.tedu.sp06.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;

@RestController
public class RibbonController {
	@Autowired
	private RestTemplate rt;
	
	@GetMapping("/item-service/{orderId}")
	public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
	    //这里服务器路径用 service-id 代替,ribbon 会向服务的多台集群服务器分发请求
	    //从eureka可以得到"item-service"对应的主机地址列表:localhost:8001,localhost:8002
		return rt.getForObject("http://item-service/{1}", JsonResult.class, orderId);
	}

	@PostMapping("/item-service/decreaseNumber")
	public JsonResult decreaseNumber(@RequestBody List<Item> items) {
		return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
	}

	/
	
	@GetMapping("/user-service/{userId}")
	public JsonResult<User> getUser(@PathVariable Integer userId) {
		return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
	}

	@GetMapping("/user-service/{userId}/score") 
	public JsonResult addScore(
			@PathVariable Integer userId, Integer score) {
		return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
	}
	
	/
	
	@GetMapping("/order-service/{orderId}")
	public JsonResult<Order> getOrder(@PathVariable String orderId) {
		return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);
	}

	@GetMapping("/order-service")
	public JsonResult addOrder() {
		return rt.getForObject("http://order-service/", JsonResult.class);
	}
}
4)访问测试负载均衡

访问测试,ribbon 会把请求分发到 8001 和 8002 两个服务端口上.
http://localhost:3001/item-service/34添加链接描述
在这里插入图片描述
在这里插入图片描述

2.2 Ribbon重试

重试是一种容错机制。
当调用后台服务失败,可以自动重试,如果重试成功,可以向客户端正常返回结果。

1)添加 spring-retry 依赖
<dependency>
	<groupId>org.springframework.retry</groupId>
	<artifactId>spring-retry</artifactId>
</dependency>
2)设置重试参数:
  • MaxAutoRetries - 单台服务器的重试次数
  • MaxAutoRetriesNextServer - 更换服务器的次数
  • OkToRetryOnAllOperations - 是否对所有类型请求都重试,默认只对 get 重试;如果要对Post等所有请求重试,设置成 true。
    注意:对post请求重试,需要做好 幂等性 控制,因为重复操作可能引起不一致问题。

       下面两个超时设置不能在 yml 中配置,需要在 java 代码中设置:

  • ConnectTimeout - 建立连接超时时间
  • ReadTimeout - 已建立连接并发送了请求,等待响应的超时时间
application.yml 配置 ribbon 重试
spring:
  application:
    name: ribbon # 指定向eureka注册时的名字

server:
  port: 3001

eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
# ribbon 重试配置
ribbon:
  # 没有提示,而且有黄色警告
  MaxAutoRetriesNextServer: 2
  MaxAutoRetries: 1
  OkToRetryOnAllOperations: true
主程序设置 RestTemplate 的请求工厂的超时属性
package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        //RestTemplate 中默认的 Factory 实例中,两个超时属性默认是 -1,
        //未启用超时,也不会触发重试
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(1000);
        factory.setReadTimeout(1000);
        return new RestTemplate(factory);
    }
}

3)访问测试
将item-service 的 ItemController 添加延迟代码,以便测试 ribbon 的重试机制
package cn.tedu.sp02.item.controller;

import java.util.List;
import java.util.Random;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import cn.tedu.web.util.JsonResult;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
public class ItemController {
    @Autowired
    private ItemService itemService;

    @Value("${server.port}")
    private int port;

    @GetMapping("/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId) throws InterruptedException {
        log.info("server.port="+port+", orderId="+orderId);

        if(Math.random()<0.6){
            // 60%概率会执行延迟代码,设置随机延迟
            int t = new Random().nextInt(5000);
            log.info("item-service-"+port+"-暂停"+t);
            Thread.sleep(t);
        }

        List<Item> items = itemService.getItems(orderId);
        return JsonResult.ok(items).msg("port="+port);
    }

    @PostMapping("/decreaseNumber")
    public JsonResult decreaseNumber(@RequestBody List<Item> items) {
        itemService.decreaseNumber(items);
        return JsonResult.ok().msg("减少商品库存成功");
    }
}

访问测试 ribbon 重试机制

通过 ribbon 访问 item-service,当超时,ribbon 会重试请求集群中其他服务器
http://localhost:3001/item-service/35

weixin073智慧旅游平台开发微信小程序+ssm后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值