Nacos注册中心11-Server端(处理服务发现请求)

0. 环境

  • nacos版本:1.4.1
  • Spring Cloud : 2020.0.2
  • Spring Boot :2.4.4
  • Spring Cloud alibaba: 2.2.5.RELEASE

测试代码:github.com/hsfxuebao/s…

本文将解析下nacos的服务发现源码实现,nacos支持两种服务发现方式,一种是直接去nacos服务端拉取某个服务的实例列表,就像eureka那样定时去拉取注册表信息,另一种是服务订阅的方式,就是订阅某个服务,然后这个服务下面的实例列表一旦发生变化,nacos服务端就会使用udp的方式通知客户端,并将实例列表带过去。在本文中我们主要先看下 直接拉取服务实例列表的实现与订阅,对于订阅服务实例列表发生变化,nacos服务端通知订阅的客户端这部分代码我们在下篇详细介绍。

1. 直接拉取方式

1.1 直接拉取客户端源码

Properties properties = new Properties();
properties.setProperty("serverAddr", "10.0.8.247:8825");
properties.setProperty("namespace","public");

NamingService naming = NamingFactory.createNamingService(properties);
naming.registerInstance("userService", "11.11.11.11", 8888, "DEFAULT");
List<Instance> userService = naming.getAllInstances("userService");
复制代码

上面这段代码,先是创建一个NamingService ,接着注册了一个服务实例,最后是调用了getAllInstances方法获取某个服务的实例列表。

我们来看下这个getAllInstances 方法实现:

@Override
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters,
        boolean subscribe) throws NacosException {

    ServiceInfo serviceInfo;
    // 是否订阅
    if (subscribe) {
        // 订阅
        serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName),
                StringUtils.join(clusters, ","));
    } else {
        // 不进行订阅
        serviceInfo = hostReactor
                .getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName),
                        StringUtils.join(clusters, ","));
    }
    List<Instance> list;
    if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) {
        return new ArrayList<Instance>();
    }
    return list;
}
复制代码

前面一堆重载方法,就是设置默认的groupcluster 我们跳过去,直接到这个参数最全的方法来,判断是否订阅,到这里默认是订阅的,也就是subscribe =true, 不过可以指定,我们就先看下这个不订阅的是怎么实现的吧,调用了getServiceInfoDirectlyFromServer 这个方法,从方法名上也能看出来,就是直接从server上获取serviceInfo:

public ServiceInfo getServiceInfoDirectlyFromServer(final String serviceName, final String clusters)
        throws NacosException {
    String result = serverProxy.queryList(serviceName, clusters, 0, false);
    if (StringUtils.isNotEmpty(result)) {
        return JacksonUtils.toObj(result, ServiceInfo.class);
    }
    return null;
}
复制代码

这里直接是调用了serverProxy组件的queryList 方法,需要主要的是这个udp端口是0 ,因为咱们这里是不订阅的,这个upd是给订阅的接收通知用的:

public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly)
        throws NacosException {

    final Map<String, String> params = new HashMap<String, String>(8);
    params.put(CommonParams.NAMESPACE_ID, namespaceId);
    params.put(CommonParams.SERVICE_NAME, serviceName);
    params.put("clusters", clusters);
    // udp端口
    params.put("udpPort", String.valueOf(udpPort));
    params.put("clientIP", NetUtils.localIP());
    params.put("healthyOnly", String.valueOf(healthyOnly));
    // 发起请求/nacos/v1/ns/instance/list GET请求
    return reqApi(UtilAndComs.nacosUrlBase + "/instance/list", params, HttpMethod.GET);
}
复制代码

这个方法就是封装请求参数,udpPort,clientIP,healthyOnly 这几个参数需要注意下,接着调用reqApi 选择server 发送请求了,请求uri是 /nacos/v1/ns/instance/list请求方法是get。后面的选择server发送请求我们就不看了,在服务注册,服务发现里面我们看了无数次了。

1.2 直接拉取服务端处理源码

InstanceController 的list 方法是处理这个请求的,我们来看下:

@GetMapping("/list")
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
public ObjectNode list(HttpServletRequest request) throws Exception {
    // 从请求中获取各种属性
    String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
    String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
    NamingUtils.checkServiceNameFormat(serviceName);
    // agent属性用于指定提交请求的客户端是哪种类型
    String agent = WebUtils.getUserAgent(request);
    String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY);
    String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY);
    // 获取到client的端口号,后续UDP通信会使用
    int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0"));
    String env = WebUtils.optional(request, "env", StringUtils.EMPTY);
    boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false"));

    String app = WebUtils.optional(request, "app", StringUtils.EMPTY);

    String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY);

    boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false"));
    // todo 对请求进行详细处理
    return doSrvIpxt(namespaceId, serviceName, agent, clusters, clientIP, udpPort, env, isCheck, app, tenant,
            healthyOnly);
}
复制代码

上面这一堆就是解析参数,我们不过多关注,主要看下doSrvIpxt这个方法。方法太长了我们分段看:

public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String clusters, String clientIP,
        int ud
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值