一次突然的技术分享--危机感是怎么产生的

本文深入探讨了微服务架构中的网关概念及其核心功能。详细分析了网关的工作原理、架构设计、源码剖析及实战案例,重点讲解了路由与过滤器的作用与实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

技术、、、即便当时搞通了,过上个一年半载不用也就忘了

我们普通工作岗位基本都是快速迭代业务,由于历史业务太多也不会轻易去改变之前的技术

时间+不轻易改变=荒废,为了改变这种趋势,训练就成了和平时期的必须课

有此种感慨也是因为近期安排了组内技术分享,时间紧的情况下找题材,肯定是要把之前学过的知识点拿出来,但翻看之前的博客,发现一年前写的那些貌似有点陌生了(可能是当时理解的不到位,写作能力欠佳),我感觉到了一丝丝危急,我正走在荒废的道路上

我供git上找出之前写的代码,对比当时写的博客开始试运行,,,总体来说还是比较顺利,但并非手到擒来,中途也遇问题反复调试

ppt已成,来个逐字稿试试(微服务路由网关)

围绕核心点

  1. 网关是什么,作用有哪些
  2. 架构分析
  3. 源码剖析
  4. 案例实操

首先学习一个知识点,我们要先了解它是什么,能干什么活帮我们解决哪些问题,我理解的网关就是在接收外部(外网)系统调用时需要有一个前置,作为外部和内部的一个连接帮我们去统一暴露,统一处理一些公关的东西,比如:统一暴露入口,接收外部请求转发到内部,统一安全服务,保护内部服务(服务熔断,限流)等,把一些公关的东西统一提取到网关层,而我们的业务服务只需要关系业务即可(一定要确保内部服务不可外网访问)

网关最核心的功能点:路由、Filter,路由就是根据请求和我们配置的一些规则能正确的匹配到我们内部服务,Filter是后面做一些安全,熔断、限流之类的处理都是根据Filter来做

外部请求(Gateway Client)到网关后,通过Handler Mapping(类似于Spring MVC的)和我们配置的一些规则去匹配我们的Controller以及我们的Web Handler(所支持的Filter),然后按照优先级去执行我们的Filter,通过后去执行我们的底层服务,然后返回并包装结果返回Clent,这里的Filter包含网关自带和我们自定义的,后面我们做一些鉴权之类的操作就是往里加Filter就好了

服务启动时加载配置,入口GatewayAutoConfiguration,首先我们看FilteringWebHandler#filteringWebHandler这个bean是加载本地Filter

@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
	return new FilteringWebHandler(globalFilters);
}
private final List<GatewayFilter> globalFilters;

public FilteringWebHandler(List<GlobalFilter> globalFilters) {
	this.globalFilters = loadFilters(globalFilters);
}

private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
	return filters.stream().map(filter -> {
		GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
		if (filter instanceof Ordered) {
			int order = ((Ordered) filter).getOrder();
			return new OrderedGatewayFilter(gatewayFilter, order);
		}
		return gatewayFilter;
	}).collect(Collectors.toList());
}

再看RoutePredicateHandlerMapping#routePredicateHandlerMapping这个bean初始化Handler Mapping,注意它继承AbstractHandlerMapping

public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {

	private final FilteringWebHandler webHandler;

	private final RouteLocator routeLocator;

	private final Integer managementPort;

	private final ManagementPortType managementPortType;

	public RoutePredicateHandlerMapping(FilteringWebHandler webHandler,
			RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties,
			Environment environment) {
		this.webHandler = webHandler;
		this.routeLocator = routeLocator;

		this.managementPort = getPortProperty(environment, "management.server.");
		this.managementPortType = getManagementPortType(environment);
		setOrder(1);
		setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
	}

当发生调用时会通过AbstractHandlerMapping的getHandler方法去通过request找到对应的handler

public Mono<Object> getHandler(ServerWebExchange exchange) {
        return this.getHandlerInternal(exchange).map((handler) -> {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
            }

            if (this.hasCorsConfigurationSource(handler)) {
                ServerHttpRequest request = exchange.getRequest();
                CorsConfiguration config = this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null;
                CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, exchange);
                config = config != null ? config.combine(handlerConfig) : handlerConfig;
                if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
                    return REQUEST_HANDLED_HANDLER;
                }
            }

            return handler;
        });
    }

    protected abstract Mono<?> getHandlerInternal(ServerWebExchange var1);

内部调用了getHandlerInternal方法,而我们的RoutePredicateHandlerMapping也重写了此方法

@Override
	protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
		// don't handle requests on management port if set and different than server port
		if (this.managementPortType == DIFFERENT && this.managementPort != null
				&& exchange.getRequest().getURI().getPort() == this.managementPort) {
			return Mono.empty();
		}
		exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());

		return lookupRoute(exchange)
				// .log("route-predicate-handler-mapping", Level.FINER) //name this
				.flatMap((Function<Route, Mono<?>>) r -> {
					exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
					if (logger.isDebugEnabled()) {
						logger.debug(
								"Mapping [" + getExchangeDesc(exchange) + "] to " + r);
					}

					exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
					return Mono.just(webHandler);
				}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
					exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
					if (logger.isTraceEnabled()) {
						logger.trace("No RouteDefinition found for ["
								+ getExchangeDesc(exchange) + "]");
					}
				})));
	}

通过lookupRoute方法找到对应的路由,然后关联webHandler(这个就是我们第一步加载的Filters)

然后就是调用时根据request去重新组装新的url

@Bean
public RouteToRequestUrlFilter routeToRequestUrlFilter() {
	return new RouteToRequestUrlFilter();
}
public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {

	。。。。。

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
		if (route == null) {
			return chain.filter(exchange);
		}
		log.trace("RouteToRequestUrlFilter start");
		URI uri = exchange.getRequest().getURI();
		boolean encoded = containsEncodedParts(uri);
		URI routeUri = route.getUri();

		if (hasAnotherScheme(routeUri)) {
			// this is a special url, save scheme to special attribute
			// replace routeUri with schemeSpecificPart
			exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR,
					routeUri.getScheme());
			routeUri = URI.create(routeUri.getSchemeSpecificPart());
		}

		。。。

		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);
		return chain.filter(exchange);
	}

}

上一步只是组装了uri我们看具体的根据不同协议组装url,这里主要看lb协议LoadBalancerClientFilter

@Override
	@SuppressWarnings("Duplicates")
	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 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);
	}

到此组装出真正的新url

最后执行WebClientHttpRoutingFilter,请求url,注意这个Filter的order=Ordered.LOWEST_PRECEDENCE;也就是最后一个执行

案例实操:https://gitee.com/carpentor/spring-cloud-example.git

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值