关于ribbon:SpringCloud二Ribbon

51次阅读

共计 8272 个字符,预计需要花费 21 分钟才能阅读完成。

Ribbon

RestTemplate 近程调用

springboot 提供的近程调用工具
相似于 HttpClient,能够发送 http 申请,并解决响应。RestTemplate 简化了 Rest API 调用,只须要应用它的一个办法,就能够实现申请、响应、Json 转换

办法:

getForObject(url, 转换的类型.class, 提交的参数)
postForObject(url, 协定体数据, 转换的类型.class)

RestTemplate 和 Dubbo 近程调用的区别:

  • RestTemplate:

    • http 调用
    • 效率低
  • Dubbo:

    • RPC 调用,Java 的序列化
    • 效率高

之前的系统结构是浏览器间接拜访后盾服务

前面咱们通过一个 Demo 我的项目演示 Spring Cloud 近程调用


上面咱们先不应用 ribbon, 独自应用 RestTemplate 来执行近程调用

  1. 新建 ribbon 我的项目
  2. pom.xml
  3. application.yml
  4. 主程序
  5. controller
  6. 启动,并拜访测试

新建 sp06-ribbon 我的项目


pom.xml
  • eureka-client 中曾经蕴含 ribbon 依赖
  • 须要增加 sp01-commons 依赖

application.yml

spring:
  application:
    name: ribbon
    
server:
  port: 3001
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
主程序
  • 创立 RestTemplate 实例
    RestTemplate 是用来调用其余微服务的工具类,封装了近程调用代码,提供了一组用于近程调用的模板办法,例如:getForObject()postForObject()
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.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
    
    // 创立 RestTemplate 实例,并存入 spring 容器
    @Bean
    public RestTemplate getRestTemplate() {return new RestTemplate();
    }

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

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} 占位符,用 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);
    }

Ribbon

Springcloud 集成的工具,作用是负载平衡,和重试

负载平衡


Ribbon 对 RestTemplate 做了封装,加强了 RestTemplate 的性能

  1. 取得地址表
  2. 轮询一个服务的主机地址列表
  3. RestTemplate 负责执行最终调用

增加负载平衡

  1. ribbon 依赖
  2. @LoadBalanced 注解,对 RestTemplate 进行性能加强
  3. 批改调用地址,应用 服务 id,而不是具体主机地址
增加 ribbon 起步依赖(可选)
  • eureka 依赖中曾经蕴含了 ribbon
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

RestTemplate 设置 @LoadBalanced

@LoadBalanced 负载平衡注解,会对 RestTemplate 实例进行封装,创立动静代理对象,并切入(AOP)负载平衡代码,把申请散发到集群中的服务器

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 {
    
    @LoadBalanced // 负载平衡注解
    @Bean
    public RestTemplate getRestTemplate() {return new RestTemplate();
    }

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

}
拜访门路设置为服务 id
package cn.tedu.sp06.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@Slf4j
public class RibbonController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId){
        // 近程调用商品服务
        //http://localhost:8001/{orderId}
        //{1} -- RestTemplate 定义的一种占位符格局, 传递参数 orderId
        //return restTemplate.getForObject("http://localhost:8001/{1}",JsonResult.class,orderId);
        return restTemplate.getForObject("http://item-service/{1}",JsonResult.class,orderId);//Ribbon 的形式,将 ip:port 改为服务名称
    }

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

    // -----------------------

    @GetMapping("/user-service/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId){return restTemplate.getForObject("http://user-service/{1}", JsonResult.class,userId);
    }
    @GetMapping("/user-service/{userId}/score")
    public JsonResult<?> addScore(@PathVariable Integer userId,Integer score){return restTemplate.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class,userId,score);
    }
    @GetMapping("/order-service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId){return restTemplate.getForObject("http://order-service/{1}", JsonResult.class,orderId);
    }
    @GetMapping("/order-service/")
    public JsonResult<?> addOrder(){return restTemplate.getForObject("http://order-service/", JsonResult.class);
    }
}
拜访测试
  • 拜访测试,ribbon 会把申请散发到 8001 和 8002 两个服务端口上
    http://localhost:3001/item-service/34

Ribbon 重试


一种容错机制,当调用近程服务失败,能够主动重试调用
增加重试:

1. 增加 spring-retry 依赖
 <dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
2. 配置重试参数
在 yml 中配置

MaxAutoRetries:单台服务器的
MaxAutoRetriesNextServer:更换服务器的次数
OkToRetryOnAllOperations=true: 默认只对 GET 申请重试, 当设置为 true 时, 对 POST 等所有类型申请都重试

spring:
  application:
    name: ribbon
    
server:
  port: 3001
  
eureka:
  client:    
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
      
ribbon:
  MaxAutoRetriesNextServer: 2
  MaxAutoRetries: 1
  OkToRetryOnAllOperations: true
在 java 代码中设置

ConnectTimeout:与近程服务建设网络连接的超时工夫
ReadTimeout:连贯已建设,申请已发送,期待响应的超时工夫

package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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;

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

    /*
    创立 RestTemplate 实例,放入 spring 容器
    */
    @Bean
    @LoadBalanced// 对 RestTemplate 进行加强与封装,增加 Ribbon 负载平衡性能
    public RestTemplate restTemplate(){
        // 设置调用超时工夫,超时后认为调用失败
        SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
        f.setConnectTimeout(1000);    // 建设连贯的等待时间
        f.setReadTimeout(1000);        // 连贯建设后,发送申请后。期待接管响应的工夫

        return new RestTemplate(f);
    }
}
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 Exception {log.info("server.port="+port+", orderId="+orderId);

        ///-- 设置随机提早
        if(Math.random()<0.9) {long 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.decreaseNumbers(items);
        return JsonResult.ok();}
}
拜访,测试 ribbon 重试机制
  • 通过 ribbon 拜访 item-service,当超时,ribbon 会重试申请集群中其余服务器
    http://localhost:3001/item-service/35


  • ribbon 的重试机制,在 feign 和 zuul 中进一步进行了封装,后续能够应用 feign 或 zuul 的重试机制

正文完
 0