What is Nacos Discovery?

Service discovery is one of the most critical components in microservices architecture.

It would be very difficult to manually configure the service list of all service providers for each client, and it would be difficult to dynamically scale the service. Nacos Discovery helps users automatically register services with Nacos servers and dynamically sense and refresh the service list of a service instance.

In addition, Nacos Discovery registers some metadata information about the service instance itself, such as host, port, health check URL, home page, etc. – with Nacos.

For Nacos installation and startup, see Spring Cloud Alibaba: Nacos Installation and Use

This article will detail some technical practices for registering and discovering Nacos Discovery services.

Quick access to the

Add the dependent

Start the Nacos server. Then add dependencies to your project.

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Copy the code

A complete configuration of POM.xml is as follows:


      
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xingtuai.example</groupId>
    <artifactId>nacos-discovery-test</artifactId>
    <version>1.0 the SNAPSHOT</version>
    <name>nacos-discovery-test</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>${spring.boot.version}</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
Copy the code

Note: in the source code of this project, dependencies are managed by the dependencies module, and the dependencyManagement node is also configured in the dependencies module. The above example only demonstrates the necessary configuration items. There are some differences in the source code of this project.

Start the Provider service Provider

In Nacos Discovery service registration and Discovery, there are generally two roles, one is Provider service Provider and the other is Consumer service Consumer. They all need to register themselves with Naocs, a step called service registration. Service providers provide services externally, and service consumers invoke service providers in various ways to complete business functions. And a service can act as both a provider and a consumer.

1. Configuration application. Yml

To use Nacos, you need to configure some basic parameters in either application.yml or bootstrap.yml. As follows:

# Nacos parameters
nacos:
  server-addr: 192.1689.17.: 8848
  username: nacos
  password: nacos
  # namespace, used for environment isolation
  namespace: 904174a3-d51f-43ed-a456-c4fd7386ecb3
  
spring:
  application:
    name: nacos-discovery-provider
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      Nacos.core.auth. enabled=true
      username: ${nacos.username}
      password: ${nacos.password}
      discovery:
        server-addr: ${nacos.server-addr}
        # namespace, used for environment isolation
        namespace: ${nacos.namespace}
        # Group, usually by project
        group: SPRING_CLOUD_EXAMPLE_GROUP
        # Custom metadata
        metadata:
          # version
          version: 1.0
          # region
          region: hangzhou
Copy the code

The above configuration is complete. If you need to simplify the configuration, the configuration is as follows:

spring:
  application:
    name: nacos-discovery-provider
  cloud:
    nacos:
      discovery:
        server-addr: 192.1689.17.: 8848 
Copy the code

2. Enable service discovery

Just add the annotation @enableDiscoveryClient to the project startup class. As follows:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class NacosDiscoveryProviderApplication {
    public static void main(String[] args) { SpringApplication.run(NacosDiscoveryProviderApplication.class, args); }}Copy the code

3. Create a REST interface

Service providers need to provide interfaces for consumers to invoke, typically using RESTful HTTP interfaces. As follows:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping
public class TestController {

    @GetMapping("/echo/{message}")
    public String echo(@PathVariable String message) {
        return "Hello Nacos Discovery "+ message; }}Copy the code

4. Verify

After starting the Provider service Provider, I open the Nacos console and see that the service is registered in the dev namespace because I’m using the dev development environment namespace.

Start the Consumer service for consumers

The configuration of the Consumer’s dependencies is basically the same as that of the Provider service Provider, but a little more complex. The REST service on the Provider side needs to be called on the Consumer side. And there are many ways to call it.

1. The RestTemplate way

The dependency and configuration are consistent with those of the Provider.

Then create the RestTemplate configuration class. As follows:

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(a){
        return newRestTemplate(); }}Copy the code

The Provider service Provider is then invoked. As follows:

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Slf4j
@RestController
@RequestMapping
public class BaseTestController {

    @Resource
    private LoadBalancerClient loadBalancerClient;

    @Resource
    private RestTemplate restTemplate;

    @Value("${spring.application.name}")
    private String appName;

    @GetMapping("/echo")
    public String echoAppName(a) {
        // Use a combination of LoadBalanceClient and RestTemplate to access
        // LoadBalanceClient provides load balancing and gets service instances from Nacos based on service names
        ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-discovery-provider");
        String url = String.format("http://%s:%s/echo/%s", serviceInstance.getHost(), serviceInstance.getPort(), appName);

        log.info("request url: {}", url);
        returnrestTemplate.getForObject(url, String.class); }}Copy the code

Start the project, open a browser requests: http://localhost:8080/echo

