基于Spring Cloud Alibaba搭建nacos

关系

Spring Boot是框架。将各个组件集合在一起,方便快速开发web应用。
Spring Cloud基于Spring Boot,限定了一组特定的组件,从而可以方便地进行微服务工程的开发。
Spring Cloud Alibaba在Spring Cloud的基础上进行了一些调整,将某些组件替换为阿里巴巴的组件,同样是为了方便微服务工程的开发。
也就是说,Spring Cloud Alibaba依赖于Spring Cloud。
故而,想要使用Spring Cloud Alibaba,必须在Spring Boot的基础上引入Spring Cloud,然后引入Spring Cloud Alibaba。
查看官方的版本说明:

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

其中的 毕业版本依赖关系(推荐使用) 当前内容为:

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(停止维护,建议升级)2.0.X.RELEASE
Spring Cloud Edgware1.5.1.RELEASE(停止维护,建议升级)1.5.X.RELEASE

该表指明了使用Spring Cloud Alibaba时,要同时引入的Spring Boot版本与Spring Cloud版本。
以表中第一行为例,其Spring Cloud Alibaba Versio为2021.1,那么:

  • 需引入Spring Cloud Version的版本为Spring Cloud 2020.0.0。
  • 需引入Spring Boot Version的版本为2.4.2。
  • 一般来说版本向下兼容,因此可以使用比该指定更高的版本。但使用指定版本可能更稳定。

父子工程

一般地,使用SpringCloud开发往往会使用父子工程。
所谓父子工程,指的是先创建一个父工程,在pom.xml中用<dependencyManagement>指定组件的<version>;然后在其下创建子工程,修改pom.xml中的<parent>来指向父工程,这样子工程的依赖将从父工程继承。
这样即可将依赖统一划归父工程管理,多个子工程使用的依赖将保持一致。
父工程仅仅负责对依赖的管理,不包含任何具体的逻辑。

创建父工程

  1. 点击File→New→Project,在左侧选择Spring Initializr,配置好后点Next。
  2. 左上角选择Spring Boot版本,左侧通常选择Developer Tools/Lombok,以及Web/Spring Web。然后点击Finish。

这样一个父工程就创建好了。
打开父工程的pom.xml,添加<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>

注意其中的版本与官方文档中的 毕业版本依赖关系(推荐使用) 保持一致。
pom.xml源码:

<?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>

其中的<parent>,该标签下的多个属性指明了父工程。
<parent></parent>结束后紧跟的<groupId><artifactId><version>3个标签则指明了本工程。这3个标签及内容会复制到子工程中。
特别注意其中的<packaging>标签,需要将其设置为pom

创建子工程。

  1. 打开Project面板,在父工程上点右键,选择New→Module。
  2. 在左侧选择Spring Initializr,配置好后点Next。
  3. 左上角选择Spring Boot版本,左侧什么也不要选。然后点击Finish。

这样一个子工程就创建完毕了。
打开子工程的pom.xml,将其<parent>标签下的内容删除,更换为上一步提到的父工程的3个标签。

nacos

打开父工程的pom.xml,按下Ctrl+鼠标左键来点击<artifactId>spring-cloud-alibaba-dependencies</artifactId>。此时会显示 spring-cloud-alibaba-dependcies-2.2.1.RELEASE.pom 文件内容。
在其中分别搜索 nacosnacos-discovery,可以找到内容:

<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>

从而可知当前spring-cloud-alibaba所需的nacos客户端版本为1.2.1,spring-cloud-starter-alibaba-nacos-discovery版本为2.2.1.RELEASE

nacos客户端

已知所需nacos客户端版本为1.2.1,因此从官网下载对应版本客户端。官网的nacos目前已分为1.x与2.x两个分支,因此下载1.x最新版本1.4.2
下载后,运行nacos。

子工程

依赖

打开子工程的pom.xml,在其中引入nacos:

<dependencies>
    ...

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

由于父工程负责版本管理,因此子工程只需要引入即可,不需要再设置<version>属性。

设置

需要在子工程的配置文件application.yml中设置工程名与nacos地址。

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

一般来说也会在该处指定当前子工程的端口号。
服务提供者必须配置spring.application.namespring.cloud.nacos.discovery.server-addr两个属性。但服务消费者可以不配置。
特别注意:若使用Ribbon,则服务名不能包含_

