Spring Cloud Ribbon負載均衡

1、簡介

Spring Cloud Ribbon是一個基於HTTP 和 TCP的客戶端負載工具,它基於Netflix Ribbon實現,咱們可使用它來進行遠程服務負載均衡的調用。它不像Zuul 和 Eureka 等能夠獨立部署,它雖然是一個工具類框架,可是幾乎全部的Spring Cloud微服務架構和基礎設施都離不開它,包括後面所介紹的Feign 遠程調用,也是基於Ribbon實現的工具算法

2、客戶端負載均衡

負載均衡是在一個架構中很是重要,並且不得不去實施的內容。由於負載均衡對系統的高可用,網絡壓力的緩解和處理能力擴容的重要手段之一。一般負載均衡分爲兩種:硬件負載均衡軟件負載均衡,硬件負載均衡通常是經過硬件來實現,在服務器節點之間安裝特定的負載均衡設備,好比F5。 而軟件負載均衡是採用軟件控制的手段實現的,它實在服務器之間安裝某種特定功能的軟件來完成特定的請求分開工做,好比Nginx等。不管硬件負載仍是軟件負載,只要是服務端負載均衡都能如下圖的架構方式構建起來:spring

​ 硬件負載均衡的設備和軟件負載均衡的模塊都會維護一個下掛可用的服務清單,經過心跳檢測剔除故障的服務節點以保證清單中都是能夠訪問的服務端節點。當客戶發送請求到負載均衡的設備時。設備按照服務負載均衡的算法(隨機訪問,輪詢訪問,權重訪問,最少訪問次數算法)來找到對應的服務端。服務器

​ 而客戶端負載均衡和服務端負載均衡最大的不一樣點在於上面所提到服務清單的存儲位置。在客戶端負載均衡中,全部客戶端節點都維護着本身要訪問的服務清單,而這些服務清單都來自注冊中心,好比咱們上一章介紹的Eureka服務端。網絡

​ 經過Spring Cloud Ribbon的封裝,咱們在微服務架構中使用負載均衡就比較簡單,只須要下面兩步:架構

  • 服務提供者只須要啓動多個服務實例並註冊到一個註冊中心或是多個相關聯的服務註冊中心
  • 服務消費者直接調用被@LoadBalanced註解修飾過的RestTemplate來實現面向服務的接口調用。

3、RestTemplate詳解

​ 在上一章中,咱們已經引入了Spring Cloud Ribbon實現了客戶端負載均衡的一個簡單的實例,其中,咱們使用了一個很是有用的對象RestTemplate。該對象會使用Ribbon的自動化配置,同時經過配置@LoadBalanced開啓客戶端負載均衡。下面咱們將詳細介紹RestTemplate 針對幾種不一樣的請求類型和參數類型的服務調用實現。app

準備工做負載均衡

在上一篇博客中,咱們搭建了一個註冊中心一個服務提供者一個ribbon消費者客戶端,如今咱們也須要這三個組件來作Ribbon 服務消費框架

GET請求

在RestTemplate中,對GET請求能夠經過以下兩個方法進行調用實現。ide

第一種:getForEntity()函數,該方法返回的是ResponseEntity,該對象是Spring對HTTP請求響應的封裝,其中主要存儲了HTTP的幾個重要元素,好比HTTP請求狀態碼的枚舉對象HttpStatus(經常使用的404,500這些錯誤),在它的父類HttpEntity中還存儲着HTTP請求的頭信息對象HttpHeaders以及泛型類型集合的請求體對象。

它的通常形式有三種:

/*
* url是遠程服務端的路徑,responseType是返回值類型,urlVariables是可變參數,給服務端傳遞的參數
*/
getForEntity(String url, Class<T> responseType, Object... urlVariables)
  
/*
* 可使用Map封裝參數傳遞給客戶端
*/
getForEntity(String url, Class<T> responseType, Map<String, ?> urlVariables)
  
/*
* 也是一直接使用uri地址
*/
getForEntity(URI url, Class<T> responseType) throws RestClientException

/*
* getForObject 用法和getForEntity基本相同
*/
getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException

getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException

getForObject(URI url, Class<T> responseType) throws RestClientException

URI 和 URL 的關係:

URI : 統一資源標誌符:

URL: 統一資源定位符****

URN : 統一資源名稱****

三者之間的關係:

通常用法

  • getForEntity

Ribbon 消費者

