負載均衡-Ribbon-Openfeign


  想想,什么叫負載均衡,就是把大量請求分散均衡的放在各個節點,不會讓單個節點負載太大而崩掉。

 


Ribbon

方法一:硬編碼分發端口實現負載均衡

  在controller處,實現兩個端口的輪詢轉發,通過奇偶數的規律,給兩個端口依次分發,但是這樣不好的是,端口定死了,如果后台節點宕機,那么程序就執行不了,數據就會訪問失敗 。

@RestController
public class UserController {
    //定義一個全局變量
    private int count = 0;


    @GetMapping("/user/findps")
    public String findps(){
        System.out.println("正在調用客戶端");
        //http請求工具去訪問
        RestTemplate restTemplate = new RestTemplate();

        //訪問另一台機器上的端口     每次執行函數都會+1
        String ps = restTemplate.getForObject("http://localhost:"+ randomPort()+"/product/findps", String.class);
        return ps;
    }

    private String randomPort() {
        int result = this.count++%2;  //每次執行完后+1
        return result == 0?"9996":"9997";   //能被2整除的話  就得到9996   不能得到2整除的話就是9997
    }
}

  新增restTemplateConfig配置類

@Configuration    //配置類
public class RestTemplateConfig {

    //在工廠中創建一個restTemplate對象  調用的時候具備負載均衡能力的工具
    @Bean
    @LoadBalanced   //代表ribbon負載均衡的restTemplate 客戶端對象
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
} 

  在后台同一個啟動類,開啟兩個啟動類端口,模擬兩個服務同時在不同機器上運行。開啟另一個端口。

  訪問同一個端口,會打印出經過的端口和數據

  這個打印的數據對應的products中的模擬數據源

 

 


方法二:使用負載均衡策略進行分發請求

1、查看請求的主機名,端口,地址

@Resource   //客戶端服務發現
private DiscoveryClient discoveryClient;

// 訪問路由
    @GetMapping("/user/findps2")
    public List<ServiceInstance> getPs2() {
        List<ServiceInstance> products = discoveryClient.getInstances("product9997");
        for(ServiceInstance product: products) {
            //節點
            System.out.println(product.getHost());
            //端口
            System.out.println(product.getPort());
            //uri
            System.out.println(product.getUri());
        }
        return products;
    }

訪問效果

2、 只顯示當下分發的端口信息

   @Resource
  private LoadBalancerClient loadBalancerClient;

// 測試3的訪問路徑 @GetMapping("/user/findps3") public String getProduct() { //根據負載均衡策略來拉取一個服務調用 ServiceInstance product = loadBalancerClient.choose("product9997"); //節點 System.out.println(product.getHost()); //端口 System.out.println(product.getPort()); //uri System.out.println(product.getUri()); return product.toString(); }

一次訪問結果:

 再次訪問結果,serviceId代表在consule注冊中心進行服務名稱,我這里端口不一樣的就是因為我同一個啟動類開啟了兩個啟動端口程序。

3、只顯示分發的端口數據

    //測試4的訪問路徑
    @GetMapping("/user/findps4")
    public String getPruduct2() {
        ServiceInstance product = loadBalancerClient.choose("product9997");   //服務名稱
        //使用遠程調用工具
        RestTemplate restTemplate = new RestTemplate();
        //獲取后台負載均衡后的uri地址拼接
        String uri = "http://" + product.getHost() + ":" + product.getPort() + "/product/findps";
        //發送請求
        String forObjects = restTemplate.getForObject(uri, String.class);
        return forObjects;  //這個是拿到請求返回的值
    }

一次訪問效果

第二次訪問效果

 4、簡化步驟

    @GetMapping("/user/findps5")
    public String getProduct3() {
        //發送請求到負載均衡
        String forObject = restTemplate.getForObject("http://product9997/product/findps", String.class);
        return forObject;
    }

一次訪問效果

二次訪問效果

 


 Ribbon負載均衡算法

  -RoundRobinRule:輪詢策略,按順序循環選擇Server

  -RandomRule:隨機策略,隨機選擇server

  -AvailabilityFilteringRule:可用過濾策略,會先過濾由於多次訪問故障而處於斷路器跳閘狀態的服務,一個服務的連接數到達了規定的閾值,就過濾掉這個服務,將剩下的服務進行輪詢訪問。

  -weightedResponseTimeRule:響應時間加權策略,根據平均響應時間計算所有服務的權重,響應時間越快服務權重越大,被選中的概率就越高,換句話說,就是誰搶的快,誰的權重越高,訪問次數越多。剛啟動時如果統計信息不足,則使用RoundRobinRule。

  -RetryRule:重試策略,先安裝RoundRobRule策略獲取服務,如果獲取失敗則制定時間內進行重試,獲取可用的服務。就是說服務獲取失敗,規定時間內重新嘗試獲取該服務。

  -BestAviableRule:最低並發策略,會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然后選擇一個並發量小的服務。先過濾掉那種多次訪問后崩掉的服務,在選擇那種訪問量小的服務悄悄溜走。

 