服务提供者与消费者

创建2个子工程,一个作为服务提供者,另一个作为服务消费者。
其pom.xml与application.yml设置相同,都使用上述设置。注意调试时端口号需要有所区别。

服务提供者

服务提供者定义一个简单的测试接口:

@RestController
public class ProviderTestController {

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

运行工程。查看nacos后台,可以看到服务已顺利注册。

服务消费者

服务消费者根据访问方式不同,处理也不同。这里使用RestTemplate访问。
可以使用Ribbon来自动负载均衡,仅需引入@LoadBalanced注解。但需注意的是非Ribbon方式Ribbon方式并不兼容,一旦使用了Ribbon,则RestTemplate会被处理,以至于常规方式将无法使用。

非Ribbon方式

首先定义一个Configuration类来注册RestTemplate:

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

然后定义访问类:

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

    @RequestMapping("/test")
    public String test() {
        // 获取指定服务下所有的实例。
        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;
    }
}

其中RestTemplate.getForObject()的第二个参数指明了返回值的类型。
运行后,调用/test接口即可。

Ribbon方式

上述方法,需要人为获取所有服务实例,然后选择一个来调用。然后还需要人为拼接接口,非常烦琐。
Ribbon会自动按可配置策略选择一个实例来调用,且不再需要拼接接口:
注册RestTemplate时添加@LoadBalanced注解:

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

然后定义访问类:

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

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

其url使用"http://" + "服务名" + "接口名"的形式。
访问即可。

请求传参

上述方式RestTemplate转发请求,仅仅转发了请求的url,不包含参数。若要包含参数,根据请求的不同,方式不同:

get方式

对于get方式,直接将参数拼接在url后即可。
例如,要增加一个参数value

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

这样即可将value参数传给服务提供者。

post方式

对于post方式,需将参数放入一个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可替换RestTemplate,用于转发请求。实际上,Feign的本质就是生成一个RestTemplate来转发请求。
使用Feign的流程为:

  1. 引入依赖:

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

    由于在父工程中已引入了org.springframework.cloud,故而引入依赖时不需要指定版本号。

  2. 在启动类上添加注解开启Feign

    @EnableFeignClients
    
  3. 开发Feign客户端:

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

    其中:

    • @FeignClient("provider")指定了nacos的服务名为provider
    • @RequestMapping("/test")指定了要访问的接口为/test
  4. 调用:

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

这样即可请求成功。
Feign传参,必须将参数放在接口定义中,并指明value属性。例如:

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

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

部署

上述测试,都是基于IDEA直接运行。而实际环境中,无论服务提供者还是服务消费者,都需要部署到Tomcat。在此过程中会遇到两个问题:

  • 端口问题。
  • 工程名问题。

端口问题

一个服务提供者,在yml文件中配置了server.port,使用IDEA运行一切正常。然而部署到Tomcat后,发现服务没有注册给naocs。
这是因为nacos的注册类绑定了一个监听事件,监听到容器端口后即会向注册中心注册。然而,当使用外部容器时,不能监听到事件,因此注册失败。
故而,解决方案为:容器初始化时人为地将服务提供者端口注册给注册中心。
考虑到部署在容器内的应用,必须通过容器指定的端口才能访问,因此注册的服务提供者端口必须与容器端口相同。

@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();
        }
    }

    /**
     *	获取外部tomcat端口
     */
    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;
    }
}

该配置仅服务提供者需要,服务消费者是不需要的。

工程名问题

设服务提供者的工程名为myPorject,接口名为test
在IDEA中直接运行的工程,若没有在配置文件中设置server.servlet.context-path,那么消费者访问的路径是不包含工程名的。此时访问接口的路径为:

http://127.0.0.1:8080/test

然而部署到Tomcat后,需带工程名访问,此时访问接口的路径为:

http://127.0.0.1:9080/myProject/test

因此,最佳实践应该是将工程名作为接口的一部分。即服务消费者获取到nacos实例后,拼接的接口名不再是/test,而是/myProject/test
同时,为了IDEA调试与Tomcat的表现统一,需在服务提供者的配置文件中设置server.servlet.context-path为工程名。

其他

nacos中显示的已注册服务地址不对:查看windows的网络和Internet,打开适配器选项,看是否有其他网卡,例如虚拟机或翻墙软件所使用的虚拟网卡。关闭即可。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值