Springcloud gateway + nacos灰度路由、非cloud项目通过Httpclient+nacos自定义lb灰度服务请求【设计实践】

目录

背景

灰度路由设计描述

Springcloud gateway设计描述

Httpclient 设计描述

实践

Springcloud gateway实现

拓展方式一

拓展方式二

Httpclient实现


背景

灰度路由设计描述

nacos中服务的元数据存储灰度信息,HTTP调用微服务接口时header头中添加灰度参数,两个灰度信息进行匹配,匹配成功说明这次请求可以走当前服务实例,通过透传header头信息实现整个链路的灰度路由。 

将各个微服务项目注册到nacos中,在nacos服务列表中可以对服务实例配置元数据信息,比如我们规定灰度服务的元数据信息需要配置一个JSON字符串格式的内容。

{
	"gray": "{\"version\":\"v1\",\"main\":\"1\"}"
}

gray是自定义灰度元数据的名称,值为json字符串,里面的信息可以自定义,目的是和请求头中的灰度参数匹配。 

Springcloud gateway设计描述

gateway实现灰度路由需要增加一个全局的过滤器,过滤器通过nacos获取到服务列表,然后解析meta信息,选择匹配的服务进行调用

Httpclient 设计描述

直接使用http工具进行post或者是get请求,解析一下请求链接,看是否是以`lb://`协议开头,如果是则说明本次请求需要做动态路由,然后通过nacos获取服务列表,解析meta信息,选择匹配的服务进行调用

实践

Springcloud gateway实现

gateway的实现原理是通过自定义一个LoadBalancerClientFilter,获取服务实例,替换lb://协议头

cloud的默认lb实现有两种:

org.springframework.cloud.gateway.filter.LoadBalancerClientFilter

这个已经被官方标记过时了,不过依然是默认的实现

org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter

需要自己写配置类开启这个reactive的filter

以上两种可以实现负载均衡路由,但是不能实现灰度的业务,所以根据这两种可以拓展实现自己的灰度filter

配置参考Springcloud gateway网关+认证服务+token方式,入口层认证统一微服务鉴权【设计实践】_殷长庆的博客-CSDN博客

拓展方式一

package com.luck.config;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.naming.utils.Chooser;
import com.alibaba.nacos.client.naming.utils.Pair;

import reactor.core.publisher.Mono;

@Component
public class NacosLoadBalancerClientFilter implements GlobalFilter, Ordered {

	public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10099;

	protected final LoadBalancerClient loadBalancer;

	private LoadBalancerProperties properties;

	@Autowired
	private NacosServiceDiscovery nacosServiceDiscovery;

