在微服务开发中,使用Spring Cloud Gateway做为服务的网关,网关后面启动N个业务服务。但是有这样一个需求,同一个用户的操作,有时候需要保证顺序性,如果使用默认负载均衡策略,同一个用户的请求可能会转发到不同的服务实例上面。所以需要实现一个负载均衡规则。
1,重写LoadBalancerClientFilter
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import java.net.URI;
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;
public class UserLoadBalancerClientFilter extends LoadBalancerClientFilter {
public UserLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
super(loadBalancer, properties);
}
@Override
protected ServiceInstance choose(ServerWebExchange exchange) {
//这里可以拿到web请求的上下文,可以从header中取出来自己定义的数据。
String userId = exchange.getRequest().getHeaders().getFirst("userId");
if (userId == null) {
return super.choose(exchange);
}
if (this.loadBalancer instanceof RibbonLoadBalancerClient) {
RibbonLoadBalancerClient client = (RibbonLoadBalancerClient) this.loadBalancer;
String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
//这里使用userId做为选择服务实例的key
return client.choose(serviceId, userId);
}
return super.choose(exchange);
}
}
2,添加自定义的负载规则
import java.util.List;
import org.apache.commons.lang.math.RandomUtils;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;
/**
*
* @ClassName: GameCenterBalanceRule
* @Description: 根据userId对服务进行负载均衡。同一个用户id的请求,都转发到同一个服务实例上面。
* @author: wgs
* @date: 2019年3月15日 下午2:17:06
*/
public class GameCenterBalanceRule extends AbstractLoadBalancerRule {
@Override
public Server choose(Object key) {//这里的key就是过滤器中传过来的userId
List<Server> servers = this.getLoadBalancer().getReachableServers();
if (servers.isEmpty()) {
return null;
}
if (servers.size() == 1) {
return servers.get(0);
}
if (key == null) {
return randomChoose(servers);
}
return hashKeyChoose(servers, key);
}
/**
*
* <p>Description:随机返回一个服务实例 </p>
* @param servers
* @return
* @author wgs
* @date 2019年3月15日 下午2:25:23
*
*/
private Server randomChoose(List<Server> servers) {
int randomIndex = RandomUtils.nextInt(servers.size());
return servers.get(randomIndex);
}
/**
*
* <p>Description:使用key的hash值,和服务实例数量求余,选择一个服务实例 </p>
* @param servers
* @param key
* @return
* @author wgs
* @date 2019年3月15日 下午2:25:36
*
*/
private Server hashKeyChoose(List<Server> servers, Object key) {
int hashCode = Math.abs(key.hashCode());
if (hashCode < servers.size()) {
return servers.get(hashCode);
}
int index = hashCode % servers.size();
return servers.get(index);
}
@Override
public void initWithNiwsConfig(IClientConfig config) {
}
}
3. 添加Bean
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class LoadBalancedBean {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public UserLoadBalancerClientFilter userLoadBalanceClientFilter(LoadBalancerClient client, LoadBalancerProperties properties) {
return new UserLoadBalancerClientFilter(client, properties);
}
}
使用的spring cloud版本:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version><!-- 注意这里面的版本,不同的版本,代码会有一些差别 -->
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
欢迎添加QQ交流群:398808948,677464431