更換策略

  前面RestTemplate類,要補充一點內容

@Configuration    //配置類
public class RestTemplateConfig {

    //在工廠中創建一個restTemplate對象  調用的時候具備負載均衡能力的工具
    @Bean
    @LoadBalanced   //代表ribbon負載均衡的restTemplate 客戶端對象
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule ribbonRule() {
        return new RandomRule();//這里配置策略,和配置文件對應
    }
}

  在訪問端userproduct的properties中進行策略的更換配置

 


 Openfign

  ribbon在使用的過程中有很多代碼的耦合,所以使用Openfign去進行優化訪問。Openfeign默認繼承了ribbon。通過訪問user服務的連接,查詢到相同的product數據,但是訪問的是不同的接口。

實現步驟:1、引入依賴

        <!--引入openfeign依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

2、client訪問客戶端

//調用服務-value指定服務調用名稱
@FeignClient("product9997")   //被調用的服務名稱 server_id
public interface ProductClient {

    @GetMapping("/product/findps")  //調用客戶端的指定url
    public Map<String, Object> findPs();
}

  我這里client包不能亂放,springbootApplication掃描不了,如果你的也是出現類不能被容器發現,請查看該地址:https://blog.csdn.net/qq_44349459/article/details/113408191

 

 

 3、controller中訪問,在類上需要加上兩個注解

添加訪問路由

    @Resource
    private ProductClient productClient;

    @GetMapping("/user/findps6")
    public String findAlls() {
        Map<String, Object> ps = productClient.findPs();
        return ps.toString();
    }

4、效果,第一次訪問

第二次訪問結果

 


 Openfeign實現參數傳遞-Get

   user服務傳遞參數到product中,然后product得到該參數查詢相應的數據返回給瀏覽器顯示。

開發步驟:1、product服務中,在productcontroller中添加模擬數據源

    @GetMapping("/product/findById")  //這里需要訪問路由  和user服務對接
    public String findById(String productId) {
        System.out.println("port--->" + port);
        System.out.println("productId--->" + productId);
        //用hashmap來模擬數據源
        HashMap<String, Object> map = new HashMap<>();
        map.put("status", true);
        map.put("msg", "product-server port------>" + port);
        map.put("productId", productId);
        return map.toString();
    }

2、在user服務中,productclient添加接口

 

    @GetMapping("/product/findById")  //訪問路由
    public String findById(@RequestParam("productId")String productId); //這里是傳遞參數

3、在user服務中,Usercontroller中添加訪問路由

    @Resource
    private ProductClient productClients;

    //用戶通過id及訪問product
    @GetMapping("/user/findPById")
    public String getTest1(String productid) {
        String msg = productClients.findById(productid);
        System.out.println(msg);
        return msg;
    }

4、瀏覽器訪問

http://localhost:9998/user/findPById?productid=10

第一次訪問

第二次訪問

 


 Openfeign實現參數傳遞-Post

  使用瀏覽器Get得到參數值,但是user服務向product服務請求數據時,采用的是post請求方式。

開發步驟:1、在product服務中productController進行模擬數據源

    @PostMapping("/product/persist")   //持久化操作  ==save
    public String persist(String name) {  //這里需要訪問路由  和user對接
        System.out.println("port" + port);
        System.out.println("name" + name);
        HashMap<String, Object> map = new HashMap<>();
        map.put("status", true);
        map.put("msg", "psot-->product-server port-->" + port);
        map.put("name", name);
        return map.toString();
    }

2、在user服務中,productClient類中添加接口方法

    @PostMapping("/product/persist")    //這里是對接user 和product服務的
    public String persist(@RequestParam("name")String name);   //傳參數記得給requestParam

3、在user服務中,userController進行路徑訪問,使用GetMapping去接收瀏覽器傳過來的參數

    @Resource
    private ProductClient productClients;

    @GetMapping("/user/saveProduct")   //這里也可以使用瀏覽器框來接收 到后台用post進行提交
    public String postTest1(String name) {
        String msg = productClients.persist(name);   //調用接口內的持久化操作
        return msg;
    }

4、訪問效果,第一次訪問

 

 第二次訪問

 

 


 Openfeign實現參數傳遞-Post對象

  user服務向product服務傳遞對象,product接收並且返回顯示到瀏覽器。