	public NacosLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
		this.loadBalancer = loadBalancer;
		this.properties = properties;
	}

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

	@Override
	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);
		}
		addOriginalRequestUrl(exchange, 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();

		String overrideScheme = instance.isSecure() ? "https" : "http";
		if (schemePrefix != null) {
			overrideScheme = url.getScheme();
		}

		URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);

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

	protected ServiceInstance choose(ServerWebExchange exchange) {
		try {
			List<ServiceInstance> instances = nacosServiceDiscovery.getInstances(((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost());
			ServiceInstance instance = getInstance(instances, exchange.getRequest().getHeaders());
			if (null != instance) {
				return instance;
			}
		} catch (NacosException e) {
			e.printStackTrace();
		}

		return loadBalancer.choose(((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost());
	}

	/**
	 * 获取实例
	 * @param instances
	 * @param headers
	 * @return
	 */
	private ServiceInstance getInstance(List<ServiceInstance> instances, HttpHeaders headers) {
		if (instances.isEmpty()) {
			return getEmptyInstance();
		}
		ServiceInstance result = getInstanceByHeader(instances, headers);
		if (null == result) {
			result = getInstanceByWeight(instances);
		}
		if (null == result) {
			return getEmptyInstance();
		}
		return result;
	}

	/**
	 * 获取空实例
	 * @return
	 */
	private ServiceInstance getEmptyInstance() {
		return null;
	}

	/**
	 * 根据请求头中的灰度参数获取服务实例
	 * @param instances 服务实例集合
	 * @param headers 请求头
	 * @return
	 */
	private ServiceInstance getInstanceByHeader(List<ServiceInstance> instances, HttpHeaders headers) {
		ServiceInstance serviceInstance = null;
		for (ServiceInstance instance : instances) {
			Map<String, String> metadata = instance.getMetadata();
	        /** nacos server meta中的灰度标签 */
			String grey = metadata.get("grey");
			if (StringUtils.isNoneBlank(grey)) {
				Map<String, String> greyObject = JSONObject.parseObject(grey, new TypeReference<Map<String, String>>() {
				});
				// 根据需要调整
				long count = greyObject.entrySet().stream().map(x -> {
					if (x.getValue().equals(headers.getFirst(x.getKey()))) {
						return x;
					}
					return null;
				}).filter(x -> x != null).count();
				if (count > 0) {
					serviceInstance = instance;
					break;
				}
			}

		}
		return serviceInstance;
	}

	/**
	 * 根据权重获取服务实例
	 * @param instances 服务实例集合
	 */
	private ServiceInstance getInstanceByWeight(List<ServiceInstance> instances) {
		List<Pair<ServiceInstance>> hostsWithWeight = new ArrayList<Pair<ServiceInstance>>();
		for (ServiceInstance host : instances) {
			hostsWithWeight.add(new Pair<ServiceInstance>(host, Double.parseDouble(host.getMetadata().get("nacos.weight"))));
		}
		Chooser<String, ServiceInstance> vipChooser = new Chooser<String, ServiceInstance>("service_id");
		vipChooser.refresh(hostsWithWeight);

		return vipChooser.randomWithWeight();
	}

}

拓展方式二

参考代码gateway: gateway - Gitee.com

maven依赖修改,在原有的配置上添加lb的依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>

NacosLoadBalancer负载均衡路由工具

package com.luck.config;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.client.naming.utils.Chooser;
import com.alibaba.nacos.client.naming.utils.Pair;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class NacosLoadBalancer implements ReactorServiceInstanceLoadBalancer {

	private ObjectProvider<ServiceInstanceListSupplier> instanceProvider;
	private String serviceId;

	public NacosLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> instanceProvider, String serviceId) {
		this.serviceId = serviceId;
		this.instanceProvider = instanceProvider;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public Mono<Response<ServiceInstance>> choose(Request request) {
		HttpHeaders headers = (HttpHeaders) request.getContext();
		if (this.instanceProvider != null) {
			ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier) this.instanceProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
			return ((Flux) supplier.get()).next().map(list -> getInstance((List<ServiceInstance>) list, headers));
		}

		return null;
	}

	/**
	 * 获取实例
	 * @param instances
	 * @param headers
	 * @return
	 */
	private Response<ServiceInstance> getInstance(List<ServiceInstance> instances, HttpHeaders headers) {
		if (instances.isEmpty()) {
			return getEmptyInstance();
		}
		Response<ServiceInstance> result = getInstanceByHeader(instances, headers);
		if (null == result || result.getServer() == null) {
			result = getInstanceByWeight(instances);
		}
		if (null == result || result.getServer() == null) {
			return getEmptyInstance();
		}
		return result;
	}

	/**
	 * 获取空实例
	 * @return
	 */
	private Response<ServiceInstance> getEmptyInstance() {
		return new EmptyResponse();
	}

	/**
	 * 根据请求头中的灰度参数获取服务实例
	 * @param instances 服务实例集合
	 * @param headers 请求头
	 * @return
	 */
	private Response<ServiceInstance> getInstanceByHeader(List<ServiceInstance> instances, HttpHeaders headers) {
		ServiceInstance serviceInstance = null;
		for (ServiceInstance instance : instances) {
			Map<String, String> metadata = instance.getMetadata();
			String grey = metadata.get("grey");
			if (StringUtils.isNoneBlank(grey)) {
				Map<String, String> greyObject = JSONObject.parseObject(grey, new TypeReference<Map<String, String>>() {
				});
				// 根据需要调整
				long count = greyObject.entrySet().stream().map(x -> {
					if (x.getValue().equals(headers.getFirst(x.getKey()))) {
						return x;
					}
					return null;
				}).filter(x -> x != null).count();
				if (count > 0) {
					serviceInstance = instance;
					break;
				}
			}

		}
		return new DefaultResponse(serviceInstance);
	}

	/**
	 * 根据权重获取服务实例
	 * @param instances 服务实例集合
	 */
	private Response<ServiceInstance> getInstanceByWeight(List<ServiceInstance> instances) {
		List<Pair<ServiceInstance>> hostsWithWeight = new ArrayList<Pair<ServiceInstance>>();
		for (ServiceInstance host : instances) {
			hostsWithWeight.add(new Pair<ServiceInstance>(host, Double.parseDouble(host.getMetadata().get("nacos.weight"))));
		}
		Chooser<String, ServiceInstance> vipChooser = new Chooser<String, ServiceInstance>(serviceId);
		vipChooser.refresh(hostsWithWeight);

		return new DefaultResponse(vipChooser.randomWithWeight());
	}
}

NacosReactiveLoadBalancerClientFilter

package com.luck.config;

import java.net.URI;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

public class NacosReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {

	private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
	private final LoadBalancerClientFactory clientFactory;
	private LoadBalancerProperties properties;

	public NacosReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
		this.clientFactory = clientFactory;
		this.properties = properties;
	}

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

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

			return this.choose(exchange).doOnNext((response) -> {
				if (!response.hasServer()) {
					throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());
				} else {
					URI uri = exchange.getRequest().getURI();
					String overrideScheme = null;
					if (schemePrefix != null) {
						overrideScheme = url.getScheme();
					}

					DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance((ServiceInstance) response.getServer(), overrideScheme);
					URI requestUrl = this.reconstructURI(serviceInstance, uri);
					exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
				}
			}).then(chain.filter(exchange));
		} else {
			return chain.filter(exchange);
		}
	}

	protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {
		return LoadBalancerUriTools.reconstructURI(serviceInstance, original);
	}

	private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
		URI uri = (URI) exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
		NacosLoadBalancer loadBalancer = new NacosLoadBalancer(clientFactory.getLazyProvider(uri.getHost(), ServiceInstanceListSupplier.class), uri.getHost());
		return loadBalancer.choose(this.createRequest(exchange));
	}

	private Request<HttpHeaders> createRequest(ServerWebExchange exchange) {
		return new DefaultRequest<>(exchange.getRequest().getHeaders());
	}
}

 NacosGatewayReactiveLoadBalancerClientAutoConfiguration

