API网关spring cloud gateway和负载均衡框架ribbon实战

目录

1|01、rest服务构建

2|02、spring cloud gateway

3|03、ribbon负载均衡

4|04、健康检查

5|05、自定义负载均衡策略


通常我们如果有一个服务,会部署到多台服务器上,这些微服务如果都暴露给客户,是非常难以管理的,我们系统需要有一个唯一的出口,API网关是一个服务,是系统的唯一出口。API网关封装了系统内部的微服务,为客户端提供一个定制的API。客户端只需要调用网关接口,就可以调用到实际的微服务,实际的服务对客户不可见,并且容易扩展服务。

    API网关可以结合ribbon完成负载均衡的功能,可以自动检查微服务的状况,及时剔除或者加入某个微服务到可用服务列表。此外网关可以完成权限检查、限流、统计等功能。下面我们将一一完成上面的功能。注意微服务只是提供rest的接口,不会有额外的组件依赖,不需要eureka等。只需要两个工程,一个是微服务,我们可以部署到多台服务器,那么只是访问的ip不同,在演示的时候,我们在本机演示,修改端口,达到启动多个微服务的目的,另一个就是网关,主要是spring cloud gateway 和 ribbon两大组件来实现网关和负载均衡等功能。

image

GitHub代码

1|01、rest服务构建

 

1、创建一个父工程

imageimage

imageimage

删除src目录

image

pom文件增加如下内容:

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

2、新建一个module

imageimage

imageimage

3、修改pom.xml文件,增加如下内容:

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

 
 
 

4、在resources文件夹下面增加一个application.yml文件,内容为:

server:
  port: 1001
 

5、新增一个主入口类Provider1001Application,内容如下

 
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);
    }
}

 
 
 

6、新增一个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";
    }
}
 
 

7、启动main方法,在浏览器输入http://localhost:1001/hello可以看到:

image

8、同理,我们再建立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

2|02、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修改。

1、新建一个module,按照上面的方式,名称叫: gateway

2、添加依赖

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

 
3、新建一个主启动类 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);
    }
}

 
4、新增一个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
5、测试

访问 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

3|03、ribbon负载均衡

 

下面的操作都是在gateway这个服务操作的:

1、添加依赖

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

 
2、修改配置文件,增加或者修改见红色部分
 
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

 
3、重启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可以不断调用该接口,时刻检查服务的状态,如果有服务器挂掉,可以很快感知到,并把该服务剔除可用服务列表。

4|04、健康检查

 

1、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";
    }
}
2、在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
3、重新启动微服务和gateway服务,然后通过网关访问,可以看到可以正常访问,如果此时把某一台微服务停掉,访问的时候开始可能会报错,但是随着健康检查的运行,检测到该服务不可用,则会把该服务剔除,以后只会访问正常运行的服务。当宕机的服务重启,健康检查还会把该服务加入到可用服务列表,下次请求就会再次发送到该服务上。

5|05、自定义负载均衡策略

 

上面演示了随机、轮询等负载均衡算法,我们可以自定义负载均衡算法。需求是:每个服务器访问三次再跳转到下一个服务器。

只需要在gateway项目中实现AbstractLoadBalancerRule抽象类,然后配置到下面即可

image

1、负载均衡算法(参考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)
    {
    }
}
 
2、修改配置

image

3、重启gateway,然后访问http://localhost:8080/gateway/hello不断点击,发现点击三次就会调整到下一个服务。

