Calling services with Nacos discovery

更新时间:
复制 MD 格式

For high-QPS, high-traffic services such as image services, calling services through a VPC direct connection significantly improves performance and reduces latency. However, this method does not provide load balancing across multiple service instances. If you already use Nacos in your microservices architecture, you can integrate it with EAS to manage service access and enable client-side load balancing. This topic explains how to call an EAS service by associating it with a Nacos instance.

Important

Nacos integration does not currently support intelligent routing for LLMs.

How it works

  1. After you configure a VPC for an EAS service, the system assigns a VPC IP address to each pod of the service.

  2. EAS registers the pods of the inference service with the associated Nacos instance.

  3. The client subscribes to service registration events from Nacos.

  4. Nacos pushes changes to the EAS service's IP address list to the client.

  5. The client uses an SDK to select an instance. The SDK includes a built-in weighted round-robin (WRR) load balancing algorithm.

  6. The client uses the IP address and port returned by the SDK to call the service.

Configuration methods

Console configuration

On the deployment configuration page, in the Network Information section, configure the VPC and enable Service Discovery Nacos.

  1. Configure the VPC. To associate with Nacos, you must configure a VPC, vSwitch, and security group for the EAS service. If you have not created them, see Create and manage a VPC and Manage security groups.

    • The Nacos instance must be in the same VPC and vSwitch as the EAS service.

    • Ensure that the vSwitch's CIDR block has enough available IP addresses.

      Important

      Network access between the client (such as an ECS instance) and the EAS service instances is controlled by security group rules.

  2. Enable Service Discovery Nacos and select a Nacos instance from the dialog box that appears. If you do not have one, Create an MSE registry instance first.

    • You can associate multiple Nacos instances with a single service for disaster recovery. The service is registered with all associated instances.

JSON configuration

The following JSON parameters are required to associate a Nacos instance. For more information about each parameter, see Configure in the console.

Parameter

Description

cloud

networking

vpc_id

The IDs of the VPC, vSwitch, and security group for the EAS service.

vswitch_id

security_group_id

networking

nacos

nacos_id

The ID of the Nacos instance that you created.

Example configuration:

{
    "cloud": {
        "networking": {
            "security_group_id": "sg-*****",
            "vpc_id": "vpc-***",
            "vswitch_id": "vsw-****"
        }
    },
    "networking": {
        "nacos": [
            {
                "nacos_id": "mse_regserverless_cn-****"
            }
        ]
    }
}

Verify service registration

After the service is deployed, EAS registers it with the associated Nacos instance. The registration information is as follows:

  • cluster: Defaults to the DEFAULT cluster.

  • namespace: The default public namespace.

  • serviceName: Your service name.

  • groupName: A system-generated value: pai-eas.

Verify that the service information is correct:

挂载Nacos

Verify that the number of instances and their status are correct:

Nacos实例状态

Calling from a client

Go

 package main

import (
	"fmt"
	"github.com/nacos-group/nacos-sdk-go/v2/clients"
	"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
	"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
	"github.com/nacos-group/nacos-sdk-go/v2/model"
	"github.com/nacos-group/nacos-sdk-go/v2/util"
	"github.com/nacos-group/nacos-sdk-go/v2/vo"
	"strconv"
	"testing"
	"time"
)

var Clients = make(map[string]NacosClientManager)

type NacosClientManager struct {
	userId   string
	endPoint string
	client   naming_client.INamingClient
}

func NewAndGetNacosClient(userId string, endPoint string) (*NacosClientManager, error) {
	key := generateKey(userId, endPoint)
	client, exists := Clients[key]

	if exists {
		return &client, nil
	}

	client, exists = Clients[key]
	if exists {
		return &client, nil
	}

	newClient, err := clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig: constant.NewClientConfig(
				constant.WithNamespaceId(""),
				constant.WithTimeoutMs(5000),
				constant.WithNotLoadCacheAtStart(true),
				constant.WithLogDir("/tmp/nacos/log"),
				constant.WithCacheDir("/tmp/nacos/cache"),
				constant.WithLogLevel("debug"),
			),
			ServerConfigs: []constant.ServerConfig{
				*constant.NewServerConfig(endPoint, 8848, constant.WithContextPath("/nacos")),
			},
		},
	)
	if err != nil {
		return nil, err
	}
	nacosClient := NacosClientManager{
		userId:   userId,
		endPoint: endPoint,
		client:   newClient,
	}
	Clients[key] = nacosClient
	return &nacosClient, nil
}