package com.luck.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class NacosGatewayReactiveLoadBalancerClientAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean({ NacosReactiveLoadBalancerClientFilter.class })
	public NacosReactiveLoadBalancerClientFilter nacosReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
		return new NacosReactiveLoadBalancerClientFilter(clientFactory, properties);
	}

}

Httpclient实现

配置参考Springcloud、Springmvc+Nacos注册中心实现服务注册_殷长庆的博客-CSDN博客

注册微服务时上传灰度元数据信息

package com.luck.config.nacos;

import java.util.Properties;

import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.luck.utils.Constant;

import cn.hutool.core.util.NetUtil;

@Configuration
public class ServerDiscoveryConfig implements SmartInitializingSingleton, ApplicationListener<ContextClosedEvent> {

	private String namespace = Constant.getProperty("nacos.naming.namespace", "default");
	private String serverAddr = Constant.getProperty("nacos.naming.server-addr", null);
	private String grey = Constant.getProperty("nacos.naming.server.grey", null);// 是否灰度服务
	public static String group = Constant.getProperty("nacos.naming.group", "DEFAULT");
	private String ip = NetUtil.getLocalhostStr();
	private Integer port = Integer.parseInt(Constant.getProperty("server.port", "8080"));
	private String serviceName = Constant.getProperty("nacos.server.name", "cloud-luckserver");

