问题

微服务和航显设备通过socket连接,微服务端管理socket的连接,用户从web发送指令到航显设备执行。
如果用户发送的指令到一台没有soket连接的微服务,那么控制指令将无法到达航显设备。

解决思路

用户发送的请求通过 spring cloud gateway负载,自定义负载均衡,发送请求的时候带航显设备连接的微服务ip,自定义负载根据ip去选择转发的服务器,这样请求就不会到达没有连接的微服务

自定义负载均衡全局过滤器 LoadBalancerClientFilter

根据自动配置规则,如果用户定义了LoadBalancerClientFilter ,就使用用户定义的

package com.cares.gateway.filter;

import com.cares.gateway.rules.FidsLoadBalanceRule;
import com.google.common.collect.Maps;
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.gateway.route.Route;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import javax.annotation.Resource;
import java.net.URI;
import java.util.Map;

import static com.cares.gateway.consts.GatewayConstant.FIDS_CLIENT_KEY;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;

/**
 * @author wangcj
 * @desc
 * @date 2020/12/24 12:05
 **/
@Component
public class CustomLoadBalancerClientFilter extends LoadBalancerClientFilter {


    @Resource
    FidsLoadBalanceRule fidsLoadBalanceRule;


    public CustomLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
        super(loadBalancer, properties);
    }


    /**
     * fids 模块向航显屏发送指令
     * 1、航显屏必须在线
     * 2、请求URL要带$FIDS_CLIENT=host 参数
     * host是client当前连接到的主机ip,连接时记录到数据库
     *
     * @param exchange
     * @return
     */
    @Override
    protected ServiceInstance choose(ServerWebExchange exchange) {
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
        Object hint = null;
        //这里使用userId做为选择服务实例的key
        if (route.getId().equals("acdm-fids")) {
           String host = exchange.getRequest().getQueryParams().getFirst(FIDS_CLIENT_KEY);
            Map<String, Object> map = Maps.newHashMap();
            map.put(FIDS_CLIENT_KEY, host);
            hint=map;
        }

        return fidsLoadBalanceRule.choose(serviceId, hint);
    }
}

自定义规则

package com.cares.gateway.rules;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.cloud.client.ServiceInstance;
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.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;

import static com.cares.gateway.consts.GatewayConstant.FIDS_CLIENT_KEY;

/**
 * @author wangcj
 * @desc
 * @date 2020/12/24 11:30
 **/
@Component
public class FidsLoadBalanceRule extends AbstractLoadBalancerRule {

    @Resource
    SpringClientFactory springClientFactory;

    private ILoadBalancer lb;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    /**
     * fids 模块向航显屏发送指令
     * 1、航显屏必须在线
     * 2、请求URL要带$FIDS_CLIENT=host 参数
     * host是client当前连接到的主机ip,连接时记录到数据库
     *
     * @param o
     * @return
     */
    @Override
    public Server choose(Object o) {

        List<Server> servers = lb.getReachableServers();

        if (CollectionUtils.isEmpty(servers)) {
            return null;
        }

        if (null!=o && o instanceof Map) {
            Map<String,Object> map = (Map<String, Object>) o;
            String host = (String) map.get(FIDS_CLIENT_KEY);
            Optional<Server> optional = servers.stream().filter(s -> s.getHost().equals(host)).findAny();
            if (optional.isPresent()) {
                return optional.get();
            }
        }

        Random random = new Random(System.currentTimeMillis());
        return servers.get(random.nextInt(servers.size()));
    }

    public ServiceInstance choose(String serviceId, Object hint) {
        this.lb = springClientFactory.getLoadBalancer(serviceId);

        Server server = choose(hint);
        ServerIntrospector serverIntrospector = springClientFactory.getInstance(serviceId, ServerIntrospector.class);
        Map map = serverIntrospector.getMetadata(server);
        return new RibbonLoadBalancerClient.RibbonServer(serviceId, server, isSecure(server, serviceId), map);

    }

    private boolean isSecure(Server server, String serviceId) {

        ServerIntrospector serverIntrospector = springClientFactory.getInstance(serviceId, ServerIntrospector.class);
        IClientConfig config = springClientFactory.getClientConfig(serviceId);
        return RibbonUtils.isSecure(config, serverIntrospector, server);
    }
}



SPRINGCLOUD     

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!