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@SpringBootApplicationpublic 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;@RestControllerpublic 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@SpringBootApplicationpublic 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@Slf4jpublic 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;@SpringBootApplicationpublic 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@RestControllerpublic 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的重试机制