API網關spring cloud gateway和負載均衡框架ribbon實戰

    一般咱們若是有一個服務,會部署到多臺服務器上,這些微服務若是都暴露給客戶,是很是難以管理的,咱們系統須要有一個惟一的出口,API網關是一個服務,是系統的惟一出口。API網關封裝了系統內部的微服務,爲客戶端提供一個定製的API。客戶端只須要調用網關接口,就能夠調用到實際的微服務,實際的服務對客戶不可見,而且容易擴展服務。java

    API網關能夠結合ribbon完成負載均衡的功能,能夠自動檢查微服務的情況,及時剔除或者加入某個微服務到可用服務列表。此外網關能夠完成權限檢查、限流、統計等功能。下面咱們將一一完成上面的功能。注意微服務只是提供rest的接口,不會有額外的組件依賴,不須要eureka等。只須要兩個工程,一個是微服務,咱們能夠部署到多臺服務器,那麼只是訪問的ip不一樣,在演示的時候,咱們在本機演示,修改端口,達到啓動多個微服務的目的,另外一個就是網關,主要是spring cloud gateway 和 ribbon兩大組件來實現網關和負載均衡等功能。git

image

GitHub代碼github

一、rest服務構建


一、建立一個父工程web

imageimage

imageimage

刪除src目錄算法

image

pom文件增長以下內容:spring

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
</parent>

二、新建一個module瀏覽器

imageimage

imageimage

三、修改pom.xml文件,增長以下內容:服務器

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

四、在resources文件夾下面增長一個application.yml文件,內容爲:架構

server:
  port: 1001

五、新增一個主入口類Provider1001Application,內容以下app

package com.yefengyu.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


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

六、新增一個rest接口HelloWorldController,內容以下:

package com.yefengyu.cloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class HelloWorldController
{
    @GetMapping("/hello")
    @ResponseBody
    public String hello()
    {
        return "hello spring cloud, 1001";
    }
}

七、啓動main方法,在瀏覽器輸入http://localhost:1001/hello能夠看到:

image

八、同理,咱們再創建2個rest服務,步驟以下

(1)新增module,參考第二小節,主要把名稱改成:

  • provider1002
  • provider1003

(2)pom.xml文件參考第三小節,加上便可。

(3)參考第四小節,新建application.yml文件,注意端口改成

  • 1002
  • 1003

(4)參考第五小節,新建一個主啓動類,名稱爲以下,內容都同樣。

  • Provider1002Application
  • Provider1003Application

(5)參考第六小節,新增一個HelloWorldController接口,其中只有下面這句中的 1001 改成 1002 或 1003,方便測試觀察結果

return "hello spring cloud, 1001";

(6)參考第7小節測試成功新增的兩個服務便可。

(7)如今的工程以下:

image

二、spring cloud gateway


Spring Cloud Gateway是Spring官方基於Spring 5.0,Spring Boot 2.0和Project Reactor等技術開發的網關,Spring Cloud Gateway旨在爲微服務架構提供一種簡單而有效的統一的API路由管理方式。

  • 路由:Gateway的基礎構建模塊。它包括一個ID,一個目標URL,一個斷言集合和一個過濾器集合。若是斷言判斷爲真,則路由匹配。
  • 斷言:這是Java8的新增功能,輸入的類型爲Spring框架的ServerWebExchange。它能夠匹配HTTP請求中的任何東西,好比:請求頭或者參數。
  • 過濾器:是Spring框架的GatewayFilter,請求和響應均可以被Filter修改。

一、新建一個module,按照上面的方式,名稱叫: gateway

二、添加依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
        <version>2.1.0.RELEASE</version>
    </dependency>
</dependencies>

三、新建一個主啓動類 GatewayApplication

package com.yefengyu.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


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

四、新增一個application.yml文件,內容以下:

server:
  port: 8080
