Microservices -- Spring Cloud Alibaba 02 (Nacos)

The last one explained the relationship and concepts between them. This one is about practical operation

1, Configuration in Nacos service registration

1. Overview:

         In microservices, the first problem we need to face is how to find services (software is a service), and the second is how to communicate between different services? How to manage each service in the application better and more conveniently, and how to establish the link between various services, so that the registration center was born (for example, Taobao sellers provide services and buyers invoke services).
The commonly used registration centers in the market include zookeeper (Yahoo Apache),Eureka(Netfix),Nacos(Alibaba),Consul(Google), etc. we use Nacos

2. Features:

         Nacos (dynamic naming and configuration service) is a platform applied to service registration, discovery and configuration management. It was incubated in Alibaba and grew up in the peak test of the double 11 in the past decade, precipitating the core competitiveness of simplicity, ease of use, stability and reliability and excellent performance.

3. Installation and use:

3.1. Download and unzip
Download addresshttps://github.com/alibaba/nacos/releases 3.2 database establishment

         There is a nacos-mysql.sql script file under the conf file, which is executed in the database. This SQL script file is incomplete. You need to manually create a library and execute it under the library

Database version: mysql version is required to be greater than 5.7 (MariaDB is better than 10.5.11)

3.3 configuration file configuration

         Open the default configuration in / conf/application.properties, configure the database to be connected based on your current environment, and the user name and password to be used when connecting to the database (if there is "#" in front, remove it)

3.4 start up test

Start: standalone stands for stand-alone mode, not cluster mode

Linux/Unix/Mac startup commands

./startup.sh -m standalone

Windows startup command

startup.cmd -m standalone

Premise:

  1. When executing the execution command, either configure the environment variable or execute it directly in the nacos/bin directory
  2. When nacos starts, Java needs to be configured in the local environment variable_ Home (corresponding to the jdk installation directory),
  3. Make sure that the database you connect to (nacos_config) exists

Access services:

Default address

http://localhost:8848/nacos

Default password: nacso/nacos

Add script button in IDEA to start:

 

2, Project creation  

1. Create aggregate parent project

Structure:

  Addition of pom file dependency:

    <!--maven Single inheritance and multi-layer inheritance are supported, so the import method is only available pom To import-->
    <groupId>com.cy</groupId>
    <artifactId>01-sca</artifactId>
    <!--When you create a child project of a parent project, the packaging method of the parent project automatically changes to pom mode-->
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <!--When creating a subproject, the name of the subproject is automatically placed in the modules lower-->
    <modules>
        <module>sca-provider</module>
        <module>sca-consumer</module>
        <module>sca-gateway</module>
    </modules>
    <!--properties Some attribute elements are defined in,The dynamic definition version number is as follows
        <version>${spring-boot-version}</version>-->
    <properties>
        <spring-boot-version>2.3.2.RELEASE</spring-boot-version>
    </properties>
    <!--maven The parent project of is pom engineering
    This project is mainly responsible for the management of dependent versions and some basic dependencies-->
    <dependencyManagement>
        <!--spring boot rely on-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot-version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
            <!--Spring Cloud rely on-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR9</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
            <!--Spring Cloud Alibaba rely on-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.6.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <!--Here's this dependencies Element to define the dependencies required by all child projects-->
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <!--The compile phase is valid and the run phase is invalid-->
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. Create sub project

Concept: both producers and consumers are relative, and there is no absolute consumer and producer

2.1 producer service creation registration

Create a sub project producer service SCA provider under the parent project

2.1.1. Addition of POM dependency

<!--Engineering module coordinates-->
    <artifactId>sca-provider</artifactId>
    <!--establish maven Built in module, used to specify maven Middle pair java The compiled and running version of the class-->
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--web Service dependency-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--nacos Service registration and discovery dependencies-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--nacos Configuration center dependency( nacos.io)-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    </dependencies>

2.1.2. Create and configure the bootstrap.yml file

server:  #Create Producer Services Registration
  port: 8081 #Service port
spring:
  application:
    name: sca-provider   #service name
  cloud:
    nacos: #When the service starts, a heartbeat packet will be sent to nacos (once every 5s)
      discovery: #Registration and discovery of services
        server-addr: localhost:8848  #nacos server

2.1.3. Create startup class definition code

@SpringBootApplication
public class ProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }

    /**Define the Controller object (this object is defined as handler in spring mvc),
     * Process client requests based on this object*/
    @RestController
    public class ProviderController{
        //@Value reads the content configured in the project configuration file by default
        //8080 is the default value given when the value of server.port is not read
        @Value("${server.port:8080}")
        private String server;
        //Echo echo
        //Rest is a software architecture coding style based on which URLs can be defined
        //http://localhost:8081/provider/echo/tedu
        @GetMapping("/provider/echo/{msg}")
        public String doRestEcho1(@PathVariable String msg){
            return server+":"+msg;
        }
    }
  }

