SpringCloud(二)--Ribbon

Ribbon

RestTemplate遠程調用

springboot 提供的遠程調用工具
相似於 HttpClient,能夠發送 http 請求,並處理響應。RestTemplate簡化了Rest API調用,只須要使用它的一個方法,就能夠完成請求、響應、Json轉換java

方法:web

getForObject(url, 轉換的類型.class, 提交的參數)
postForObject(url, 協議體數據, 轉換的類型.class)

RestTemplate 和 Dubbo 遠程調用的區別:spring

  • RestTemplate:瀏覽器

    • http調用
    • 效率低
  • Dubbo:springboot

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

以前的系統結構是瀏覽器直接訪問後臺服務
直接訪問
後面咱們經過一個Demo項目演示 Spring Cloud 遠程調用服務器

Demo
下面咱們先不使用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
Ribbonapp

負載均衡

負載均衡
Ribbon 對 RestTemplate 作了封裝,加強了 RestTemplate 的功能負載均衡

  1. 得到地址表
  2. 輪詢一個服務的主機地址列表
  3. RestTemplate負責執行最終調用

添加負載均衡dom

  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 重試

重試
一種容錯機制,當調用遠程服務失敗,能夠自動重試調用
添加劇試:

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的重試機制,在 feign 和 zuul 中進一步進行了封裝,後續可使用feign或zuul的重試機制
相關文章
相關標籤/搜索