/**
     * 文章基於spring-boot-starter-parent 1.3.7 版本
     * 若是讀者使用1.5.9 以上的版本,能夠用GetMapping
     * @return
     */
    @RequestMapping(value = "/ribbon-consumer1", method = RequestMethod.GET)
    public ResponseEntity<String> helloUser(){
      // 返回值是String類型,因此對應第一個逗號後面的類型
      // /user/{1} 中的{1}表示的是第一個參數,傳的值是didi
      // 也能夠用getForEntity().getBody() 方法,此時返回值就只是一個String類型
        return restTemplate.getForEntity("http://server-provider/user/{1}",String.class,"didi");
    }

        
    @RequestMapping(value = "/ribbon-consumer2", method = RequestMethod.GET)
    public ResponseEntity<User> helloUser2(){
        // 返回值是一個User類型
        // 多個參數之間用& 隔開
        return restTemplate.getForEntity("http://server-provider/user2?id=001&name=didi",User.class);
    }

        // 傳遞一個Map類型的對象
    @RequestMapping(value = "/ribbon-consumer3", method = RequestMethod.GET)
    public ResponseEntity<String> helloUser3(){
        Map params = new HashMap();
        params.put("name","data");
        // {name}表示的是params中的key
        return restTemplate.getForEntity("http://server-provider/user3?name={name}", String.class,params);
    }

        // 其實最核心的就是經過uri進行調用,上面全部的寫法都會轉換爲下面這種寫法
        // 也就是說下面這種寫法是最根本的。
    @RequestMapping(value = "/ribbon-consumer4", method = RequestMethod.GET)
    public ResponseEntity<String> helloUser4(){
        UriComponents uriComponents = UriComponentsBuilder.fromUriString(
                "http://server-provider/user4?name={name}")
                .build()
                .expand("lx")
                .encode();
        URI uri = uriComponents.toUri();
        return restTemplate.getForEntity(uri,String.class);
    }

User 對象

public class User {

    private Integer id;
    private String name;

    public User(){}
    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
        get and set...
}

服務提供者

來看一下服務提供者的代碼:

// 返回的類型是String
    @RequestMapping(value = "/user/{name}", method = RequestMethod.GET)
    public String helloUser(@PathVariable("name") String name){
        return "Hello " + name;
    }

    // 返回的類型是User
    @RequestMapping(value = "/user2", method = RequestMethod.GET)
    public User helloUser(User user){
        return user;
    }

    @RequestMapping(value = "/user3", method = RequestMethod.GET)
    public String helloUser1(@RequestParam("name") String name){
        return "Hello " + name;
    }

    @RequestMapping(value = "/user4", method = RequestMethod.GET)
    public String helloUser2(@RequestParam("name") String name){
        return "Hello " + name;
    }
  • getForObject()

Ribbon 消費者

@RequestMapping(value = "/ribbonGet", method = RequestMethod.GET)
    public String ribbonGet(){
        // {1} 和 {2} 都是佔位符,分別表明着 001 和 lx的值 
        return restTemplate.getForObject("http://server-provider/ribbon?id={1}&name={2}",String.class,
                new Object[]{"001","lx"});
    }

        // 和上面用法基本相同
    @RequestMapping(value = "/ribbonGet2", method = RequestMethod.GET)
    public String ribbonGet2(){
        Map params = new HashMap();
        params.put("id","001");
        params.put("name","lx");
        return restTemplate.getForObject("http://server-provider/ribbon?id={id}&name={name}",String.class,
                params);
    }

    @RequestMapping(value = "/ribbonGet3", method = RequestMethod.GET)
    public String ribbonGet3(){
        UriComponents uriComponents = UriComponentsBuilder.fromUriString(
                "http://server-provider/ribbon?id={id}&name={name}")
                .build()
                .expand("001","lx")
                .encode();
        URI uri = uriComponents.toUri();
        return restTemplate.getForObject(uri,String.class);
    }

服務提供者

// 上面全部的url共用下面一個方法
        @RequestMapping(value = "/ribbon", method = RequestMethod.GET)
    public String acceptRibbon(@RequestParam("id")String id,
                               @RequestParam("name") String name){

        System.out.println("id = " + id + "name = " + name);
        return "Hello " + id + " World " + name;
    }

POST請求

瞭解完GET請求後,再來看一下POST請求:

RestTemplate中,POST請求能夠用一下幾種方式來實現

// postForEntity
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)   
postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException

// postForObject
postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
postForObject(URI url, Object request, Class<T> responseType) throws RestClientException

// postForLocation
postForLocation(String url, Object request, Object... urlVariables) throws RestClientException
postForLocation(String url, Object request, Map<String, ?> urlVariables) throws RestClientException 
postForLocation(URI url, Object request) throws RestClientException

Ribbon服務端