實現步驟:1、在product服務和user服務中,都創建Vo類對象

@Data
public class ProductVo {
    private Integer id;
    private String name;
    private String date;
}

2、product服務中模擬數據源

    @PostMapping("/product/save")   //post發送對象
    public String save(@RequestBody ProductVo productVo){
        HashMap<String, Object> map = new HashMap<>();
        map.put("Status", true);
        map.put("msg", "object-product-server  port--->:" + port);
        map.put("productVo", productVo);
        return map.toString();
    }

3、user服務中productclient接口方法

    @PostMapping("/product/save")   //對應的product后台路由
    public String save(@RequestBody ProductVo productVo);//

4、user服務中controller訪問

    @Resource
    private ProductClient productClients;

    //這個對接的是瀏覽器的路由
    @GetMapping("/user/persistProduct")   //這里也可以使用瀏覽器框來接收 到后台用post進行提交
    public String postTest2(ProductVo productVo) {
        String msg = productClients.save(productVo);  //接口訪問
        return msg;
    }

5、訪問效果,第一次訪問:http://localhost:9998/user/persistProduct?id=1&name=aaa&date=20200202

第二次訪問

 


Openfeign的超時管理

  openfeign連接請求默認是1秒,如果超過1秒就訪問失敗。

 

1、做個小測試:在productcontroller內,增加線程3秒的休眠時間,保證請求超過三秒

      @PostMapping("/product/persist")   //持久化操作  ==save
        public String persist(String name) {  //這里需要訪問路由  和user對接

            //讓后台這個服務停止3秒 //openfeign有個默認請求時間為1秒 // 這里模擬的有時候訪問數據時間超過1秒 是3秒
            try{ Thread.sleep(3000); }catch(InterruptedException e) { e.printStackTrace(); }
            System.out.println("port" + port);
            System.out.println("name" + name);
        HashMap<String, Object> map = new HashMap<>();
        map.put("status", true);
        map.put("msg", "psot-->product-server port-->" + port);
        map.put("name", name);
        return map.toString();
    }

在瀏覽器訪問的結果就是,超時報錯

 

2、openfeign的時間管理

  在user服務中,properties配置文件中添加超時設置

feign.client.config.product9997.connect-timeout=5000
feign.client.config.product9997.read-timeout=5000

  瀏覽器再次訪問,成功

 

3、openfeign默認時間 

  以上是對單個服務模塊進行時間管理設置,有默認default屬性可以將所有服務都設置成該時間

#default
feign.client.config.default.connect-timeout=5000
feign.client.config.default.read-timeout=5000

  這個訪問也是沒有問題的

 


 Openfeign日志功能

1、日志的級別

#日志級別:
#openfeign 默認對debug級別信息做出響應
#none 不記錄任何事情
#basic 請求方法,url,響應狀態碼,執行時間
#header 在basic基礎上,記錄請求和響應時間的header
#full  請求,響應的header body  元數據

2、設置方式

  在user服務模塊的properties中設置日志的訪問級別。user作為訪問入口,調用debug級別。product服務作為被訪問采用full級別。

#設置方式
#分模塊 服務進行設置
feign.client.config.product9997.logger-level=full
#指定feign調用的客戶端對象包所在的包  必須是debug級別
logging.level.com.demo.user9998.controller.client=debug

  訪問效果

2021-01-30 12:34:09.356  INFO 508 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: product9997.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2021-01-30 12:34:11.510 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] <--- HTTP/1.1 200 (3358ms)
2021-01-30 12:34:11.510 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] connection: keep-alive
2021-01-30 12:34:11.510 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] content-length: 62
2021-01-30 12:34:11.510 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] content-type: text/plain;charset=UTF-8
2021-01-30 12:34:11.510 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] date: Sat, 30 Jan 2021 04:34:11 GMT
2021-01-30 12:34:11.510 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] keep-alive: timeout=60
2021-01-30 12:34:11.510 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] 
2021-01-30 12:34:11.512 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] {msg=psot-->product-server port-->9996, name=pig, status=true}
2021-01-30 12:34:11.512 DEBUG 508 --- [nio-9998-exec-2] c.d.u.controller.client.ProductClient    : [ProductClient#persist] <--- END HTTP (62-byte body)

  如果單個的級別設置麻煩,就使用全局配置

#全局設置
#feign.client.config.default.logger-level=full

 


 

總結

  本章演示了ribbon負載均衡的算法端口切換、使用負載均衡策略、其他負載均衡策略(輪詢、隨機、可用過濾、重試、響應時間加權、最低並發等策略),openfeign傳參的請求方式Get、Post,openfeign時間管理、openfeign日志功能

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM