Building nacos based on Spring Cloud Alibaba

Posted by MikeL7 on Tue, 08 Feb 2022 00:05:43 +0100

relationship

Spring Boot is a framework. The various components are gathered together to facilitate the rapid development of web applications.
Spring Cloud is based on Spring Boot and defines a set of specific components, which can facilitate the development of microservice engineering.
Spring Cloud Alibaba has made some adjustments on the basis of Spring Cloud, replacing some components with Alibaba's components, also to facilitate the development of microservice engineering.
In other words, Spring Cloud Alibaba relies on Spring Cloud.
Therefore, if you want to use Spring Cloud Alibaba, you must introduce Spring Cloud based on Spring Boot, and then Spring Cloud Alibaba.
Check the official version Description:

https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

The current content of the graduation version dependency (recommended) is:

Spring Cloud VersionSpring Cloud Alibaba VersionSpring Boot Version
Spring Cloud 2020.0.02021.12.4.2
Spring Cloud Hoxton.SR82.2.5.RELEASE2.3.2.RELEASE
Spring Cloud Greenwich.SR62.1.4.RELEASE2.1.13.RELEASE
Spring Cloud Hoxton.SR32.2.1.RELEASE2.2.5.RELEASE
Spring Cloud Hoxton.RELEASE2.2.0.RELEASE2.2.X.RELEASE
Spring Cloud Greenwich2.1.2.RELEASE2.1.X.RELEASE
Spring Cloud Finchley2.0.4. Release (stop maintenance, upgrade recommended)2.0.X.RELEASE
Spring Cloud Edgware1.5.1. Release (stop maintenance, upgrade recommended)1.5.X.RELEASE

This table indicates the Spring Boot version and Spring Cloud version to be introduced simultaneously when using Spring Cloud Alibaba.
Taking the first behavior in the table as an example, its Spring Cloud Alibaba Versio is 2021.1, then:

  • The version of Spring Cloud Version to be introduced is Spring Cloud 2020.0.0.
  • The version of Spring Boot Version to be introduced is 2.4.2.
  • Generally speaking, this version is more compatible than the version specified downward. However, using the specified version may be more stable.

Father son project

In general, development using spring cloud often uses parent-child projects.
The so-called parent-child project refers to creating a parent project in POM Use < dependencymanagement > in XML to specify the < version > of the component; Then create a sub project under it and modify POM < parent > in XML to point to the parent project, so that the dependencies of the child project will be inherited from the parent project.
In this way, the dependencies can be uniformly managed by the parent project, and the dependencies used by multiple sub projects will be consistent.
The parent project is only responsible for the management of dependencies and does not contain any specific logic.

Create parent project

  1. Click File → New → Project, select Spring Initializr on the left, and click Next after configuration.
  2. Select the Spring Boot version in the upper left corner, and Developer Tools/Lombok and Web/Spring Web are usually selected on the left. Then click Finish.

Such a parent project is created.
Open the POM of the parent project XML, add < dependencymanagement >:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Note that the version is consistent with the dependency (recommended) of the graduation version in the official document.
pom.xml source code:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>alibaba</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>SpringCloudAlibaba</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.1.RELEASE</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.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Where < parent >, multiple attributes under the label indicate the parent project.
The three labels < groupid >, < artifactid > and < version > immediately following < parent > < / parent > indicate the project. These three labels and contents will be copied to the sub project.
Pay special attention to the < packaging > tag, which needs to be set to pom.

Create a subproject.

  1. Open the Project panel, right-click the parent Project and select New → Module.
  2. Select Spring Initializr on the left and click Next after configuration.
  3. Select the Spring Boot version in the upper left corner and nothing on the left. Then click Finish.

So a sub project is created.
Open the POM of the subproject XML, delete the content under its < parent > tag and replace it with the three tags of the parent project mentioned in the previous step.

nacos

Open parent project. POM XML, press Ctrl + left mouse button to click < artifactid > spring cloud Alibaba dependencies < / artifactid >. Spring-cloud-alibaba-dependencies-2.2.1.0 is displayed RELEASE. POM file content.
Search for nacos and nacos discovery respectively to find the following contents:

<nacos.client.version>1.2.1</nacos.client.version>

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  <version>2.2.1.RELEASE</version>
</dependency>

It can be seen that the current version of nacos client required by spring cloud Alibaba is 1.2.1, and the version of spring cloud starter Alibaba nacos discovery is 2.2.1 RELEASE.

nacos client

It is known that the required nacos client version is 1.2.1, so download the corresponding version client from the official website. The nacos on the official website has been divided into 1 X and 2 X two branches, so download 1 X latest version 1.4.2.
After downloading, run nacos.

Subproject

rely on

Open the POM of the subproject XML, in which nacos is introduced:

<dependencies>
    ...

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

Since the parent project is responsible for version management, the child project only needs to be imported without setting the < version > attribute.

set up

The configuration file of the subproject application Set the project name and nacos address in YML.

server:
  port: 8081
spring:
  application:
    name: provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

Generally speaking, the port number of the current sub project will also be specified here.
The service provider must configure spring application. Name and spring cloud. nacos. discovery. Server addr has two properties. However, service consumers can not configure.
Special note: if Ribbon is used, the service name cannot contain.

Service providers and consumers

Create two sub projects, one as a service provider and the other as a service consumer.
Its POM XML and application YML settings are the same, and the above settings are used. Note that the port number needs to be different during debugging.

Service provider

The service provider defines a simple test interface:

@RestController
public class ProviderTestController {

    @RequestMapping("/test")
    String test() {
        return "this is the test string.";
    }
}

Operation engineering. Check the background of nacos and you can see that the service has been successfully registered.

Serving consumers