/**
     * 文章基於spring-boot-starter-parent 1.3.7 版本
     * 若是讀者使用1.5.9 以上的版本,能夠用 PostMapping
     * @return
     */
    @RequestMapping(value = "/ribbonPost", method = RequestMethod.POST)
    public User ribbonPost(){
        User user = new User(001,"lx");
        return restTemplate.postForEntity("http://server-provider/rpost",user,User.class)
                .getBody();
    }

    @RequestMapping(value = "/ribbonPost2", method = RequestMethod.POST)
    public User ribbonPost2(){
        User user = new User(001,"lx");
        UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://server-provider/location")
                .build()
                .expand(user)
                .encode();
        URI uri = uriComponents.toUri();
        return restTemplate.postForEntity(uri,user,User.class).getBody();
    }

    @RequestMapping(value = "/ribbonPost3", method = RequestMethod.POST)
    public String ribbonPost3(){
        User user = new User(001,"lx");
        // 佔位符石str, 服務端能夠用 @PathVariable獲取
        return restTemplate.postForEntity("http://server-provider/rbPost/{str}",user,String.class,"hello")
                .getBody();
    }

    @RequestMapping(value = "/ribbonPost4", method = RequestMethod.POST)
    public String ribbonPost4(){
        Map<String,String> params = new HashMap<>();
        params.put("id","001");
        params.put("name","lx");
        return restTemplate.postForEntity("http://server-provider/mapPost",params,String.class).getBody();
    }

    /**
     *  restTemplate.postForObject()方法與上面用法幾乎相同
     *  postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
     *  postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
     *  postForEntity(URI url, Object request, Class<T> responseType)
     *  
     *. postForLocation 也類似,這裏就再也不舉例說明了
     */

服務提供者

@RequestMapping(value = "/rpost", method = RequestMethod.POST)
    public User accpetRibbonPost(@RequestBody User user){
        log.info("id = " + user.getId() + " name = " + user.getName());
        return user;
    }

    @RequestMapping(value = "/location", method = RequestMethod.POST)
    public User acceptRibbonPost2(@RequestBody User user){
        log.info("id = " + user.getId() + " name = " + user.getName());
        return user;
    }

    @RequestMapping(value = "/rbPost/{str}", method = RequestMethod.POST)
    public String accpetRibbonPost3(@PathVariable String str, @RequestBody User user){
        log.info("str = " + str);
        log.info("id = " + user.getId() + " name = " + user.getName());
        return str + " " + user.getId() + " " + user.getName();
    }

    @RequestMapping(value = "/mapPost", method = RequestMethod.POST)
    public String acceptRibbonPost4(@RequestBody Map map){
        String id = (String)map.get("id");
        String name = (String)map.get("name");
        return "id = " + id + " name = " + name;
    }

PUT請求

Restful中的put請求常常用來修改某些屬性的值,他和POST請求類似

通常形式

/*
* 它的形式比較少,只有一種比較形式
*/
put(String url, Object request, Object... urlVariables) throws RestClientException
put(String url, Object request, Map<String, ?> urlVariables) throws RestClientException
put(URI url, Object request) throws RestClientException

Ribbon服務端

@RequestMapping(value = "/putRibbon", method = RequestMethod.PUT)
  public void putRibbon(){
    restTemplate.put("http://server-provider/ribbonPut",new User(21,"lx"));
  }

這裏只採用了一種簡單形式,用法和Post很類似,沒有再詳細說明

PUT請求沒有返回值,能夠理解爲只把須要的值傳過去就能夠,修改爲功不成功與我沒有關係

服務提供者

@RequestMapping(value = "/ribbonPut", method = RequestMethod.PUT)
  public void acceptRibbonPut(@RequestBody User user){
    log.info("user.id = " + user.getId() + " user.name = " + user.getName());
  }

DELETE請求

delete請求在Restful API中通常用於根據id刪除某條信息,用法也比較簡單,沒有返回值

通常形式

delete(String url, Object... urlVariables) throws RestClientException
delete(String url, Map<String, ?> urlVariables) throws RestClientException
delete(URI url) throws RestClientException

Ribbon服務端

@RequestMapping(value = "/deleteRibbon", method = RequestMethod.DELETE)
  public void deleteUser(){
    User user = new User(21,"lx");
    restTemplate.delete("http://server-provider/ribbonDelete/{1}",user.getId());
  }

服務提供者

@RequestMapping(value = "/ribbonDelete/{id}", method = RequestMethod.DELETE)
  public void deleteRibbon(@PathVariable Integer id){
    log.info("delete user " + id);
  }

參考資料:

https://www.jb51.net/article/138563.htm#comments

《Spring Cloud 微服務實戰》

相關文章
相關標籤/搜索