<think>好的,我现在需要回答用户关于Spring Cloud Gateway是否具有负载均衡功能的问题。首先,我需要回顾一下Spring Cloud Gateway的基本概念功能。 Spring Cloud GatewaySpring Cloud生态系统中的一个API网关,它基于Project ReactorSpring WebFlux构建,旨在提供一种简单而有效的方式来路由请求,并提供一些跨领域的关注点,如安全、监控/指标弹性。负载均衡API网关中常见的功能之一,因此需要确认Spring Cloud Gateway是否支持。 接下来,我需要考虑Spring Cloud Gateway负载均衡机制。记得Spring Cloud Gateway通常与服务发现组件(如Eureka、Consul或Zookeeper)结合使用,通过这些服务发现组件,网关可以动态地路由请求到不同的服务实例。负载均衡可能是在服务发现的基础上实现的,比如使用Ribbon或者新的Spring Cloud LoadBalancer。 我需要验证这一点。查阅Spring Cloud Gateway的官方文档,确认其是否内置了负载均衡功能。根据文档,Spring Cloud Gateway确实集成了负载均衡的功能,通过使用Spring Cloud LoadBalancer客户端来实现。这意味着当网关配置了服务发现时,它可以自动将请求分发到可用的服务实例上,实现客户端负载均衡。 此外,需要说明负载均衡的具体实现方式。例如,当使用URI以"lb://service-id"格式时,网关会使用负载均衡器来选择一个可用的实例。这里的"lb"代表负载均衡,而"service-id"是注册在服务发现中的服务名称。这样,网关就能够动态地将请求路由到不同的实例,实现负载均衡。 还需要提到可能的负载均衡策略,比如轮询、随机、加权等,以及如何配置这些策略。Spring Cloud LoadBalancer支持不同的负载均衡策略,用户可以通过配置选择适合的策略。例如,可以配置使用RoundRobinLoadBalancer(轮询)或者RandomLoadBalancer(随机)等。 另外,可能需要对比Spring Cloud Gateway与其他网关(如Zuul)在负载均衡方面的异同。Zuul 1.x也集成了Ribbon进行客户端负载均衡,而Spring Cloud Gateway作为更现代的解决方案,使用了Spring Cloud LoadBalancer,并且支持响应式编程模型,性能更好。 需要注意的是,如果用户没有配置服务发现,而是使用静态的URL列表,Spring Cloud Gateway是否还能实现负载均衡。在这种情况下,可能需要手动配置多个路由目标,但通常负载均衡更依赖于动态的服务发现机制。 最后,总结Spring Cloud Gateway确实具备负载均衡的能力,尤其是在结合服务发现组件使用时,能够自动实现请求的负载分发。需要以清晰的结构向用户解释这一点,并给出配置示例或相关文档链接,帮助用户更好地理解应用。</think>Spring Cloud Gateway **内置客户端负载均衡能力**,通过与Spring Cloud生态深度整合实现智能流量分发。以下是技术细节解析: --- ### **一、核心负载均衡机制** #### **1. 服务发现集成** ```yaml spring: cloud: gateway: routes: - id: user-service uri: lb://user-service # 关键配置:lb协议触发负载均衡 predicates: - Path=/api/users/** ``` - `lb://`协议自动激活Spring Cloud LoadBalancer - 底层集成服务发现组件(Eureka/Nacos/Consul) #### **2. 负载均衡实现** - 默认使用**RoundRobinLoadBalancer**(轮询算法) - 支持自定义策略: ```java @Bean ReactorLoadBalancer<ServiceInstance> customLoadBalancer(...) { return new RandomLoadBalancer(...); // 切换为随机算法 } ``` --- ### **二、负载均衡关键特性** #### **1. 动态路由更新** - 实时感知服务实例上下线事件 - 自动刷新路由表(结合Spring Cloud Bus) #### **2. 健康检查机制** ```yaml spring: cloud: loadbalancer: configurations: health-check # 启用健康检查 health-check: interval: 10s initial-delay: 30s ``` - 自动过滤不健康节点 - 支持主动/被动健康检查模式 #### **3. 流量权重分配** 通过自定义元数据实现金丝雀发布: ```yaml # 服务实例配置 spring: application: name: order-service cloud: nacos: discovery: metadata: traffic-weight: 20 ``` ```java // 自定义负载均衡策略 public class WeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer { // 根据traffic-weight元数据分配流量 } ``` --- ### **三、性能优化配置** #### **1. 连接池管理** ```yaml spring: cloud: gateway: httpclient: pool: maxConnections: 1000 # 最大连接数 acquireTimeout: 2000 # 连接获取超时(ms) maxIdleTime: 60s # 空闲连接存活时间 ``` #### **2. 响应缓存配置** ```java @Bean public RouteLocator routes(RouteLocatorBuilder builder) { return builder.routes() .route("cache_route", r -> r.path("/static/**") .filters(f -> f.localResponseCache(30, TimeUnit.MINUTES)) .uri("lb://static-resource")) .build(); } ``` --- ### **四、与Kubernetes服务的对比** | 特性 | Spring Cloud Gateway | Kubernetes Service + Istio | |----------------------|----------------------------|----------------------------------| | 负载均衡层级 | L7应用层 | L4(Service) + L7(Istio) | | 服务发现依赖 | 需要Spring Cloud注册中心 | 内置K8s Endpoints API | | 配置方式 | 代码/配置文件 | CRD+YAML声明式配置 | | 协议支持 | HTTP/WebSocket/gRPC | 支持TCP/UDP/HTTP等全协议 | | 性能开销 | 较低(纯Java栈) | 较高(Sidecar代理模式) | --- ### **五、生产最佳实践** #### **1. 熔断降级配置** ```yaml spring: cloud: gateway: routes: - id: payment-service uri: lb://payment filters: - name: CircuitBreaker args: name: paymentCB fallbackUri: forward:/fallback/payment statusCodes: 500,503 ``` #### **2. 分布式限流** ```java @Bean public RedisRateLimiter redisRateLimiter() { return new RedisRateLimiter(10, 20); // 每秒10个请求,突发20 } // 路由配置 .filters(f -> f.requestRateLimiter() .setRateLimiter(redisRateLimiter()) .setKeyResolver(exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName())) ) ``` --- ### **六、故障排查指南** **场景:部分节点未接收流量** 1. 检查服务发现注册状态: ```bash curl http://nacos-server:8848/nacos/v1/ns/instance/list?serviceName=user-service ``` 2. 验证负载均衡策略: ```java @Configuration public class LoadBalancerConfig { @Bean public ReactorLoadBalancer<ServiceInstance> customLB(...) { // 断点调试策略执行过程 } } ``` 3. 查看实时指标: ```text /actuator/gateway/routes # 路由详细信息 /actuator/metrics/gateway.requests ``` --- Spring Cloud Gateway通过深度整合Spring Cloud生态,提供了**声明式API路由**与**智能化负载均衡**的统一解决方案,特别适合需要精细流量控制的Spring Boot微服务架构。在Kubernetes环境中,可与Service形成互补,实现跨集群的混合流量治理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值