目录
背景
灰度路由设计描述
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));
}