从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)(优化篇)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题

发布于:2022-07-26 ⋅ 阅读:(407) ⋅ 点赞:(0)

 

 

前言:
​​​​​

本篇是上一篇《从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(七) 开发环境使用轻量级在线文档解决知识分享问题》的优化篇

原来是基于下图,分散处理的,分散在各个代码层并且不是统一获取version对应的instance

本篇将集成为两个共通,统一通过client 端设置version 依次传递 下去,然后各个层通过lb获取version对应的instance,该优化是借鉴了其他一些架构,觉得不错,引入进来了,如下图

 

 源码部分

 

 新增两个共通模块mini-cloud-common-gateaway,mini-cloud-balancer ,目录结构如下

 

mini-cloud-gateaway 源码

mini-cloud-common-gateaway 代码结构以及代码明细,将作为共通被mini-cloud-gateaway 使用

 

pom.xml   dependencies

 

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <!--gateway 网关依赖,内置webflux 依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-gateway-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>com.nimbusds</groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>

 

EnableMiniCloudRoute.java 主要作为开启引用MiniCloudRouteAutoConfiguration 自定义路由的开关,不然是无法扫描到 MiniCloudRouteAutoConfiguration 类的

 

package com.minicloud.common.gateaway.annotation;

import com.minicloud.common.gateaway.config.MiniCloudRouteAutoConfiguration;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(MiniCloudRouteAutoConfiguration.class)
public @interface EnableMiniCloudRoute {
}

 

MiniCloudRouteAutoConfiguration.java 主要是注入后续源码的扫描包,便于引用项目扫面到

 

package com.minicloud.common.gateaway.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

 
@Configuration
@ComponentScan("com.minicloud.common.gateaway")
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class MiniCloudRouteAutoConfiguration {
}

 

 

MiniCloudLoadBalancerClientConfiguration.java 

  如果是使用 REACTIVE 作为web引擎,则可以初始化

MiniCloudReactiveLoadBalancerClientFilter和  MiniCloudLoadBalancer 两个类 
MiniCloudReactiveLoadBalancerClientFilter :主要是重写 ReactiveLoadBalancerClientFilter 获取request中的version

 

VersionMiniCloudLoadBalancer: 主要是获取到version 后获取version 对应lb中的instance
import com.minicloud.common.gateaway.filter.MiniCloudReactiveLoadBalancerClientFilter;
import com.minicloud.common.gateaway.rule.MiniCloudLoadBalancer;
import com.minicloud.common.gateaway.rule.VersionMiniCloudLoadBalancer;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
@EnableConfigurationProperties(LoadBalancerProperties.class)
@AutoConfigureBefore(GatewayReactiveLoadBalancerClientAutoConfiguration.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class MiniCloudLoadBalancerClientConfiguration {

	@Bean
	public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(MiniCloudLoadBalancer miniCloudLoadBalancer,
																			LoadBalancerProperties properties) {
		return new MiniCloudReactiveLoadBalancerClientFilter(properties, miniCloudLoadBalancer);
	}

	@Bean
	public MiniCloudLoadBalancer grayLoadBalancer(DiscoveryClient discoveryClient) {
		return new VersionMiniCloudLoadBalancer(discoveryClient);
	}
}

 

MiniCloudReactiveLoadBalancerClientFilter.java

 

 

@Slf4j
public class MiniCloudReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter {
	private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
	private LoadBalancerProperties properties;

	private MiniCloudLoadBalancer miniCloudLoadBalancer;

	public MiniCloudReactiveLoadBalancerClientFilter(LoadBalancerProperties properties, MiniCloudLoadBalancer miniCloudLoadBalancer) {
		super(null, properties);
		this.properties = properties;
		this.miniCloudLoadBalancer = miniCloudLoadBalancer;
	}

	@Override
	public int getOrder() {
		return LOAD_BALANCER_CLIENT_FILTER_ORDER;
	}

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

		if (log.isTraceEnabled()) {
			log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName()
					+ " url before: " + url);
		}

		return choose(exchange).doOnNext(response -> {

			if (!response.hasServer()) {
				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 = null;
			if (schemePrefix != null) {
				overrideScheme = url.getScheme();
			}

			DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(
					response.getServer(), overrideScheme);

			URI requestUrl = LoadBalancerUriTools.reconstructURI(serviceInstance, uri);

			if (log.isTraceEnabled()) {
				log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
			}
			exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
		}).then(chain.filter(exchange));
	}

	private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
		URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
		ServiceInstance serviceInstance = miniCloudLoadBalancer.choose(uri.getHost(),exchange.getRequest());
		return Mono.just(new DefaultResponse(serviceInstance));
	}
MiniCloudLoadBalancer

 

package com.minicloud.common.gateaway.rule;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.http.server.reactive.ServerHttpRequest;


public interface MiniCloudLoadBalancer {


	ServiceInstance choose(String serviceId, ServerHttpRequest request);
}

 

 