2.1.4. View service

Start the service, enter the service list of Naocs and view it. If the service name is displayed, the registration is successful

  2.2 consumer service creation registration

The first two steps are the same as that of the consumer. Note that the port number should not be repeated

2.2.1 create startup class definition code

@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
    /*When the startup class starts, the RestTemplate IOC is handed over to spring for management
    * Subsequent remote calls through this object*/
    //restTemplate needs to be added when calling the service of the service provider, and it is managed by spring
    @Bean//Don't forget the startup class annotation @ SpringBootApplication contains the configuration class annotation, which itself is a large configuration class
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    @RestController
    public class ConsumerController{

        @Value("${spring.application.name}")
        private String appName;
        @Autowired
        private RestTemplate restTemplate;
        @GetMapping("/consumer/doRestEcho1")
        public String doRestEcho01(){
        //The API URL provided by the calling service provider is also an API
        //1. Define the API to call
            String url = "http://localhost:8081/provider/echo/"+appName;
            System.out.println("request url:"+url);
        //2. Who will access this API
           return restTemplate.getForObject(url, String.class);
        }
    }
}

2.2.2 viewing services

Start the service, enter the service list of Naocs and view it. If the service name is displayed, the registration is successful

 

Question: now it's in the form of service invocation. A service instance can process requests is limited. In a certain peak period, when all other services call a service server, there will be downtime. How should we deal with it?

3. Load balancing call service

3.1 direct load balancing call service

Inject LoadBalancerClient class

        @Autowired
        private LoadBalancerClient loadBalancerClient;

Get the service instance through the registry to invoke the service

    //Load balancing mode call
    @GetMapping("consumer/doRestEcho2")
    public String doRestEcho2(){
        //1. Get service instances from the layer registry
        ServiceInstance instance = loadBalancerClient.choose("sca-provider");
        //2. Service instance object based on RestTemplate
        String ip = instance.getHost();
        int port = instance.getPort();
        String url=String.format("http://%s:%s/provider/echo/%s", ip,port,appName);
        return restTemplate.getForObject(url, String.class);
    }

  3.2 testing

Open two service producer services, access them through consumers and output port numbers

As shown in the figure, load balancing is really realized  

  Thinking 1: why can load balancing be achieved by calling the service instance obtained from the registry?

A: the default implementation of load balancing here is because Nacos integrates ribbon. Ribbon, together with RestTemplate, can easily access services. Ribbon is one of the core components of Spring Cloud, and the most important function it provides is load balancing on the client (the client can use certain algorithms, such as polling access to access server instance information) , this function allows us to easily automatically convert service-oriented REST template requests into client-side load balancing service calls.

Thinking 2: after a long time of use, we find that the first few steps of the current call are similar. Can we encapsulate them and call them directly?

3.3 simple load balancing call service

Add the Bean of RestTemplate in the startup class again and add the annotation @ LoadBalanced

    @Bean
    @LoadBalanced
    public RestTemplate loadBalancerRestTemplate(){
        return new RestTemplate();
    }

Inject

        @Autowired
        private RestTemplate loadBalancerRestTemplate;

Call the service directly through the service name

    @GetMapping("consumer/doRestEcho3")
    public String doRestEcho3(){
        //Define url
        //Save the three steps of getting the service instance and getting the ip and port number of the service in the registry. Just write the service name directly. Remember to call with the Bean annotated with @ LoadBalanced
        String url=String.format("http://sca-provider/provider/echo/%s", appName);
        //Service call
        return loadBalancerRestTemplate.getForObject(url, String.class );
    }

Underlying implementation:  

         RestTemplate will be intercepted by loadbalancerinceptor when sending a request. It is used for load balancing of RestTemplate. Loadbalancerinceptor gives the core logic of load balancing to loadBalancer. The @ LoadBalanced annotation belongs to spring, not the Ribbon. When spring initializes the container, if it detects that the Bean is annotated by @ LoadBalanced, Spring will set the interceptor of loadbalancerinceptor for it.

@LoadBalanced function: describes the RestTemplate object, which is used to tell the Spring framework that when a service call is made using resttempalt, the call process will be intercepted by an interceptor, and then start the load balancing policy inside the interceptor.

Ribbon: the load balancing client provided by Netflix is generally used for service consumption

Ribbon load balancing policy: the default is polling policy. When the load balancing policy provided by the system cannot meet our needs, we can also define our own policy based on IRule interface

3.4 Feign's remote service call -- key points

summary:

         When the service consumer requests the service of the service provider based on rest, a direct way is to splice the url, splice the parameters, and then implement the service call. However, this splicing is required for each service call. The amount of code is complex and difficult to maintain. At this time, feign was born. Feign is a declarative Web service client. The bottom layer encapsulates the application of rest technology. Feign can simplify the call and implementation of remote service provider methods by service consumers

1. Add dependency on the service consumer

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

2. Add @ EnableFeignClients annotation on the startup class

@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {...}

3. Create a service package, define the Http request API, and access the remote service with OpenFeign based on this API

@FeignClient(name="sca-provider")//Service name of remote call
public interface RemoteProviderService {
    @GetMapping("provider/echo/{msg}")//Service path
    public abstract String echoMessage(@PathVariable("msg") String msg);
}

Note: the annotation @ PathVariable must be added because it can be omitted after jdk8, but Feign is the version of jdk5 for forward compatibility

4. Create a Controller package for testing

@RestController
@RequestMapping("/consumer")
public class FeignConsumerController {
    @Autowired
    private RemoteProviderService remoteProviderService;
    /*Perform remote service call*/
    /*browser->consumer->provider*/
    /*Built in load balancing*/
    @GetMapping("/echo/{msg}")
    public String doRestEcho(@PathVariable String msg)  {
        return remoteProviderService.echoMessage(msg);
    }
}

5. Test

  Test successful

4.Feign configuration advanced practice

Question 1: a service provider usually provides many resource services. The service consumer writes many service call interfaces based on the same service provider. At this time, if the contextId is not specified, the service startup will fail. For example, if the service consumer adds another interface as follows, the consumer will fail when starting,

Solution: at this time, we need to specify a contextId for the remote calling service interface as the unique ID of the remote calling service

@FeignClient(name="sca-provider",contextId = "remoteProviderService",)
public interface RemoteProviderService {
    @GetMapping("provider/echo/{msg}")
    public abstract String echoMessage(@PathVariable("msg") String msg);
}

Question 2: when we call a remote service, what if the called service is suddenly unavailable or the calling process times out? As a developer, we can't expose the error in front of the user, so our general service consumer will give a specific fault-tolerant scheme

solve:

1. create a class to solve the problem, implement the FallbackFactory interface, rewrite the create method inside, give the solution in the method, and note that the generic type is the name of Feign interface

@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
    /*org.slf4j.Logger It is the log specification of java, which defines a set of interfaces. The implementation of the interface is log4f and logback (fast)*/
//    private static Logger log= LoggerFactory.getLogger(ProviderFallbackFactory.class);// For the facade design pattern, there is no need to write this sentence with Slf4j annotation
    @Override
    public RemoteProviderService create(Throwable throwable) {
        //Mode 1:
//        return new RemoteProviderService() {
//            @Override
//            public String echoMessage(String msg) {
//                /*Service regulation*/
//                return "the service is busy, wait a minute";
//            }
//        };
        //Mode 2:
        //With the help of lambda expression in jdk8
//        Return (MSG) - > {/ / lambda expression (an expression in jdk)
//            return "the service is busy, wait a minute";
//        };
        //Mode 3:
        return msg -> "The service is busy, wait a minute";
    }
}

Note: the first method is prototype, and the latter two are short expressions of lambda

2. Apply FallbackFactory = the class name of the solution when the problem occurs in Feign provider

@FeignClient(name="sca-provider",contextId = "remoteProviderService",fallbackFactory = ProviderFallbackFactory.class)
public interface RemoteProviderService {
    @GetMapping("provider/echo/{msg}")
    public abstract String echoMessage(@PathVariable("msg") String msg);

  3. Add the following configuration in the configuration file application.yml to start the service interrupt processing mechanism during feign mode call

feign:  
  hystrix:
    enabled: true #The default value is false

4. Test

Add a delay code to the service producer to simulate an access error and access the address to view the phenomenon

  Test successful

summary

technological process:

  1. Tell springcloud through the @ EnableFeignCleints annotation to start the Feign Starter component.
  2. Feign Starter registers the global configuration during project startup, scans the interface described by @ FeignClient annotation under the package, then creates the interface implementation class (JDK proxy class) at the bottom of the system, constructs the class object, and then submits it to spring management (registers the IOC container).
  3. When the interface is called, it is intercepted by the dynamic proxy class logic. The @ FeignClient Request information is generated into a Request object through the encoder, and remote procedure calls are made based on this object.
  4. The request object performs load balancing through the Ribbon and selects a healthy Server instance.
  5. The Client carries the Request and calls the remote service to return the Request response.
  6. The decoder generates a Response to return to the client, and parses the information flow into interface return data.

Key knowledge:

  1. Why use Feign? (based on Feign, you can implement service invocation more friendly and simplify service consumer's invocation of service provider's methods).
  2. @What is the function of FeignClient annotation? (tell Feign Starter to create an implementation class proxy class for the interface described in this annotation when the project is started)
  3. How is the underlying load balancing implemented for Feign mode calls? (Ribbon)
  4. @What is the function of the EnableFeignCleints annotation? (describe the configuration class, such as the startup class)

Tags: Spring Cloud

Posted on Tue, 21 Sep 2021 00:23:16 -0400 by Ryodox