Nacos源码学习系列服务端第7篇服务查询之订阅

目录

服务查询流程图

服务查询请求入口

服务查询处理(InstanceOperatorClientImpl)

 服务订阅处理(EphemeralClientOperationServiceImpl )

 发布事件(ClientSubscribeServiceEvent )

 总结


服务查询流程图

服务查询请求入口

    @GetMapping("/list")
    @Secured(action = ActionTypes.READ)
    public Object list(HttpServletRequest request) throws Exception {
        //获取命名空间id
        String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, 
               Constants.DEFAULT_NAMESPACE_ID);

        //得到服务名及服务名校验 这里不赘述 前面几篇已经讲解过多次了
        String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        NamingUtils.checkServiceNameFormat(serviceName);
        
        String agent = WebUtils.getUserAgent(request);
        String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY);

        //获取clientIp 和 udpPort 方便给客户端推送消息
        String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY);
        int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0"));
        boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, 
                "healthyOnly", "false"));
        String app = WebUtils.optional(request, "app", StringUtils.EMPTY);
        //构建订阅者
        Subscriber subscriber = new Subscriber(clientIP + ":" + udpPort, agent, app, 
               clientIP, namespaceId, serviceName, udpPort, clusters);
        //查询服务实例 
        //这里 getInstanceOperator() 得到的是 InstanceOperatorClientImpl 类型对象
        return getInstanceOperator().listInstance(namespaceId, 
            serviceName, subscriber, clusters, healthyOnly);
    }

 服务订阅 Subscriberl


//服务订阅者
public class Subscriber implements Serializable {
    ...    
    //订阅费的ip和端口
    //比如udp推动的话就是客户端的ip:udp端口号
    private String addrStr;

    //客户端sdk的版本号 包括客户端的语言比如java及版本号比如1.1.4等信息
    private String agent;
    
    //可以为空 客户端标识
    private String app;
    
   //客户端ip
    private String ip;
    //客户端端口比如udp 端口
    private int port;
    //命名空间默认是public
    private String namespaceId;
    //带有组名的服务名
    private String serviceName;
    //集群名称可以为空
    private String cluster;
    ...
}

服务查询处理(InstanceOperatorClientImpl)

public class InstanceOperatorClientImpl implements InstanceOperator {
  @Override
    public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber 
          subscriber, String cluster,  boolean healthOnly) {
        //构建service
        Service service = getService(namespaceId, serviceName, true);
        // 判断客户端sdk是否支持推送功能
        if (subscriber.getPort() > 0 && pushService.canEnablePush(subscriber.getAgent())) {
            String clientId = IpPortBasedClient.getClientId(subscriber.getAddrStr(), true);
            //创建客户端这里不在赘述创建客户端的逻辑需要了解客户端的朋友异步到前面
            //的客户端章节
            createIpPortClientIfAbsent(clientId);
            //EphemeralClientOperationServiceImpl 完成服务注册功能
            clientOperationService.subscribeService(service, subscriber, clientId);
        }
       
        //查询服务信息包括所有的实例列表
        ServiceInfo serviceInfo = serviceStorage.getData(service);
        //查询服务的元数据信息  
        ServiceMetadata serviceMetadata = 
                metadataManager.getServiceMetadata(service).orElse(null);
       //对实例列表根据集群名称/是否可用/是否健康/自定义过滤器/阈值保护层曾过滤
        ServiceInfo result = ServiceUtil
                .selectInstancesWithHealthyProtection(serviceInfo, serviceMetadata, 
                   cluster, healthOnly, true, subscriber.getIp());
        // adapt for v1.x sdk
        result.setName(NamingUtils.getGroupedName(result.getName(), 
                  result.getGroupName()));
        return result;
    }
}

createIpPortClientIfAbsent(clientId);

//创建客户端的逻辑跟之前服务注册Client的创建逻辑是一样,读者可以移步去服务注册系列专门讲解Client的章节学习
接下来我们看看EphemeralClientOperationServiceImpl 是如何完成服务注册的

 服务订阅处理(EphemeralClientOperationServiceImpl )


public class EphemeralClientOperationServiceImpl implements ClientOperationService {

    @Override
    public void subscribeService(Service service, Subscriber subscriber, String clientId) {
        //首先从服务管理器中去查询服务 查询不到 就用当前的
        Service singleton = 
                ServiceManager.getInstance().getSingletonIfExist(service).orElse(service);

        //从客户端管理器获取客户端对象
        Client client = clientManager.getClient(clientId);
        //校验逻辑很简单 1、是否为空 2、是否是临时节点
        if (!clientIsLegal(client, clientId)) {
            return;
        }
        //客户端自己为服务的订阅者 
        client.addServiceSubscriber(singleton, subscriber);
        client.setLastUpdatedTime();
        //发布客户端订阅事件
        NotifyCenter.publishEvent(new ClientOperationEvent.ClientSubscribeServiceEvent(singleton, clientId));
    }
}


public abstract class AbstractClient implements Client {
    protected final ConcurrentHashMap<Service, InstancePublishInfo> publishers = new 
         ConcurrentHashMap<>(16, 0.75f, 1);
    
    protected final ConcurrentHashMap<Service, Subscriber> subscribers = new 
         ConcurrentHashMap<>(16, 0.75f, 1);
 
 //该客户端的subscribers订阅者记录里面加一个记录
 @Override
    public boolean addServiceSubscriber(Service service, Subscriber subscriber) {
        if (null == subscribers.put(service, subscriber)) {
            MetricsMonitor.incrementSubscribeCount();
        }
        return true;
    }
}

 事件是订阅的核心就是 1、创建一个客户端并维护健康检查任务   2、给该客户端的订阅列表加入该服务的订阅记录 3、发布 ClientOperationEvent.ClientSubscribeServiceEvent 事件

接下来我们看下ClientSubscribeServiceEvent 这个事件的监听者是如果处理事件的

通过前面讲解服务注册事件的时候已经知道了订阅事件的处理是在 ClientServiceIndexesManager 这个类里面,我们看下是怎么处理的

 发布事件(ClientSubscribeServiceEvent )

public class ClientServiceIndexesManager extends SmartSubscriber {

   ....
    
    private void addSubscriberIndexes(Service service, String clientId) {
        subscriberIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());
        // 在订阅注册表加一条记录 并发布服务订阅事件
        if (subscriberIndexes.get(service).add(clientId)) {
            NotifyCenter.publishEvent(new ServiceEvent.ServiceSubscribedEvent(service, 
              clientId));
        }
    }

   ....
}

 这里的操作很简单给服务注册表加一个注册记录,同时发布 ServiceEvent.ServiceSubscribedEvent 事件,这个事件的处理在【

Nacos源码学习系列服务端第4篇服务注册服务变更事件】篇章中已经讲解过,该事件发出后由一个延时任务推送消息给该客户端,推送的消息内容是什么跟客户端又是怎么交互的,后面专门开篇讲解。

到此为止服务订阅逻辑是完毕了,剩下的是服务实例的查找。

 总结

 为了坚持文章短小精悍、易读易懂的原则,关于服务查询我们分2篇,本片是服务订阅,下一篇我们讲解剩下的部分,服务查询和过滤,敬请期待。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值