Service consumers process differently according to different access methods. RestTemplate access is used here.
You can use the Ribbon to automatically load balance. Just introduce the @ LoadBalanced annotation. However, it should be noted that the non Ribbon mode is not compatible with the Ribbon mode. Once the Ribbon is used, the RestTemplate will be processed so that the conventional mode will not be used.

Non Ribbon mode

First, define a Configuration class to register RestTemplate:

@Configuration
public class SpringCloudAlibabaConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Then define the access class:

@RestController
public class ConsumerTestController {
    @Autowired
    private DiscoveryClient discoveryClient;
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/test")
    public String test() {
        // Get all instances under the specified service.
        List<ServiceInstance> serviceInstanceList = this.discoveryClient.getInstances("provider");
        ServiceInstance serviceInstance = serviceInstanceList.get(0);
        String uri = serviceInstance.getUri().toString();
        String url = uri + "/test";

        String r = restTemplate.getForObject(url, String.class);
        return r;
    }
}

Where resttemplate The second parameter of getforobject () indicates the type of return value.
After running, call the /test interface.

Ribbon mode

The above method needs to manually obtain all service instances, and then select one to call. Then you need to splice the interface manually, which is very cumbersome.
The Ribbon will automatically select an instance to call according to the configurable policy, and the splicing interface is no longer required:
Add @ LoadBalanced annotation when registering RestTemplate:

@Configuration
public class SpringCloudAlibabaConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Then define the access class:

@RestController
public class ConsumerTestController {
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/test")
    public String test() {
        String r = restTemplate.getForObject("http://provider/test", String.class);
        return r;
    }
}

The url is in the form of "http: / /" + "service name" + "interface name".
Just visit.

Request parameters

In the above way, RestTemplate forwards the request, which only forwards the url of the request without parameters. To include parameters, the methods vary depending on the request:

get mode

For the get method, you can splice the parameters directly after the url.
For example, to add a parameter value:

String value = "abc123";
String r = restTemplate.getForObject("http://provider/test" + "?value=" + value, String.class);

This passes the value parameter to the service provider.

post mode

For post mode, put the parameters into a LinkedMultiValueMap:

MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
paramMap.add("value", "abc123");
String r = this.restTemplate.postForObject("http://provider/test", paramMap, String.class);

Feign

Feign can replace RestTemplate to forward requests. In fact, the essence of feign is to generate a RestTemplate to forward requests.
The process of using Feign is:

  1. Import dependency:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    Because org. Has been introduced into the parent project springframework. Cloud, so you don't need to specify the version number when introducing dependencies.

  2. Add a comment on the startup class to start Feign:

    @EnableFeignClients
    
  3. Develop Feign client:

    @FeignClient("provider")
    public interface Client {
        @RequestMapping("/test")
        String test();
    }
    

    Of which:

    • @FeignClient("provider") specifies the service name of nacos as provider.
    • @RequestMapping("/test") specifies that the interface to be accessed is / test.
  4. Call:

    @Resource
    Client client;
    
    @RequestMapping("/test")
    public String test() {
        String r = client.test();
        return r;
    }
    

In this way, the request is successful.
Feign passes parameters. You must put the parameters in the interface definition and specify the value attribute. For example:

@FeignClient("provider")
public interface Client {
    @RequestMapping("/testName")
    String testName(@RequestParam(value = "name") String name);

    @RequestMapping("/testUser")
    String testUser(@RequestBody User user);
}

deploy

The above tests are run directly based on IDEA. In the actual environment, both service providers and service consumers need to be deployed to Tomcat. There are two problems in this process:

  • Port problem.
  • Project name problem.

Port problem

A service provider with server configured in the yml file Port, using IDEA, everything works normally. However, after deploying to Tomcat, it is found that the service is not registered with naocs.
This is because the registration class of nacos is bound with a listening event. After listening to the container port, it will register with the registry. However, when using an external container, events cannot be listened to, so the registration fails.
Therefore, the solution is to artificially register the service provider port with the registry when the container is initialized.
Considering that the applications deployed in the container can only be accessed through the port specified by the container, the registered service provider port must be the same as the container port.

@Configuration
public class NacosConfig implements ApplicationContextAware {

    @Autowired(required = false)
    private NacosAutoServiceRegistration registration;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        if (registration != null) {
            try {
                Integer tomcatPort = new Integer(getTomcatPort());
                registration.setPort(tomcatPort);
            } catch (Exception e) {
                e.printStackTrace();
            }
            registration.start();
        }
    }

    /**
     *	Get external tomcat port
     */
    public String getTomcatPort() throws Exception {
        MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
        Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"),
                Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
        String port = objectNames.iterator().next().getKeyProperty("port");
        return port;
    }
}

This configuration is only required by the service provider, but not by the service consumer.

Project name problem

Set the project name of the service provider as myPorject and the interface name as test.
For projects that run directly in IDEA, if server is not set in the configuration file servlet. Context path, then the path accessed by the consumer does not contain the project name. At this time, the path of the access interface is:

http://127.0.0.1:8080/test

However, after deployment to Tomcat, you need to access with the project name. At this time, the path of the access interface is:

http://127.0.0.1:9080/myProject/test

Therefore, the best practice should be to use the project name as part of the interface. That is, after the service consumer obtains the nacos instance, the spliced interface name is no longer / test, but / myProject/test.
At the same time, in order to unify the performance of IDEA debugging and Tomcat, you need to set server in the configuration file of the service provider servlet. Context path is the project name.

other

The registered service address displayed in nacos is incorrect: check the network and Internet of windows, open the adapter option, and see if there are other network cards, such as those used by virtual machines or wall climbing software. Just close it.

Topics: Spring Boot Spring Cloud Nacos