View the returned results for verification.

2. Feign way

First you need to add Feign dependencies as follows:

<! -- Spring Cloud Feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Copy the code

After adding dependencies, you need to add the @enableFeignClients annotation to the project startup class to enable functionality. As follows:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class NacosDiscoveryConsumerApplication {
    public static void main(String[] args) { SpringApplication.run(NacosDiscoveryConsumerApplication.class, args); }}Copy the code

You then need to create an interface class, FeignService, that provides access methods for the service interfaces provided by the Provider service Provider. As follows:

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/** * nacos-discovery-provider specifies the service name of the service provider */
@FeignClient("nacos-discovery-provider")
public interface FeignService {

    Provider Specifies the REST interface for the service Provider@param message
     * @return* /
    @GetMapping("/echo/{message}")
    public String echo(@PathVariable String message);
}
Copy the code

Call the FeignService interface in the Controller to implement the call to the Provider side. The code is as follows:

import com.xingtuai.cloud.nacos.discovery.service.FeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@Slf4j
@RestController
@RequestMapping("feign")
public class FeignTestController {

    @Resource
    private FeignService feignService;

    @Value("${spring.application.name}")
    private String appName;

    @GetMapping("/echo")
    public String echoAppName(a) {
        returnfeignService.echo(appName); }}Copy the code

Start the project, open a browser requests: http://localhost:8080/feign/echo

View the returned results for verification.

Multiple environment isolation

Nacos Discovery and Nacos Config have the same environment isolation, distinguished by namespace.

See the previous article: Spring Cloud Alibaba: Nacos Config Configuration Center

In real development, this is a very good way to isolate the various environments and avoid confusion in service management.

Combined with Sentinel fuse

Sentinel is an important component of Ali micro service ecosystem with many functions, which will be briefly introduced here. There will be a special topic for detailed research and practice.

This is combined, primarily, with Feign calls. If the call fails, a circuit breaker can be performed.

First you need to add a dependency, as follows:

<! -- Sentinel -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
Copy the code

Configure in application.yml as follows:

nacos:
  server-addr: 192.1689.17.: 8848
  username: nacos
  password: nacos
  namespace: 904174a3-d51f-43ed-a456-c4fd7386ecb3
sentinel:
  dashboard: 192.1689.17.: 8883
  
spring:
  application:
    name: nacos-discovery-sentinel
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      Nacos.core.auth. enabled=true
      username: ${nacos.username}
      password: ${nacos.password}
      # Nacos service registration and discovery
      discovery:
        server-addr: ${nacos.server-addr}
        namespace: ${nacos.namespace}
        group: SPRING_CLOUD_EXAMPLE_GROUP
    # Spring Cloud Alibaba Sentinel configuration
    sentinel:
      transport:
      	# Sentinel console
        dashboard: ${sentinel.dashboard}

feign:
  # Enable Feign support for Sentinel
  sentinel:
    enabled: true
Copy the code

Create a callback class FeignServiceFallback for the failure of FeignService interface methods:

public class FeignServiceFallback implements FeignService {
    @Override
    public String echo(String message) {
        return "echo fallback, please try again."; }}Copy the code

When accessing the echo(String Message) method fails, this callback will return the data format you want.

In addition to this, we need to create a configuration class FeignConfig as follows:

import com.xingtuai.cloud.nacos.discovery.service.fallback.FeignServiceFallback;
import org.springframework.context.annotation.Bean;

public class FeignConfig {

    @Bean
    public FeignServiceFallback feignServiceFallback(a) {
        return newFeignServiceFallback(); }}Copy the code

Add FeignServiceFallback and FeignConfig to @feignClient and modify the FeignService interface as follows:

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/** * nacos-discovery-provider specifies the service name of the service provider */
@FeignClient(name = "nacos-discovery-provider", fallback = FeignServiceFallback.class, configuration = FeignConfig.class)
public interface FeignService {

    Provider Specifies the REST interface for the service Provider@param message
     * @return* /
    @GetMapping("/echo/{message}")
    public String echo(@PathVariable String message);
}
Copy the code

On normal access, the interface returns normal data, but when the interface fails, such as when the service provider goes offline and the access fails. The echo(String Message) method in FeignServiceFallback will be called and returned. This avoids returning an exception and instead returns a controlled piece of data that can be used as a service circuit breaker.

There is a lot of knowledge and practice related to Sentinel, which will be shared in the future. I will not repeat it here.

Project source code

GitHub – spring-cloud-example

For more technical articles, please visit my blog at JemGeek.com

Click to read the original article