parent
9bb6bcb9cb
commit
23bbf26db7
@ -1,12 +1,14 @@ |
|||||||
package com.gitee.sop.bridge; |
package com.gitee.sop.bridge; |
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration; |
import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
import org.springframework.context.annotation.Import; |
import org.springframework.context.annotation.Import; |
||||||
|
|
||||||
/** |
/** |
||||||
* https://blog.csdn.net/seashouwang/article/details/80299571
|
* https://blog.csdn.net/seashouwang/article/details/80299571
|
||||||
* @author tanghc |
* @author tanghc |
||||||
*/ |
*/ |
||||||
|
@Configuration |
||||||
@Import(AlipayGatewayConfiguration.class) |
@Import(AlipayGatewayConfiguration.class) |
||||||
public class SopGatewayAutoConfiguration { |
public class SopGatewayAutoConfiguration { |
||||||
} |
} |
||||||
|
@ -0,0 +1,10 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.bean; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ApiParamAware<T> { |
||||||
|
ApiParam getApiParam(T t); |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.gateway.filter; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.gateway.loadbalancer.SopLoadBalancerClient; |
||||||
|
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.LoadBalancerClientFilter; |
||||||
|
import org.springframework.web.server.ServerWebExchange; |
||||||
|
|
||||||
|
import java.net.URI; |
||||||
|
|
||||||
|
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; |
||||||
|
|
||||||
|
/** |
||||||
|
* 扩展负载均衡过滤器 |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class SopLoadBalancerClientFilter extends LoadBalancerClientFilter { |
||||||
|
public SopLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) { |
||||||
|
super(loadBalancer, properties); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected ServiceInstance choose(ServerWebExchange exchange) { |
||||||
|
if (loadBalancer instanceof SopLoadBalancerClient) { |
||||||
|
SopLoadBalancerClient sopLoadBalancerClient = (SopLoadBalancerClient)loadBalancer; |
||||||
|
return sopLoadBalancerClient.choose(((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost(), exchange); |
||||||
|
} else { |
||||||
|
return super.choose(exchange); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.gateway.loadbalancer; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; |
||||||
|
import com.gitee.sop.gatewaycommon.loadbalancer.LoadBalanceServerChooser; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import org.springframework.cloud.client.ServiceInstance; |
||||||
|
import org.springframework.cloud.netflix.ribbon.SpringClientFactory; |
||||||
|
import org.springframework.web.server.ServerWebExchange; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class GatewayLoadBalanceServerChooser extends LoadBalanceServerChooser<ServerWebExchange, ServiceInstance> { |
||||||
|
|
||||||
|
public GatewayLoadBalanceServerChooser(SpringClientFactory clientFactory) { |
||||||
|
this.setClientFactory(clientFactory); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getHost(ServerWebExchange exchange) { |
||||||
|
return exchange.getRequest().getURI().getHost(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ApiParam getApiParam(ServerWebExchange exchange) { |
||||||
|
return ServerWebExchangeUtil.getApiParam(exchange); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.gateway.loadbalancer; |
||||||
|
|
||||||
|
import com.netflix.loadbalancer.Server; |
||||||
|
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer; |
||||||
|
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class NacosServerIntrospector extends DefaultServerIntrospector { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Map<String, String> getMetadata(Server server) { |
||||||
|
if (server instanceof NacosServer) { |
||||||
|
NacosServer discoveryServer = (NacosServer)server; |
||||||
|
return discoveryServer.getInstance().getMetadata(); |
||||||
|
} else { |
||||||
|
return super.getMetadata(server); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.gateway.loadbalancer; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; |
||||||
|
import com.gitee.sop.gatewaycommon.loadbalancer.ServerChooserContext; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import com.netflix.client.config.IClientConfig; |
||||||
|
import com.netflix.loadbalancer.Server; |
||||||
|
import org.springframework.cloud.client.ServiceInstance; |
||||||
|
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; |
||||||
|
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; |
||||||
|
import org.springframework.cloud.netflix.ribbon.RibbonUtils; |
||||||
|
import org.springframework.cloud.netflix.ribbon.ServerIntrospector; |
||||||
|
import org.springframework.cloud.netflix.ribbon.SpringClientFactory; |
||||||
|
import org.springframework.web.server.ServerWebExchange; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.ThreadLocalRandom; |
||||||
|
|
||||||
|
/** |
||||||
|
* 重写负载均衡处理。 |
||||||
|
* 默认使用的是RibbonLoadBalancerClient类,详见org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration#loadBalancerClient() |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class SopLoadBalancerClient extends RibbonLoadBalancerClient implements ServerChooserContext<ServerWebExchange> { |
||||||
|
|
||||||
|
private final SpringClientFactory clientFactory; |
||||||
|
private GatewayLoadBalanceServerChooser loadBalanceServerChooser; |
||||||
|
|
||||||
|
public SopLoadBalancerClient(SpringClientFactory clientFactory) { |
||||||
|
super(clientFactory); |
||||||
|
this.clientFactory = clientFactory; |
||||||
|
this.loadBalanceServerChooser = new GatewayLoadBalanceServerChooser(clientFactory); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* New: Select a server using a 'key'. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public ServiceInstance choose(String serviceId, Object hint) { |
||||||
|
return loadBalanceServerChooser.choose( |
||||||
|
serviceId |
||||||
|
, (ServerWebExchange) hint |
||||||
|
, this.getLoadBalancer(serviceId) |
||||||
|
, () -> super.choose(serviceId, hint) |
||||||
|
, (servers) -> getRibbonServer(serviceId, servers) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ApiParam getApiParam(ServerWebExchange exchange) { |
||||||
|
return ServerWebExchangeUtil.getApiParam(exchange); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getHost(ServerWebExchange exchange) { |
||||||
|
return exchange.getRequest().getURI().getHost(); |
||||||
|
} |
||||||
|
|
||||||
|
private RibbonServer getRibbonServer(String serviceId, List<Server> servers) { |
||||||
|
Server server = this.chooseRandomServer(servers); |
||||||
|
if (server == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return new RibbonServer( |
||||||
|
serviceId |
||||||
|
, server |
||||||
|
, isSecure(server, serviceId) |
||||||
|
, serverIntrospector(serviceId).getMetadata(server) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 随机选取一台实例 |
||||||
|
* |
||||||
|
* @param servers 服务列表 |
||||||
|
* @return 返回实例,没有返回null |
||||||
|
*/ |
||||||
|
private Server chooseRandomServer(List<Server> servers) { |
||||||
|
if (servers.isEmpty()) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
int serverCount = servers.size(); |
||||||
|
// 随机选取一台实例
|
||||||
|
int index = chooseRandomInt(serverCount); |
||||||
|
return servers.get(index); |
||||||
|
} |
||||||
|
|
||||||
|
private int chooseRandomInt(int serverCount) { |
||||||
|
return ThreadLocalRandom.current().nextInt(serverCount); |
||||||
|
} |
||||||
|
|
||||||
|
private ServerIntrospector serverIntrospector(String serviceId) { |
||||||
|
ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId, |
||||||
|
ServerIntrospector.class); |
||||||
|
if (serverIntrospector == null) { |
||||||
|
serverIntrospector = new DefaultServerIntrospector(); |
||||||
|
} |
||||||
|
return serverIntrospector; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isSecure(Server server, String serviceId) { |
||||||
|
IClientConfig config = this.clientFactory.getClientConfig(serviceId); |
||||||
|
ServerIntrospector serverIntrospector = serverIntrospector(serviceId); |
||||||
|
return RibbonUtils.isSecure(config, serverIntrospector, server); |
||||||
|
} |
||||||
|
} |
@ -1,64 +0,0 @@ |
|||||||
package com.gitee.sop.gatewaycommon.loadbalancer; |
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.SpringContext; |
|
||||||
import com.gitee.sop.gatewaycommon.zuul.loadbalancer.BaseServerChooser; |
|
||||||
import com.netflix.loadbalancer.Server; |
|
||||||
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer; |
|
||||||
import org.springframework.core.env.Environment; |
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
/** |
|
||||||
* 预发布、灰度环境选择,参考自:https://segmentfault.com/a/1190000017412946
|
|
||||||
* |
|
||||||
* @author tanghc |
|
||||||
*/ |
|
||||||
public class EnvironmentServerChooser extends BaseServerChooser { |
|
||||||
|
|
||||||
private static final String MEDATA_KEY_ENV = "env"; |
|
||||||
private static final String ENV_PRE_VALUE = "pre"; |
|
||||||
private static final String ENV_GRAY_VALUE = "gray"; |
|
||||||
|
|
||||||
/** |
|
||||||
* 预发布机器域名 |
|
||||||
*/ |
|
||||||
private static final String PRE_DOMAIN = "localhost"; |
|
||||||
|
|
||||||
@Override |
|
||||||
protected boolean isPreServer(Server server) { |
|
||||||
String env = getEnvValue(server); |
|
||||||
return ENV_PRE_VALUE.equals(env); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected boolean isGrayServer(Server server) { |
|
||||||
String env = getEnvValue(server); |
|
||||||
return ENV_GRAY_VALUE.equals(env); |
|
||||||
} |
|
||||||
|
|
||||||
private String getEnvValue(Server server) { |
|
||||||
// eureka存储的metadata
|
|
||||||
Map<String, String> metadata = getMetada(server); |
|
||||||
return metadata.get(MEDATA_KEY_ENV); |
|
||||||
} |
|
||||||
|
|
||||||
protected Map<String, String> getMetada(Server server) { |
|
||||||
return ((NacosServer) server).getMetadata(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 通过判断hostname来确定是否是预发布请求,可修改此方法实现自己想要的 |
|
||||||
* |
|
||||||
* @param request request |
|
||||||
* @return 返回true:可以进入到预发环境 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
protected boolean canVisitPre(Server server, HttpServletRequest request) { |
|
||||||
String serverName = request.getServerName(); |
|
||||||
String domain = SpringContext.getBean(Environment.class).getProperty("pre.domain", PRE_DOMAIN); |
|
||||||
return domain.equals(serverName); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package com.gitee.sop.gatewaycommon.loadbalancer; |
|
||||||
|
|
||||||
import com.netflix.loadbalancer.Server; |
|
||||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; |
|
||||||
|
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author tanghc |
|
||||||
*/ |
|
||||||
public class EurekaEnvironmentServerChooser extends EnvironmentServerChooser { |
|
||||||
@Override |
|
||||||
protected Map<String, String> getMetada(Server server) { |
|
||||||
return ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata(); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,8 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.loadbalancer; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class LoadBalanceConfig { |
||||||
|
|
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.loadbalancer; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.SopConstants; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.SpringContext; |
||||||
|
import com.netflix.loadbalancer.ILoadBalancer; |
||||||
|
import com.netflix.loadbalancer.Server; |
||||||
|
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; |
||||||
|
import org.springframework.cloud.netflix.ribbon.ServerIntrospector; |
||||||
|
import org.springframework.cloud.netflix.ribbon.SpringClientFactory; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.function.Function; |
||||||
|
import java.util.function.Supplier; |
||||||
|
|
||||||
|
/** |
||||||
|
* 预发布、灰度发布服务器选择 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public abstract class LoadBalanceServerChooser<T, R> implements ServerChooserContext<T> { |
||||||
|
|
||||||
|
private SpringClientFactory clientFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* 选择服务器 |
||||||
|
* |
||||||
|
* @param serviceId serviceId,仅gateway网关有作用 |
||||||
|
* @param exchange 请求上下文 |
||||||
|
* @param loadBalancer loadBalancer |
||||||
|
* @param superChooser 父类默认的选择 |
||||||
|
* @param serverChooserFunction 执行选择操作 |
||||||
|
* @return 返回服务器实例 |
||||||
|
*/ |
||||||
|
public R choose( |
||||||
|
String serviceId |
||||||
|
, T exchange |
||||||
|
, ILoadBalancer loadBalancer |
||||||
|
, Supplier<R> superChooser |
||||||
|
, Function<List<Server>, R> serverChooserFunction) { |
||||||
|
// 获取所有服务实例
|
||||||
|
List<Server> servers = loadBalancer.getReachableServers(); |
||||||
|
|
||||||
|
// 存放预发服务器
|
||||||
|
List<Server> preServers = new ArrayList<>(4); |
||||||
|
// 存放灰度发布服务器
|
||||||
|
List<Server> grayServers = new ArrayList<>(4); |
||||||
|
// 存放非预发服务器
|
||||||
|
List<Server> notPreServers = new ArrayList<>(4); |
||||||
|
|
||||||
|
for (Server server : servers) { |
||||||
|
// 获取实例metadata
|
||||||
|
Map<String, String> metadata = getMetadata(serviceId, server); |
||||||
|
// 是否开启了预发模式
|
||||||
|
if (this.isPreServer(metadata)) { |
||||||
|
preServers.add(server); |
||||||
|
} else if (this.isGrayServer(metadata)) { |
||||||
|
grayServers.add(server); |
||||||
|
} else { |
||||||
|
notPreServers.add(server); |
||||||
|
} |
||||||
|
} |
||||||
|
notPreServers.addAll(grayServers); |
||||||
|
// 如果没有开启预发布服务和灰度发布,直接用默认的方式
|
||||||
|
if (preServers.isEmpty() && grayServers.isEmpty()) { |
||||||
|
return superChooser.get(); |
||||||
|
} |
||||||
|
// 如果是从预发布域名访问过来,则认为是预发布请求
|
||||||
|
if (this.isRequestFromPreDomain(exchange)) { |
||||||
|
return serverChooserFunction.apply(preServers); |
||||||
|
} |
||||||
|
// 如果是灰度请求
|
||||||
|
if (this.isRequestGrayServer(exchange)) { |
||||||
|
return serverChooserFunction.apply(grayServers); |
||||||
|
} |
||||||
|
|
||||||
|
// 到这里说明不能访问预发/灰度服务器,则需要路由到非预发服务器
|
||||||
|
return serverChooserFunction.apply(notPreServers); |
||||||
|
} |
||||||
|
|
||||||
|
protected Map<String, String> getMetadata(String serviceId, Server server) { |
||||||
|
return serverIntrospector(serviceId).getMetadata(server); |
||||||
|
} |
||||||
|
|
||||||
|
protected SpringClientFactory getSpringClientFactory() { |
||||||
|
if (clientFactory == null) { |
||||||
|
clientFactory = SpringContext.getBean(SpringClientFactory.class); |
||||||
|
} |
||||||
|
return clientFactory; |
||||||
|
} |
||||||
|
|
||||||
|
public void setClientFactory(SpringClientFactory clientFactory) { |
||||||
|
this.clientFactory = clientFactory; |
||||||
|
} |
||||||
|
|
||||||
|
private ServerIntrospector serverIntrospector(String serviceId) { |
||||||
|
ServerIntrospector serverIntrospector = getSpringClientFactory().getInstance(serviceId, |
||||||
|
ServerIntrospector.class); |
||||||
|
if (serverIntrospector == null) { |
||||||
|
serverIntrospector = new DefaultServerIntrospector(); |
||||||
|
} |
||||||
|
return serverIntrospector; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否是预发布服务器 |
||||||
|
* |
||||||
|
* @param metadata metadata |
||||||
|
* @return true:是 |
||||||
|
*/ |
||||||
|
private boolean isPreServer(Map<String, String> metadata) { |
||||||
|
return Objects.equals(metadata.get(SopConstants.METADATA_ENV_KEY), SopConstants.METADATA_ENV_PRE_VALUE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否是灰度发布服务器 |
||||||
|
* |
||||||
|
* @param metadata metadata |
||||||
|
* @return true:是 |
||||||
|
*/ |
||||||
|
private boolean isGrayServer(Map<String, String> metadata) { |
||||||
|
return Objects.equals(metadata.get(SopConstants.METADATA_ENV_KEY), SopConstants.METADATA_ENV_GRAY_VALUE); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.loadbalancer; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiParamAware; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import org.apache.commons.lang.ArrayUtils; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ServerChooserContext<T> extends ApiParamAware<T> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 通过判断hostname来确定是否是预发布请求 |
||||||
|
* |
||||||
|
* @param t t |
||||||
|
* @return 返回true:可以进入到预发环境 |
||||||
|
*/ |
||||||
|
default boolean isRequestFromPreDomain(T t) { |
||||||
|
String domain = EnvironmentKeys.PRE_DOMAIN.getValue(); |
||||||
|
if (StringUtils.isEmpty(domain)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
String[] domains = domain.split("\\,"); |
||||||
|
return ArrayUtils.contains(domains, getHost(t)); |
||||||
|
} |
||||||
|
|
||||||
|
default boolean isRequestGrayServer(T t) { |
||||||
|
ApiParam apiParam = getApiParam(t); |
||||||
|
return apiParam.isGrayRequest(); |
||||||
|
} |
||||||
|
|
||||||
|
String getHost(T t); |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.loadbalancer.builder; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class AppIdGrayUserBuilder implements GrayUserBuilder { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String buildGrayUserKey(ApiParam apiParam) { |
||||||
|
return apiParam.fetchAppKey(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int order() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.loadbalancer.builder; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface GrayUserBuilder { |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取灰度用户key |
||||||
|
* |
||||||
|
* @param apiParam apiParam |
||||||
|
* @return 返回用户key |
||||||
|
*/ |
||||||
|
String buildGrayUserKey(ApiParam apiParam); |
||||||
|
|
||||||
|
/** |
||||||
|
* 优先级,数字小优先 |
||||||
|
* |
||||||
|
* @return 返回数字 |
||||||
|
*/ |
||||||
|
int order(); |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.loadbalancer.builder; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class IpGrayUserBuilder implements GrayUserBuilder { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String buildGrayUserKey(ApiParam apiParam) { |
||||||
|
return apiParam.fetchIp(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int order() { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
} |
@ -1,81 +0,0 @@ |
|||||||
package com.gitee.sop.gatewaycommon.zuul.loadbalancer; |
|
||||||
|
|
||||||
import com.google.common.base.Optional; |
|
||||||
import com.netflix.loadbalancer.ILoadBalancer; |
|
||||||
import com.netflix.loadbalancer.Server; |
|
||||||
import com.netflix.loadbalancer.ZoneAvoidanceRule; |
|
||||||
import com.netflix.zuul.context.RequestContext; |
|
||||||
import lombok.extern.slf4j.Slf4j; |
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest; |
|
||||||
import java.util.List; |
|
||||||
import java.util.stream.Collectors; |
|
||||||
|
|
||||||
/** |
|
||||||
* 服务实例选择器 |
|
||||||
* |
|
||||||
* @author tanghc |
|
||||||
*/ |
|
||||||
@Slf4j |
|
||||||
public abstract class BaseServerChooser extends ZoneAvoidanceRule { |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 是否是预发布服务器 |
|
||||||
* @param server 服务器 |
|
||||||
* @return true:是 |
|
||||||
*/ |
|
||||||
protected abstract boolean isPreServer(Server server); |
|
||||||
|
|
||||||
/** |
|
||||||
* 是否是灰度发布服务器 |
|
||||||
* @param server 服务器 |
|
||||||
* @return true:是 |
|
||||||
*/ |
|
||||||
protected abstract boolean isGrayServer(Server server); |
|
||||||
|
|
||||||
/** |
|
||||||
* 能否访问预发布 |
|
||||||
* @param server 预发布服务器 |
|
||||||
* @param request request |
|
||||||
* @return true:能 |
|
||||||
*/ |
|
||||||
protected abstract boolean canVisitPre(Server server, HttpServletRequest request); |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public Server choose(Object key) { |
|
||||||
ILoadBalancer lb = getLoadBalancer(); |
|
||||||
// 获取服务实例列表
|
|
||||||
List<Server> allServers = lb.getAllServers(); |
|
||||||
HttpServletRequest request = RequestContext.getCurrentContext().getRequest(); |
|
||||||
|
|
||||||
List<Server> preServers = allServers.stream() |
|
||||||
.filter(this::isPreServer) |
|
||||||
.filter(server -> canVisitPre(server, request)) |
|
||||||
.collect(Collectors.toList()); |
|
||||||
|
|
||||||
if (!preServers.isEmpty()) { |
|
||||||
return this.doChoose(preServers, key); |
|
||||||
} |
|
||||||
|
|
||||||
List<Server> grayServers = allServers.stream() |
|
||||||
.filter(this::isGrayServer) |
|
||||||
.collect(Collectors.toList()); |
|
||||||
if (!grayServers.isEmpty()) { |
|
||||||
return doChoose(grayServers, key); |
|
||||||
} |
|
||||||
|
|
||||||
return super.choose(key); |
|
||||||
} |
|
||||||
|
|
||||||
protected Server doChoose(List<Server> servers, Object key) { |
|
||||||
Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(servers, key); |
|
||||||
if (server.isPresent()) { |
|
||||||
return server.get(); |
|
||||||
} else { |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,55 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.zuul.loadbalancer; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.loadbalancer.ServerChooserContext; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import com.gitee.sop.gatewaycommon.zuul.ZuulContext; |
||||||
|
import com.google.common.base.Optional; |
||||||
|
import com.netflix.loadbalancer.Server; |
||||||
|
import com.netflix.loadbalancer.ZoneAvoidanceRule; |
||||||
|
import com.netflix.zuul.context.RequestContext; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 预发布、灰度环境选择,参考自:https://segmentfault.com/a/1190000017412946
|
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class EnvironmentServerChooser extends ZoneAvoidanceRule implements ServerChooserContext<HttpServletRequest> { |
||||||
|
|
||||||
|
private ZuulLoadBalanceServerChooser loadBalanceServerChooser = new ZuulLoadBalanceServerChooser(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getHost(HttpServletRequest request) { |
||||||
|
return request.getServerName(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ApiParam getApiParam(HttpServletRequest request) { |
||||||
|
return ZuulContext.getApiParam(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Server choose(Object key) { |
||||||
|
return loadBalanceServerChooser.choose( |
||||||
|
String.valueOf(key) |
||||||
|
, RequestContext.getCurrentContext().getRequest() |
||||||
|
, getLoadBalancer() |
||||||
|
, () -> super.choose(key) |
||||||
|
, (servers) -> this.doChoose(servers, key) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
protected Server doChoose(List<Server> servers, Object key) { |
||||||
|
Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(servers, key); |
||||||
|
if (server.isPresent()) { |
||||||
|
return server.get(); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.zuul.loadbalancer; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.loadbalancer.LoadBalanceServerChooser; |
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam; |
||||||
|
import com.gitee.sop.gatewaycommon.zuul.ZuulContext; |
||||||
|
import com.netflix.loadbalancer.Server; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class ZuulLoadBalanceServerChooser extends LoadBalanceServerChooser<HttpServletRequest, Server> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getHost(HttpServletRequest request) { |
||||||
|
return request.getServerName(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ApiParam getApiParam(HttpServletRequest request) { |
||||||
|
return ZuulContext.getApiParam(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue