一、前言

nacos是第二代微服务SpringCloudAlibaba开源的一款注册中心和分布式配置中心组件,其功能上为第一代微服务SpringCloudEurekaConfig的结合体。简而言之,Nacos可以实现分布式服务注册与发现和分布式配置中心功能。

二、完整项目示例

在搭建项目实战之前,先贴一下本篇博客的项目源码完整目录 :

微服务调用事务问题 微服务调用方式_RestTemplate

三、Nacos与Eureka区别

上面有提到过,阿里巴巴开源的nacos组件可替代eurekaconfig,那么nacoseureka相比,有哪些不同与相同呢?说到注册中心nacoseureka之间的区别,不得不谈谈基于微服务与分布式领域中的CAP定律和BASE理论, , 大家在想了解nacoseureka这两个注册中心之间的区别的时候,建议先看看这篇文章,那么我这里就先简单介绍下CAP定律:

1. CAP理论

  • C(一致性): 各个节点数据必须统一保持一致;
  • A(可用性) : 保证服务的高可用 必须返回响应结果
  • P(分区容错) : 网络分区可能会造成脑裂问题,部分server与集群其他节点失去联系 无法组成一个群体;

不能三者兼顾 ,只能在三者之间实现取舍 : CP、AP、CA

2. 区别

  • nacos最大的不同就是它同时支持APCP模式,而Eureka只支持AP
  • nacos既可以做为注册中心又可以担任配置中心,做配置中心时nacos客户端与nacos服务端保持长链接,服务端会有监听,一旦nacos服务端有配置信息更新变化,立马推送到客户端,然后更新到客户端的JVM内存中。
  • nacos在集群环境下,一旦leader宕机了,会重新选出一个leader,其他节点都为Follower,期间不影响服务之间互相调用、不影响服务注册、不影响服务正常启动读取配置文件。

四、Nacos-discovery默认支持Ribbon负载均衡

为了保持好奇心,特地分析了一下SpringCloud常见的注册中心组件: EurekaConsulnacos等,默认都是集成并开启了Ribbon负载均衡,本节为nacos主题,我们分析下nacos的服务发现组件,不难看出,他也是默认集成并且支持ribbon负载均衡的。

微服务调用事务问题 微服务调用方式_Nacos_02

五、环境要求

注意: 在后续的文章中,我们全部使用以下环境来搭建学习第二代服务教程时的环境。

1. 版本说明

  • spring-cloud-dependencies.version: Spring Cloud Hoxton.SR3
  • spring-cloud.version: 2.2.2.RELEASE
  • spring-cloud-alibaba-version: 2.2.0.RELEASE
  • spring-boot-version: 2.2.5.RELEASE
  • nacos-version: 1.1.4

2. 版本要求

微服务调用事务问题 微服务调用方式_分布式_03

2. Nacos的安装

nacos的安装和部署、启动都可参照nacos官方文档,Windwos平台直接下载解压,双击bin目录下面的startup.cmd启动即可。

初始访问URL: 127.0.0.1:8848/nacos/index.html

微服务调用事务问题 微服务调用方式_Rest消费服务_04

六、环境搭建

以订单(Order)调用商品(Product)为例,分别创建父工程springcloud-alibaba-nacos、子工程springcloud-alibaba-providerspringcloud-alibaba-consumer

1. 搭建springcloud-alibaba-nacos

  • pom.xml
<modules>
        <module>springcloud-alibaba-provider</module>
        <module>springcloud-alibaba-consumer</module>
    </modules>
    <packaging>pom</packaging>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--Spring Cloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Spring Cloud Alibaba-->
            <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>

2. 搭建springcloud-alibaba-provider服务提供者

  • pom.xml
<parent>
        <groupId>com.thinkingcao</groupId>
        <artifactId>springcloud-alibaba-nacos</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>/</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <name>springcloud-alibaba-provider</name>
    <artifactId>springcloud-alibaba-provider</artifactId>

    <dependencies>
        <!-- web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!--排除tomcat依赖 -->
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--undertow容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <!--nacos服务注册与发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- lombok插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • application.yml
server:
  port: 8715

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        enabled: true
  application:
    name: nacos-product
  • 启动类AppProvider
package com.thinkingcao.nacos;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @desc:
 * @author: cao_wencao
 * @date: 2020-04-16 22:54
 */
@SpringBootApplication
public class AppProvider {
    public static void main(String[] args) {
        SpringApplication.run(AppProvider.class);
    }
}
  • Product提供接口
package com.thinkingcao.nacos.api;

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.RestController;

/**
 * @desc:    生产者——>商品服务
 * @author: cao_wencao
 * @date: 2020-04-17 20:58
 */
@Slf4j
@RestController
public class ProductController {

    @Value("${server.port}")
    private String port;

    @GetMapping("/getProductMsg")
    public String getProductMsg() {
        log.info("开始调用商品服务信息啦");
        return "我是商品服务, 调用商品服务接口成功了===>> : " + port;
    }
}

2. 搭建springcloud-alibaba-consumer服务消费者

  • pom.xml
<parent>
        <artifactId>springcloud-alibaba-nacos</artifactId>
        <groupId>com.thinkingcao</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>/</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-alibaba-consumer</artifactId>

    <dependencies>
        <!--nacos服务注册与发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!--排除tomcat依赖 -->
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--undertow容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <!-- lombok插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • application.yml
server:
  port: 8710
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        enabled: true
  application:
    name: nacos-order
  • 启动类AppConsumer
package com.thinkingcao.nacos;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @desc:
 * @author: cao_wencao
 * @date: 2020-04-16 22:55
 */
@SpringBootApplication
public class AppConsumer {
    public static void main(String[] args) {
        SpringApplication.run(AppConsumer.class);
    }
}
  • Order消费商品的接口(3种方式)
package com.thinkingcao.nacos.api;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * @desc:  消费者——>订单服务
 * @author: cao_wencao
 * @date: 2020-04-17 20:58
 */
@Slf4j
@RestController
public class OrderController {
    
    @Autowired(required = false)
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private DiscoveryClient discoveryClient;

    //方式一 :通过IP地址
    @GetMapping("/getProductMsg")
    public String getProductMsg() {
        String url = "http://127.0.0.1:8715/getProductMsg";
        String response = restTemplate.getForObject(url, String.class);
        log.info("response==>: {}",response);
         return response;
    }

    //方式二 :通过服务名(这种方式不可行,在Eureka中可以)
    @GetMapping("/getProductMsg2")
    public String getProductMsg2() {
        String url = "http://nacos-product/getProductMsg";
        String response = restTemplate.getForObject(url, String.class);
        log.info("response==>: {}",response);
        return response;
    }

    //方式三 :通过LoadBalancerClient获取服务调用地址进行调用
    @GetMapping("/getProductMsg3")
    public String getProductMsg3() {
        //discoveryClient.getInstances()
        ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-product");
        String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/getProductMsg");
        String response = restTemplate.getForObject(url, String.class);
        log.info("response结果==>>>: {}", response);
        return response;
    }

    //方式四 :通过LoadBalancerClient获取服务调用地址进行调用
    @GetMapping("/getProductMsg4")
    public String getProductMsg4() {
        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("nacos-product");
        //任意选择一个,实现本地RPC调用
        ServiceInstance serviceInstance = serviceInstanceList.get(0);
        String response = restTemplate.getForObject(serviceInstance.getUri()+ "/getProductMsg", String.class);
        log.info("response结果==>>>: {}", response);
        return response;
    }

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

}

七、基于RestTemplate测试

使用nacos做注册中心时,服务消费有两大类,一类是RestTemplate,另一类是Feign客户端,其中RestTemplate具体实现服务消费有3种方式

微服务调用事务问题 微服务调用方式_Nacos_08