VersionMiniCloudLoadBalancer.java

 

@Slf4j
@AllArgsConstructor
public class VersionMiniCloudLoadBalancer implements MiniCloudLoadBalancer {
	private DiscoveryClient discoveryClient;



	@Override
	public ServiceInstance choose(String serviceId, ServerHttpRequest request) {
		List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);

		//注册中心无实例 抛出异常
		if (CollUtil.isEmpty(instances)) {
			log.warn("No instance available for {}", serviceId);
			throw new NotFoundException("No instance available for " + serviceId);
		}

		// 获取请求version,无则随机返回可用实例
		String reqVersion = request.getHeaders().getFirst("version");
		if (StrUtil.isBlank(reqVersion)) {
			return instances.get(RandomUtil.randomInt(instances.size()));
		}

		// 遍历可以实例元数据,若匹配则返回此实例
		for (ServiceInstance instance : instances) {
			Map<String, String> metadata = instance.getMetadata();
			String targetVersion = MapUtil.getStr(metadata, "version");
			if (reqVersion.equalsIgnoreCase(targetVersion)) {
				log.info("gray requst match success :{} {}", reqVersion, instance);
				return instance;
			}
		}
		return instances.get(RandomUtil.randomInt(instances.size()));
	}
}

 

mini-cloud-common-balancer 源码

 mini-cloud-common-balancer 代码结构以及代码明细,将作为共通被各个业务端使用

 

pom.xml 

 <dependencies>

        <dependency>
            <groupId>org.mini-cloud</groupId>
            <artifactId>mini-cloud-common-core</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
        </dependency>
    </dependencies>

spring.factories spring-boot 约定文件,也就是传说的“约定大于配置”

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.minicloud.common.balancer.config.MiniCloudRibbonLoadBalancerConfiguration

 

MiniCloudRibbonLoadBalancerConfiguration.java

主要为了注入bean: MiniCloudRibbonLoadBalancerRule ,RequestInterceptor



import com.minicloud.common.balancer.fegin.MiniCloudFeignRequestInterceptor;
import com.minicloud.common.balancer.rule.MiniCloudRibbonLoadBalancerRule;
import feign.RequestInterceptor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;


@Configuration
public class MiniCloudRibbonLoadBalancerConfiguration {

	@Bean
	@ConditionalOnMissingBean
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public MiniCloudRibbonLoadBalancerRule ribbonLoadBalancerRule() {
		return new MiniCloudRibbonLoadBalancerRule();
	}

	@Bean
	public RequestInterceptor grayFeignRequestInterceptor() {
		return new MiniCloudFeignRequestInterceptor();
	}
}

 

 

MiniCloudFeignRequestInterceptor.java 为了拦截fegin请求并设置上游发来的 version

 


@Slf4j
public class MiniCloudFeignRequestInterceptor implements RequestInterceptor {


	@Override
	public void apply(RequestTemplate template) {
		String reqVersion = WebUtils.getRequest() != null
				? WebUtils.getRequest().getHeader("version") : null;

		if (StrUtil.isNotBlank(reqVersion)) {
			log.debug("feign gray add header version :{}", reqVersion);
			template.header("version", reqVersion);
		}
	}

 

MiniCloudRibbonLoadBalancerRule.java 自定义使用端ribbon loadbalance 路由

 


@Slf4j
public class MiniCloudRibbonLoadBalancerRule extends AbstractLoadBalancerRule {
	@Override
	public void initWithNiwsConfig(IClientConfig iClientConfig) {
	}

	@Override
	public Server choose(Object key) {
		return choose(getLoadBalancer(), key);
	}


	public Server choose(ILoadBalancer lb, Object key) {
		List<Server> reachableServers = lb.getReachableServers();

		//注册中心无可用实例 抛出异常
		if (CollUtil.isEmpty(reachableServers)) {
			log.warn("No instance available for {}", key);
			return null;
		}

		// 获取请求version,无则随机返回可用实例
		String reqVersion = WebUtils.getRequest() != null
				? WebUtils.getRequest().getHeader("version") : null;
		if (StrUtil.isBlank(reqVersion)) {
			return reachableServers.get(RandomUtil.randomInt(reachableServers.size()));
		}

		// 遍历可以实例元数据,若匹配则返回此实例
		for (Server server : reachableServers) {
			NacosServer nacosServer = (NacosServer) server;
			Map<String, String> metadata = nacosServer.getMetadata();
			String targetVersion = MapUtil.getStr(metadata,"version");
			if (reqVersion.equalsIgnoreCase(targetVersion)) {
				log.debug("gray requst match success :{} {}", reqVersion, nacosServer);
				return nacosServer;
			}
		}
		return reachableServers.get(RandomUtil.randomInt(reachableServers.size()));
	}

}

 

 

 

 

 

 

 

 

 

 

 

 


网站公告

今日签到

点亮在社区的每一天
去签到