	/** 服务注册 */
	public static NamingService namingService;

	@Override
	public void afterSingletonsInstantiated() {
		if (null != namingService || null == serverAddr) {
			return;
		}

		Properties properties = new Properties();
		properties.setProperty("serverAddr", serverAddr);
		properties.setProperty("namespace", namespace);

		try {
			namingService = NamingFactory.createNamingService(properties);
			Instance instance = new Instance();
			instance.setIp(ip);
			instance.setPort(port);
			if (null != grey) {
				instance.addMetadata("grey", grey);
			}
			namingService.registerInstance(serviceName, group, instance);

		} catch (NacosException e) {
			e.printStackTrace();
		}

	}

	@Override
	public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
		if (null != namingService) {
			try {
				namingService.deregisterInstance(serviceName, ip, port, group);
			} catch (NacosException e) {
				e.printStackTrace();
			}
		}
	}
}

ServerDiscoveryUtil

package com.luck.config.nacos;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.utils.Chooser;
import com.alibaba.nacos.client.naming.utils.Pair;

public class ServerDiscoveryUtil {

	/**
	 * 获取nacos注册服务
	 * @return
	 */
	public static NamingService getNamingService() {
		return ServerDiscoveryConfig.namingService;
	}

	/**
	 * 获取nacos注册服务分组名称
	 * @return
	 */
	private static String getGroupName() {
		return ServerDiscoveryConfig.group;
	}

	/**
	 * 获取一个健康服务节点
	 * @param serviceName 服务名称
	 * @return
	 */
	private static Instance selectInstance(String serviceName) {
		NamingService namingService = getNamingService();
		if (null == namingService || StringUtils.isBlank(serviceName)) {
			return null;
		}
		return selectInstanceByMeta(serviceName, null);
	}

