天天看點

【開發經驗】gateway網關開發調試優先選擇本地服務

背景

在微服務開發時,本地要啟動太多的服務,比如基本服務,注冊中心、網關、鑒權等等。還要啟動目前代碼所在的服務。這樣,本地環境會特别卡,影響開發效率。

【開發經驗】gateway網關開發調試優先選擇本地服務

如下圖所示:

在區域網路内搭建一套開發環境,在開發時,這個開發環境啟動的服務用來做“備胎”。

1.用戶端通路的時候,攜帶自己想要優先通路的ip。

2.網關在轉發時,發現用戶端有想要優先通路的ip,則優先轉發。

在整個微服務轉發過程中,設計到進入​

​網關時轉發​

​​和​

​RPC調用時轉發​

​​;此隻介紹網關轉發時鎖定遠端ip。

如果是dubbo調用時想鎖定遠端ip可參考文章​​dubbo多服務本地開發調試​​

代碼思路

網關層面在進行服務選擇時,肯定會加載所有的服務,然後根據随機、輪訓、權重等規則進行分發。隻要自定義自己的路由規則即可。建立遠端鎖定ip負載均衡類​

​LockRemoteIpLoadBalancer.java​

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.*;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class LockRemoteIpLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private static final Log log = LogFactory.getLog(LockRemoteIpLoadBalancer.class);
    private static final String LockIp="lock-remote—ip";
    ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    final String serviceId;

    public LockRemoteIpLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.serviceId = serviceId;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
                .getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next()
                .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances, request));
    }

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
                                                              List<ServiceInstance> serviceInstances,
                                                              Request request) {
        Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances, request);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {

        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + serviceId);
            }
            return new EmptyResponse();
        }
        int index = ThreadLocalRandom.current().nextInt(instances.size());

        ServiceInstance instance = instances.get(index);
        //鎖定遠端ip邏輯
        //在請求頭添加想要通路的ip
        if (request instanceof DefaultRequest) {
            DefaultRequest defaultRequest = (DefaultRequest) request;
            if (defaultRequest.getContext() instanceof RequestDataContext) {
                RequestDataContext requestDataContext = (RequestDataContext) defaultRequest.getContext();
                HttpHeaders headers = requestDataContext.getClientRequest().getHeaders();
                if (headers.containsKey(LockIp)){
                    ServiceInstance lockIpInstance = this.chooseLockIpInstance(instances,headers.get(LockIp));
                    instance = lockIpInstance!=null?lockIpInstance:instance;
                }

            }
        }
        return new DefaultResponse(instance);
    }

    private ServiceInstance chooseLockIpInstance(List<ServiceInstance> instances, List<String> strings) {
        //擷取想要通路的ip
        String ip = strings.get(0);
        // 循環所有伺服器,檢視是否有符合規則的服務
        for(ServiceInstance instance:instances){
            if(ip.equals(instance.getHost())){
                return instance;
            }
        }
        return null;
    }
}      
@Configuration
@LoadBalancerClients(
                                   //遠端服務名稱,比如訂單服務、商品服務。如果哪個服務想要使用此規則,在這邊添加即可
        @LoadBalancerClient(name = "order-application", configuration = GatewayConfig.class)
)
public class GatewayConfig {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
                                                                                   LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new LockRemoteIpLoadBalancer(
                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}