spring:
  application:
    name: gateway_server
  cloud:
    gateway:
      default-filters:
      routes:
        - id: my_route
          uri: http://localhost:1001/
          predicates:
            - Path=/gateway/**
          filters:
            - StripPrefix=1

五、測試

訪問 http://localhost:8080/gateway/hello

image

上面只是簡單的路由轉發,能夠先了解下工做原理:

url 中的 http://localhost:8080/ 會訪問到gateway這個服務,spring cloud gateway會在配置的路由中作謂詞匹配,也就是url中的gateway匹配到了id爲my_route的路由,就會把http://localhost:8080/替換爲http://localhost:1001/,而且filters中的規則(StripPrefix)會把http://localhost:8080/gateway/hello中的gateway去掉,那麼http://localhost:8080/gateway/hello實際就會去訪問http://localhost:1001/hello,也就是訪問到了provider1001服務。

疑問?

上面的uri只配置了provider1001服務,如何使用geteway訪問三個服務呢?須要使用負載均衡ribbon

三、ribbon負載均衡


下面的操做都是在gateway這個服務操做的:

一、添加依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

二、修改配置文件,增長或者修改見紅色部分

server:
  port: 8080
spring:
  application:
    name: gateway_server
  cloud:
    gateway:
      default-filters:
      routes:
        - id: my_route
          uri: lb://my-load-balanced-service
          predicates:
            - Path=/gateway/**
          filters:
            - StripPrefix=1
my-load-balanced-service: ribbon: listOfServers: localhost:1001, localhost:1002,localhost:1003 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule

三、重啓gateway服務,不斷訪問 http://localhost:8080/gateway/hello,發現能夠不斷訪問的時候在三個服務中來回切換,達到了負載均衡的目的。上面咱們使用了輪詢的負載均衡策略。

注意:

  • listOfServers:配置的微服務的服務器ip端口
  • NFLoadBalancerRuleClassName:使用的負載均衡策略,默認提供了幾種,也能夠本身實現(後續講解),默認提供的以下:

image

疑問:

若是上面listOfServers中的任何一個服務關閉了,而後使用gateway訪問,會出現什麼狀況?

事實是這樣的:

好比provider1003服務宕機。那麼使用輪詢算法的時候,不斷訪問gateway,會出現:

provider1001 正常,provider1002 正常,provider1003 異常provider1001 正常,provider1002 正常,provider1003 異常。。。

provider1003 已經宕機,可是請求仍是不斷轉發到該服務上,致使服務訪問的過程當中,部分請求異常。

咱們須要有這樣的功能,若是某個服務宕機,那麼請求就不會發送到該服務器上,若是宕機的服務器恢復了,那麼請求又能夠發送到該服務器上,要實現這個功能,須要ribbon對服務進行健康檢查。

(1)首先微服務須要有個rest接口,就叫作heath接口吧,調用heath接口返回ok代表服務正常。

(2)gateway須要有調用heath接口的功能,而且配置到ribbon能夠不斷調用該接口,時刻檢查服務的狀態,若是有服務器掛掉,能夠很快感知到,並把該服務剔除可用服務列表。

四、健康檢查


一、provider1001,provider1002,provider1003增長一個rest接口HealthController

package com.yefengyu.cloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class HealthController
{
    @GetMapping("/heath")
    @ResponseBody
    public String heath()
    {
        return "ok";
    }
}

二、在gateway工程裏面作以下修改

(1)新建一個Config類

package com.yefengyu.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;


@Configuration
public class MainConfig
{
    @Bean
    public RestTemplate restTemplate()
    {
        return new RestTemplate();
    }
}

(2)新建一個健康檢查的類,主要是調用heath接口。

package com.yefengyu.gateway.loadbanlance;

import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;


@Component
public class HealthExamination implements IPing
{
    @Autowired
    private RestTemplate restTemplate;

    @Override
    public boolean isAlive(Server server)
    {
        String url = "http://" + server.getId() + "/heath";
        try
        {
            ResponseEntity<String> heath = restTemplate.getForEntity(url, String.class);
            if (heath.getStatusCode() == HttpStatus.OK)
            {
                System.out.println("ping " + url + " success and response is " + heath.getBody());
                return true;
            }
            System.out.println("ping " + url + " error and response is " + heath.getBody());
            return false;
        }
        catch (Exception e)
        {
            System.out.println("ping " + url + " failed");
            return false;
        }
    }
}

上面代碼繼承IPing接口,判斷服務是否可用。咱們在微服務中增長heath接口,在gateway中調用該接口,若是返回正常則認爲微服務可用。

(3)修改配置文件,注意最後一行,這是惟一增長的。

server:
  port: 8080
spring:
  application:
    name: gateway_server
 cloud: gateway: default-filters: routes: - id: my_route uri: lb://my-load-balanced-service
 predicates: - Path=/gateway/**
 filters: - StripPrefix=1 my-load-balanced-service: ribbon: listOfServers: localhost:1001,localhost:1002,localhost:1003 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule  NFLoadBalancerPingClassName: com.yefengyu.gateway.loadbanlance.HealthExamination

三、從新啓動微服務和gateway服務,而後經過網關訪問,能夠看到能夠正常訪問,若是此時把某一臺微服務停掉,訪問的時候開始可能會報錯,可是隨着健康檢查的運行,檢測到該服務不可用,則會把該服務剔除,之後只會訪問正常運行的服務。當宕機的服務重啓,健康檢查還會把該服務加入到可用服務列表,下次請求就會再次發送到該服務上。

五、自定義負載均衡策略


上面演示了隨機、輪詢等負載均衡算法,咱們能夠自定義負載均衡算法。需求是:每一個服務器訪問三次再跳轉到下一個服務器。

只須要在gateway項目中實現AbstractLoadBalancerRule抽象類,而後配置到下面便可

image

一、負載均衡算法(參考RandomRule)

package com.yefengyu.gateway.loadbanlance;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.ArrayList;
import java.util.List;


public class MyRule extends AbstractLoadBalancerRule
{
 private volatile int total; private volatile int index; List<Server> upList = new ArrayList<>();

    public MyRule()
    {
    }

    public Server choose(ILoadBalancer lb, Object key)
    {
        if (lb == null)
        {
            return null;
        }
        else
        {
            Server server = null;

            while (server == null)
            {
                if (Thread.interrupted())
                {
                    return null;
                }

                List<Server> allList = lb.getAllServers();
                int serverCount = allList.size();
                if (serverCount == 0)
                {
                    return null;
                }

                if (total == 0)
                {
                    upList = lb.getReachableServers();
                }

 if (total < 3) { if (upList.size() != lb.getReachableServers().size()) { index = 0; } server = lb.getReachableServers().get(index); total++; } else
 { total = 0; index++; if (index >= lb.getReachableServers().size()) { index = 0; } }

                if (server == null)
                {
                    Thread.yield();
                }
                else
                {
                    if (server.isAlive())
                    {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }

    public Server choose(Object key)
    {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig)
    {
    }
}

二、修改配置

image

三、重啓gateway,而後訪問http://localhost:8080/gateway/hello不斷點擊,發現點擊三次就會調整到下一個服務。

相關文章
相關標籤/搜索