微服务-Gateway

案例搭建

官网地址

父Pom

xml 复制代码
<com.alibaba.cloud.version>2.2.8.RELEASE</com.alibaba.cloud.version>
<com.cloud.version>Hoxton.SR12</com.cloud.version>
<com.dubbo.version>2.2.7.RELEASE</com.dubbo.version>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${com.alibaba.cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${com.cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-dubbo -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-dubbo</artifactId>
            <version>${com.dubbo.version}</version>
        </dependency>
    </dependencies>

</dependencyManagement>

工程POM

xml 复制代码
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yml 复制代码
server:
  port: 9005
spring :
  application:
    name: gateway-server
  cloud:
    nacos:
      discovery:
        server-addr: ip:8848
    gateway:
      routes:
        - id: gateway-provider
          uri: http://127.0.0.1:8088
          predicates:
            - Path=/provider/**
          filters:
            - StripPrefix=1
        - id: gateway-consumer
          uri: http://127.0.0.1:8089
          predicates:
            - Path=/consumer/** 
          filters:
            - StripPrefix=1 #转发之前将路径前面的一个字符串去除 比如/consumer/echo  最终转发到服务是 http://127.0.0.1:8089/echo
        - id: gateway-lb
          uri: lb://openfeign-provider #负载均衡,填写nacos里面的服务名称
          predicates:
            - Path=/lb/**
          filters:
            - StripPrefix=1

启动一个普通的服务端和消费端,另外起一个有三台服务的集群,这些节点的搭建比较容易,可以看同目录笔记。

怎么做到负载均衡?怎么探索源码找到负载均衡?

org.springframework.cloud.gateway.filter.LoadBalancerClientFilter

java 复制代码
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
		String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
		if (url == null
				|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
			return chain.filter(exchange);
		}
		// preserve the original url
		addOriginalRequestUrl(exchange, url);

		if (log.isTraceEnabled()) {
			log.trace("LoadBalancerClientFilter url before: " + url);
		}
		// 选择实例
		final ServiceInstance instance = choose(exchange);

		if (instance == null) {
			throw NotFoundException.create(properties.isUse404(),
					"Unable to find instance for " + url.getHost());
		}

		URI uri = exchange.getRequest().getURI();

		// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
		// if the loadbalancer doesn't provide one.
		String overrideScheme = instance.isSecure() ? "https" : "http";
		if (schemePrefix != null) {
			overrideScheme = url.getScheme();
		}
		// 选择好节点以后重新组织请求URI
		URI requestUrl = loadBalancer.reconstructURI(
				new DelegatingServiceInstance(instance, overrideScheme), uri);

		if (log.isTraceEnabled()) {
			log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
		}

		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
		return chain.filter(exchange);
	}

自定义拦截

首先看官方网提供的几种断言,都是比较简单的这里不在演示:
官网地址

源码,这些类提供了这些能力:

自定义局部

自定义类,这个类可以参考DedupeResponseHeaderGatewayFilterFactory进行定义

java 复制代码
@Component
public class CostGatewayFilterFactory extends
        AbstractGatewayFilterFactory<CostGatewayFilterFactory.CustomConfig> {
    public CostGatewayFilterFactory() {
        super(CustomConfig.class);
    }
    @Override
    public List<String> shortcutFieldOrder() {
       return Arrays.asList("switchStatus");
    }
    @Override
    public GatewayFilter apply(CostGatewayFilterFactory.CustomConfig config) {
        return new GatewayFilter() {

            @Override
            public Mono<Void> filter(ServerWebExchange exchange,
                                     GatewayFilterChain chain) {
                if (!config.switchStatus.equalsIgnoreCase("on")) {
                    // 如果这个开关是关闭状态
                   return chain.filter(exchange);
                }
                long sTime = System.currentTimeMillis();
                exchange.getAttributes().put("STIME", sTime);
                return chain.filter(exchange).then(Mono.fromRunnable(
                        () -> {
                            long stime = (long) exchange.getAttributes().get("STIME");
                            long cost = System.currentTimeMillis() - stime;
                            System.out.println("cost is : " + cost + " ms");
                        }));
            }
            @Override
            public String toString() {
                return filterToStringCreator(
                        CostGatewayFilterFactory.this)
                        .append(config.getSwitchStatus(), config.getSwitchStatus())
                        .toString();
            }
        };
    }
    public static class CustomConfig  {
        private String switchStatus;

        public String getSwitchStatus() {
            return switchStatus;
        }

        public void setSwitchStatus(String switchStatus) {
            this.switchStatus = switchStatus;
        }
    }
}

配置

yml 复制代码
routes:
  - id: gateway-provider
    uri: http://127.0.0.1:8088
    predicates:
      - Path=/provider/**
    filters:
      - StripPrefix=1
      - Cost=ON # 这个名字取前面GatewayFilterFactory的

自定义全局

java 复制代码
@Component
public class GlobalCustomFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 过滤前
        System.out.println("Before : " + exchange.getRequest().getURI());
        return chain.filter(exchange).then(Mono.fromRunnable(
                () -> {
                    long stime = (long) exchange.getAttributes().get("STIME");
                    long cost = System.currentTimeMillis() - stime;
                    System.out.println("End cost time is  : " +  cost);
                }));
    }
}

Spring Cloud Gateway 是基于Netty 实现的,而不是Tomcat。我本来想试试mvc的过滤器和gateway的过滤器的优先级,结果怎么配置filter也不生效,原来如此,菜狗终究是菜狗。
可以换为Tomcat

转发重定向原理

  • Gateway 框架体系处理的入口org.springframework.http.server.reactive.ReactorHttpHandlerAdapter
  • 构建网关上下文:org.springframework.web.server.adapter.HttpWebHandlerAdapter
  • 遍历 Mapping 获取真实处理请求的 Handler org.springframework.web.reactive.DispatcherHandler
  • 构建过滤器链:org.springframework.cloud.gateway.handler.FilteringWebHandler
  • 过滤器必经之路:org.springframework.cloud.gateway.handler.FilteringWebHandler.DefaultGatewayFilterChain
  • 负载均衡过滤器:org.springframework.cloud.gateway.filter.LoadBalancerClientFilter
  • 利用 Netty 发送网络请求过滤器:org.springframework.cloud.gateway.filter.NettyRoutingFilter

    RouteToRequestUrlFilter 这个过滤器处理了Route的配置,并且将请求的真实地址存放到了
java 复制代码
URI mergedUrl = UriComponentsBuilder.fromUri(uri)
		// .uri(routeUri)
		.scheme(routeUri.getScheme()).host(routeUri.getHost())
		.port(routeUri.getPort()).build(encoded).toUri();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);

NettyRoutingFilter 进行了真正的转发调用

这个经典的图必须贴上:

相关推荐
天天扭码9 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
余生H34 分钟前
transformer.js(三):底层架构及性能优化指南
javascript·深度学习·架构·transformer
凡人的AI工具箱36 分钟前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
运维&陈同学1 小时前
【zookeeper01】消息队列与微服务之zookeeper工作原理
运维·分布式·微服务·zookeeper·云原生·架构·消息队列
哔哥哔特商务网14 小时前
一文探究48V新型电气架构下的汽车连接器
架构·汽车
007php00714 小时前
GoZero 上传文件File到阿里云 OSS 报错及优化方案
服务器·开发语言·数据库·python·阿里云·架构·golang
码上有前16 小时前
解析后端框架学习:从单体应用到微服务架构的进阶之路
学习·微服务·架构
gjh120818 小时前
什么是微服务?
微服务
货拉拉技术1 天前
多元消息融合分发平台
javascript·后端·架构
问窗1 天前
微服务中Spring boot的包扫描范围
java·spring boot·微服务