	/**
	 * 根据header匹配meta信息,获取一个健康服务节点
	 * @param serviceName 服务名称
	 * @param header 上下文header
	 * @return
	 */
	private static Instance selectInstanceByMeta(String serviceName, Map<String, String> header) {
		NamingService namingService = getNamingService();
		if (null == namingService || StringUtils.isBlank(serviceName)) {
			return null;
		}
		try {
			List<Instance> selectInstances = namingService.selectInstances(serviceName, getGroupName(), true);
			Instance result = null;
			// 灰度流量选择灰度服务
			if (null != header && !header.isEmpty()) {
				for (Instance instance : selectInstances) {
					Map<String, String> metadata = instance.getMetadata();
					if (null != metadata && !metadata.isEmpty()) {
						String grey = metadata.get("grey");
						if (StringUtils.isNoneBlank(grey)) {
							Map<String, String> greyObject = JSONObject.parseObject(grey, new TypeReference<Map<String, String>>() {
							});
							// 根据需要调整
							long count = greyObject.entrySet().stream().map(x -> {
								if (x.getValue().equals(header.get(x.getKey()))) {
									return x;
								}
								return null;
							}).filter(x -> x != null).count();
							if (count > 0) {
								result = instance;
								break;
							}
						}
					}
				}
			}
			// 正常流量过滤灰度服务
			if (null == result) {
				List<Instance> noGreyInstances = selectInstances.stream().map(x -> {
					Map<String, String> metadata = x.getMetadata();
					if (null != metadata && !metadata.isEmpty()) {
						// 有灰度标识,需要剔除
						if (StringUtils.isNoneBlank(metadata.get("grey"))) {
							return null;
						}
					}
					return x;
				}).filter(x -> x != null).collect(Collectors.toList());

				selectInstances = CollectionUtils.isEmpty(noGreyInstances) ? selectInstances : noGreyInstances;
			}

			// 正常流量处理
			return null == result ? randomInstance(selectInstances, serviceName) : result;
		} catch (NacosException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 从集群选一个服务节点
	 * @param selectInstances 集群
	 * @param serviceName 服务名称
	 * @return
	 */
	private static Instance randomInstance(List<Instance> selectInstances, String serviceName) {
		if (null == selectInstances || selectInstances.size() == 0) {
			return null;
		}
		List<Pair<Instance>> hostsWithWeight = new ArrayList<Pair<Instance>>();
		for (Instance host : selectInstances) {
			hostsWithWeight.add(new Pair<Instance>(host, host.getWeight()));
		}
		Chooser<String, Instance> vipChooser = new Chooser<String, Instance>(serviceName);
		vipChooser.refresh(hostsWithWeight);
		return vipChooser.randomWithWeight();
	}

	/**
	 * 获取一个健康服务节点
	 * @param serviceName 服务名称
	 * @return
	 */
	public static String selectHost(String serviceName) {
		return instanceToHost(selectInstance(serviceName));
	}

	/**
	 * 获取一个健康服务节点
	 * @param serviceName 服务名称
	 * @return
	 */
	public static String selectHostByMeta(String serviceName, Map<String, String> header) {
		return instanceToHost(selectInstanceByMeta(serviceName, header));
	}

	/**
	 * 根据服务节点获取服务IP端口
	 * @param instance 服务节点
	 * @return
	 */
	private static String instanceToHost(Instance instance) {
		if (null != instance) {
			int port = instance.getPort();
			return instance.getIp() + (0 == port ? "" : ":" + instance.getPort());
		}
		return null;
	}

	/**
	 * 根据url获取服务名称,只处理lb协议开头的地址
	 * @param url 请求地址
	 * @return
	 */
	public static String lbUrlToServiceName(String url) {
		String prefix = "lb://";
		if (StringUtils.isBlank(url) || !url.startsWith(prefix)) {
			return null;
		}
		int endOf = url.indexOf("/", prefix.length());
		endOf = endOf <= 0 ? url.length() : endOf;
		return url.substring(prefix.length(), endOf);
	}

	/**
	 * 根据url获取服务真实访问地址,只处理lb协议开头的地址
	 * @param url 请求地址
	 * @param header 请求头
	 * @return
	 */
	public static String lbUrlToRealUrl(String url, Map<String, String> header) {
		String prefix = "lb://";
		if (StringUtils.isBlank(url) || !url.startsWith(prefix)) {
			return url;
		}
		int endOf = url.indexOf("/", prefix.length());
		endOf = endOf <= 0 ? url.length() : endOf;
		String serviceName = url.substring(prefix.length(), endOf);
		if (StringUtils.isBlank(serviceName)) {
			return url;
		}
		String host = selectHostByMeta(serviceName, header);
		return null == host ? null : "http://" + host + url.substring(endOf);
	}
}

httpclient在发起请求的时候修改一下lb://协议,代码参考

		URI uri = request.getURI();
		String url = uri.toString();
		Map<String, String> headers = new HashMap<>();
		Header[] allHeaders = request.getAllHeaders();
		for (Header header : allHeaders) {
		    headers.put(header.getName(), header.getValue());
		}
		String afterurl = ServerDiscoveryUtil.lbUrlToRealUrl(url, headers);
		if (null == afterurl || afterurl.startsWith("lb://")) {
			throw new Exception("lb协议转换http协议失败");
		}
		if (!url.equals(afterurl)) {
		    // 替换协议头
		    request.setURI(uri.resolve(afterurl));
		}

猜你喜欢

转载自blog.csdn.net/anshichuxuezhe/article/details/126258458