根据 cookie 进行 gateway 路由 01.01-根据 cookie 进行 gateway 路由
1. 设计 参与角色:gateway, service-provider
2. 实现 2.1 LoadBalancer 实现类,参照 RoundRibonLoadBalancer 实现。 功能:跟进前端的 cookie 中的 zone,来匹配 nacos 注册信息中 metadata(元数据)的 version。进行匹配。
其他完全根据 gateway 的路由完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 package com.alibaba.cloud.examples.loadbalancer;import com.alibaba.cloud.commons.lang.StringUtils;import java.util.List;import java.util.Random;import java.util.concurrent.atomic.AtomicInteger;import java.util.stream.Collectors;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.ObjectProvider;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.DefaultResponse;import org.springframework.cloud.client.loadbalancer.EmptyResponse;import org.springframework.cloud.client.loadbalancer.Request;import org.springframework.cloud.client.loadbalancer.RequestDataContext;import org.springframework.cloud.client.loadbalancer.Response;import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;import reactor.core.publisher.Mono;public class MyLoadBalancer implements ReactorServiceInstanceLoadBalancer { private static final Log log = LogFactory.getLog(MyLoadBalancer.class); final AtomicInteger position; final String serviceId; ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; public MyLoadBalancer ( ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String name) { this (serviceInstanceListSupplierProvider, name, new Random ().nextInt(1000 )); } public MyLoadBalancer ( ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String name, int seedPosition) { this .serviceId = name; this .serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; this .position = new AtomicInteger (seedPosition); } @Override public Mono<Response<ServiceInstance>> choose (Request request) { ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable( NoopServiceInstanceListSupplier::new ); return supplier.get(request).next() .map(serviceInstances -> processInstanceResponse(request, supplier, serviceInstances)); } private Response<ServiceInstance> processInstanceResponse (Request request, ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) { String zone = ((RequestDataContext) request.getContext()).getClientRequest().getCookies() .getFirst("zone" ); if (StringUtils.isNotBlank(zone)) { serviceInstances = serviceInstances.stream() .filter(e -> zone.equalsIgnoreCase(e.getMetadata().get("version" ))).collect(Collectors.toList()); } Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(request, serviceInstances); if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { ((SelectedInstanceCallback) supplier).selectedServiceInstance( serviceInstanceResponse.getServer()); } return serviceInstanceResponse; } private Response<ServiceInstance> getInstanceResponse (Request request, List<ServiceInstance> instances) { if (instances.isEmpty()) { if (log.isWarnEnabled()) { log.warn("No servers available for service: " + serviceId); } return new EmptyResponse (); } if (instances.size() == 1 ) { return new DefaultResponse (instances.get(0 )); } int pos = this .position.incrementAndGet() & Integer.MAX_VALUE; ServiceInstance instance = instances.get(pos % instances.size()); return new DefaultResponse (instance); } }
2.2 LoadBalancerConfiguration 配置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.alibaba.cloud.examples.config;import com.alibaba.cloud.examples.loadbalancer.MyLoadBalancer;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.env.Environment;@Configuration(proxyBeanMethods = false) public class LoadBalancerConfiguration { @Bean public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer ( Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new MyLoadBalancer ( loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); } }
2.3 启动方法 在 service-provider 类上面添加启动参数
1 -Dserver.port=0 -Dspring.cloud.nacos.discovery.metadata.version=v2
-Dserver.port=0 标识随机端口
-Dspring.cloud.nacos.discovery.metadata.version=xxx 标识 snacos 注册的元数据特殊值
2.4 效果
服务
实例端口
meta.version
zone=V1
zone=V2
zone=null
gateway
18085
service-provider
18086
V1
Y
N
N
service-provider
18087
V2
N
P
N
service-provider
18088
V2
N
P
N
service-provider
18089
N
N
Y
2.4.1 zone=V1
2.4.2 zone=V2 第 1 次请求
第 2 次请求
2.4.3 zone=null