func (p *NacosClientManager) SelectOneHealthyInstance(param vo.SelectOneHealthInstanceParam) (*model.Instance, error) {
	instance, err := p.client.SelectOneHealthyInstance(param)
	if err != nil {
		return nil, fmt.Errorf("SelectOneHealthyInstance failed: %v", err)
	}
	fmt.Printf("SelectOneHealthyInstance success, param: %+v\n", param)
	return instance, nil
}

func (p *NacosClientManager) Subscribe(service string, group string) error {
	subscribeParam := &vo.SubscribeParam{
		ServiceName: service,
		GroupName:   group,
		SubscribeCallback: func(services []model.Instance, err error) {
			fmt.Printf("callback return services:%s \n\n", util.ToJsonString(services))
		},
	}
	return p.client.Subscribe(subscribeParam)
}

func generateKey(userId string, endPoint string) string {
	return userId + fmt.Sprintf("-%s", endPoint)
}

func Test(t *testing.T) {

	nacosClient, err := NewAndGetNacosClient("yourAliyunUid", "yourNacosEndpoint")
	if err != nil {
		panic(err)
	}

	nacosClient.Subscribe("your_service", "pai-eas")

	params := vo.SelectOneHealthInstanceParam{
		ServiceName: "your_service",
		GroupName:   "pai-eas",
	}
	
	instance, err := nacosClient.SelectOneHealthyInstance(params)
	fmt.Println(instance)

    // Replace the host with the IP address and port from your invocation information in the console.
	url := fmt.Sprintf("http://%s:%s/api/predict/xxxxxxx", instance.Ip, strconv.FormatUint(instance.Port, 10))
	fmt.Println(url)

    // TODO: Invoke the service by using the URL.
}    

Python

Note

The official Nacos Python SDK lacks a built-in load balancing algorithm, so you must implement your own, such as weighted round-robin.

Install dependencies

pip install nacos-sdk-python

Call the service

# -*- coding: utf8 -*-
import nacos

# Nacos server configuration
SERVER_ADDRESSES = "yourNacosEndpoint"
NAMESPACE = ""  # Use default namespace if empty
SERVICE_NAME = "your_service"
GROUP_NAME = "pai-eas"

# Create Nacos client
client = nacos.NacosClient(SERVER_ADDRESSES, namespace=NAMESPACE)


def callback(args):
    print(args)


if __name__ == '__main__':
    # Correctly pass the callback function as a list
    client.add_config_watchers(SERVICE_NAME, "pai-eas", [callback])

    # Fetch and print naming instance details
    b = client.list_naming_instance(SERVICE_NAME, None, None, GROUP_NAME, True)
    print(b)
    if b"hosts":
        print("ip", b["hosts"][0]["ip"])
        ip = b["hosts"][0]["ip"]
        port = b["hosts"][0]["port"]
        # Replace the host with the IP address and port from your invocation information in the console.
        url = f"http://{ip}:{port}/api/predict/xxxxxxx"
        print(url)
     # TODO: Invoke the service by using the URL.        

Java

Add the Maven dependency

<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>2.3.2</version>
</dependency>

Call the service

package test;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;



import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.AbstractEventListener;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;

public class NacosTest {
    public static void main(String[] args) {
        try {
            String serverAddr = "yourNacosEndpoint:8848";
            NamingService namingService = NacosFactory.createNamingService(serverAddr);

            ExecutorService executorService = Executors.newFixedThreadPool(1);
            EventListener serviceListener = new AbstractEventListener() {
                @Override
                public void onEvent(Event event) {
                    if (event instanceof NamingEvent) {
                        System.out.println(((NamingEvent) event).getServiceName());
                        System.out.println(((NamingEvent) event).getGroupName());
                    }
                }

                @Override
                public Executor getExecutor() {
                    return executorService;
                }
            };
            namingService.subscribe("your_service", "pai-eas", serviceListener);
            Instance instance = namingService.selectOneHealthyInstance("your_service", "pai-eas");
            System.out.println(instance.getIp());
            System.out.println(instance.getPort());
            // Replace the host with the IP address and port from your invocation information in the console.
            String url = String.format("http://%s:%d/api/predict/xxxxxxx", instance.getIp(), instance.getPort());
            System.out.println(url);
            
            // TODO: Invoke the service by using the URL.
            
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }
}