自定义负载策略:这里以ip_hash为例子
项目依赖
我个人不推荐继承这个AbstractLoadBalancerRule方式去做
<!--nacos+ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
第一种方式:
@Bean
public IRule getRule(){
return new IpHashRule();
}
@Slf4j
public class IpHashRule extends AbstractLoadBalancerRule {
public IpHashRule() {}
public IpHashRule(ILoadBalancer lb) {
this.setLoadBalancer(lb);
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {}
@Override
public Server choose(Object o) {
return this.choose(this.getLoadBalancer(), o);
}
public Server choose(ILoadBalancer lb, Object o) {
if (lb == null) {
log.warn("No load balancer!");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if (upCount != 0 && serverCount != 0) {
server = allServers.get(this.ipHashIndex(serverCount));
log.info(">>> IP_Hash 策略预选[{}]!", server);
if (server == null)
Thread.yield();
// 这里除了判断服务是否存活以及是否可用外,我还额外判断了当前服务是否存在于可用服务列表中
else if (server.isAlive() && server.isReadyToServe() && reachableServers.contains(server)) {
log.info("IP_Hash 策略选择[{}]服务!<<<", server);
return server;
} else
server = null;
continue;
}
log.warn("No up servers available from load balancer: {}", lb);
return null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: {}", lb);
}
return server;
}
private int ipHashIndex(int serverCount){
String userIp = getRemoteAddr();
int hashCode = Math.abs(userIp.hashCode());
return hashCode % serverCount;
}
private String getRemoteAddr() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 两种获取Request的方式,这里容易出现 null的情况,第二步是关键
// HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
String remoteAddr = request.getRemoteAddr();
if (request.getHeader("X-FORWARDED-FOR") != null) {
remoteAddr = request.getHeader("X-FORWARDED-FOR");
}
log.debug("RemoteAddr: {}", remoteAddr);
return remoteAddr;
}
}
这个自定义的策略规则之后:访问服务器A,在访问B之后,只会拿到A服务接口,访问出错。
解决办法:
第二种方式:
继承:LoadBalancerClientFilter
通过NacosDiscoveryProperties 获取注册中心服务实例化出服务出来做负载策略处理
在choose方法中new DefaultServiceInstance
@Bean
public BalancerFilter getBalancerFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
return new BalancerFilter(loadBalancer, properties);
}
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.DefaultServiceInstance;
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.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.web.server.ServerWebExchange;
import java.net.URI;
import java.util.List;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
public class BalancerFilter extends LoadBalancerClientFilter {
@Autowired
private NacosDiscoveryProperties properties;
public BalancerFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
super(loadBalancer, properties);
}
@Override
protected ServiceInstance choose(ServerWebExchange exchange) {
if (this.loadBalancer instanceof RibbonLoadBalancerClient) {
URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
try {
//获取nacos所有实例
List<Instance> allInstances = properties.namingServiceInstance().getAllInstances(uri.getHost());
//自己实现负载---通过获取实例
Instance instance = allInstances.get(0);
DefaultServiceInstance defaultServiceInstance = new DefaultServiceInstance();
defaultServiceInstance.setInstanceId(instance.getInstanceId());
defaultServiceInstance.setHost(instance.getIp());
defaultServiceInstance.setPort(instance.getPort());
return defaultServiceInstance;
} catch (NacosException e) {
e.printStackTrace();
}
}
return super.choose(exchange);
}
}