基于Spring Cloud、Nacos搭建微服务项目

·  阅读 2632
基于Spring Cloud、Nacos搭建微服务项目

一、准备注册中心(Nacos)

1、Nacos简介

  Nacos是阿里推出的微服务注册中心以及配置中心的中间件。Nacos项目地址github.com/alibaba/nac…,我们可以从git拉下项目,并使用mvn编译打包,下面将讲解如何打包启动Nacos。

2、Nacos编译、打包及运行

Ⅰ、拉取项目

  直接在终端输入:git clone https://github.com/alibaba/nacos.git

Ⅱ、切换到一个稳定版本的分支

  查看项目的Tags,此时显示1.3.2是最后发行的版本,所以我们需要将本地代码切换到这个1.3.2这个标签,进入项目根目录输入命令:git checkout -b 1.3.2 1.3.2

Ⅲ、使用mvn编译打包项目

  在项目根目录输入打包命令: mvn -Prelease-nacos -DskipTests clean install -U -Drat.skip=true

Ⅳ、启动Nacos

  打包成功后,在目录\nacos\distribution\target\会生成nacos-server-1.3.2.zipnacos-server-1.3.2.tar.gz,我们只需要将他拷贝到部署目录解压即可。然后在终端进入解压后的bin目录输入命令:startup.cmd -m standalone,即可完成启动。

Ⅴ、启动效果

  终端启动后,显示8848端口,并content path 是‘/nacos’,此时表示nacos启动成功了。如下图:   当我们访问http://127.0.0.1:8848/nacos/#/login ,可以得到下面登录界面:   然后输入用户名密码(nacos/nacos)即可到服务管理界面:   可以看到界面上有配置管理、服务管理等功能菜单,至此nacos就启动成功了。nacos的管理端使用起来非常方便,在界面上就能完成服务配置的增删改查,对服务提供者与订阅者的管理也是可视化操作。

3、Nacos其他配置

Ⅰ、配置DB实现数据持久化

  在nacos下的conf下有一个application.properties文件,相信大家对这个文件并不陌生,这就是项目的配置文件,数据库等相关的配置都在里面。如果我们想使用数据库保存nacos的数据,就可以修改这个文件中的下面代码:

...
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
### 数据库用户名
db.user=root
### 数据库密码
db.password=1315977663
...
复制代码

  然后我们在创建一个nacos数据库,并执行conf下的nacos-mysql.sql脚本。会得到下图几张表:
  数据库准备好了之后,我们重启一下nacos,并在管理界面增加一个配置,看看数据是否存入了记录。

二、搭建各个模块

  注册中心Nacos准备好后就可以开始搭建项目模块了,我这里就拿一个简单的项目为例,创建用户模块、订单模块、通用的父模块、项目root模块。其中用户模块充当服务的生产者,订单模块为订阅者(二者是互相调用,身份也是相互的),让二者通过注册中心进行分布式服务访问;而通用父模块则是所有模块的父依赖,用于配置所有模块都需要的通用mvn依赖、以及统一管理各个依赖的版本;最后项目root模块则是项目的根目录,会导入所有项目里的子模块,进行统一的打包编译、管理。除此之外还有服务网关模块、流程管理模块等。项目目录如下:

1、root模块(ccm-root)

  root模块主要作用是统一管理、打包编译项目里的各个模块,让项目子模块有条理的组织在一起。该模块也是项目的根目录。构件方法比较简单,只需要穿件一个空文件夹,里面增加如下pom文件:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.fbl</groupId>
    <artifactId>ccm-root</artifactId>
    <version>1.0</version>
    <name>ccm-root</name>
    <description>根项目</description>
    <packaging>pom</packaging>

    <modules>
    	<!--用户模块-->
        <module>ccm-user</module>
        <!--订单模块-->
        <module>ccm-order</module>
        <!--通用父类依赖模块-->
        <module>ccm-parent</module>
        <!--服务网关模块-->
        <module>ccm-gateway</module>
        <!--流程管理模块-->
        <module>ccm-process</module>
    </modules>

</project>
复制代码

  XML文件中modules下的module节点内容就是项目里的各个模块的名称,这么组织项目结构还有一个好处,在idea导入项目时所有的模块都可以自动导入到编辑器。

2、通用父模块(ccm-parent)

  通用父模块主要作用是引入其他模块都需要的依赖、统一管理项目里依赖版本。项目里该模块是其他所有模块的父模块(parent),子模块会自动继承他引入的依赖,并且可以定义依赖的版本让所有模块依赖版本保持一致。这个模块的构建pom如下:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.fbl</groupId>
    <artifactId>ccm-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ccm-parent</name>
    <packaging>pom</packaging>
	
    <!-- 定义版本属性,后面去的版本都从这里取 -->
    <properties>
        <java.version>1.8</java.version>
        <!-- https://start.spring.io/actuator/info 对应的版本信息在这个网站找 -->
        <spring.cloud.version>Hoxton.SR8</spring.cloud.version>
        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
    </properties>
    
    <!-- dependencies 里引入的依赖,在子模块都会继承过去。这里一般加入的是每个模块都需要的依赖 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
        </dependency>
    </dependencies>

    <!-- dependencyManagement 里引入的依赖,在子模块不会自动继承过去 -->
    <!-- ,而是需要在子模块再次引入,但是在子模块不需要指定版本号了。 -->
    <!-- 这里一般做的事情是管理依赖版本,把项目里所有依赖在这里指定版本,子模块引入时无需指定版本就可以保持版本一致。 -->
    <dependencyManagement>
        <dependencies>
        	<!-- 这个依赖可以保证子模块所有org.springframework.cloud 相关的依赖版本保持一致 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- 这个依赖可以保证子模块所有com.alibaba.cloud 相关的依赖版本保持一致 -->
            <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>
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-engine</artifactId>
                <version>6.0.0</version>
            </dependency>
            <dependency>
                <groupId>org.activiti</groupId>
                <artifactId>activiti-spring</artifactId>
                <version>6.0.0</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.3</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.23</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.21</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
复制代码

  如上面pom文件里的注释代码所述:
    dependencies 里引入的依赖,在子模块都会继承过去,这里一般加入的是每个模块都需要的依赖;
    dependencyManagement 里引入的依赖,在子模块不会自动继承过去,而是需要在子模块再次引入,但是在子模块不需要指定版本号了。 这里一般做的事情是管理依赖版本,把项目里所有依赖在这里指定版本,子模块引入时无需指定版本就可以保持版本一致。
  有了这里依赖文件后,只需要在项目里创建一个文件夹,并把该文件放入其中,然后在ccm-root根模块的pom文件的module节点下加入<module>ccm-parent<module>。这样我们就完成了通用父模块的构建。

3、订单模块(ccm-order)

  之前的两个模块的构建都是为了后面构建模块铺路,从订单模块开始就是真正的有义务含义的模块了。订单模块的构建我们将学会如何在nacos注册服务、如何使用nacos里的配置。下面就从pom文件开始讲解如何构建这个模块。

Ⅰ、创建pom文件

<?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>com.fbl</groupId>
        <artifactId>ccm-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <artifactId>ccm-order</artifactId>
    <name>ccm-order</name>
    <description>订单模块</description>

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

        <!-- 引入nacos配置中心依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <!-- 引入web服务依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- 引入mybatis依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
		
        <!-- 引入druid数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        
        <!-- 引入mysql连接驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <!-- 引入springboot 打包插件。能将所有的依赖打成一个jar包 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
复制代码

  如上面pom文件里的注释代码所述:
    * 所有的依赖我们没有指定版本,因为这个模块的指定的<parent>模块已经指定好了版本。
    * 类似spring-boot-starter、spring-boot-starter-test、lombok等通用的依赖没有在这个模块引入,但是任然可以在该模块正常使用(后面你会看见他们的使用)。这是因为这个模块会自动继承父模块的依赖。
    * spring-cloud-starter-alibaba-nacos-discovery的作用就是加入nacos服务发现与注册的依赖。
    * spring-cloud-starter-alibaba-nacos-config的作用就是加入nacos配置中心的依赖。
    * 其他依赖的说明在上面代码里都加了注释。

Ⅱ、创建模块的目录

  目录接口如下图:

  其实项目的结构和Spring Boot项目查不多。其中:
    provider放的是对外提供的服务接口;

Ⅲ、将服务注册到Nacos

  在application.yml文件中加入注册中心的地址配置、应用名称配置。具体配置如下:

server:
  port: 8081
spring:
  application:
  	# 应用名称,会在注册中心中显示
    name: order
  cloud:
    nacos:
      discovery:
      	# 注册中心地址
        server-addr: 127.0.0.1:8848
      config:
      	#配置中心地址。
        server-addr: ${spring.cloud.nacos.discovery.server-addr}

复制代码

  在SpringBoot启动类上加@EnableDiscoveryClient注解开启服务注册与发现功能,代码如下:

package com.fbl.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
// 开启服务注册与发现功能
@EnableDiscoveryClient
@MapperScan(basePackages = "com.fbl.order.mapper")
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
复制代码

  编写一个服务提供者(OrderProviderTest.java)。只需要在写一个RestController,我们就可以将服务注册到Nacos中。代码如下:

package com.fbl.order.provider;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("orderProviderTest")
// 动态刷新配置注解 配置相关后面会介绍到
@RefreshScope
public class OrderProviderTest {
	// 配置相关后面会介绍到
    @Value("${user.name}")
    private String name;
    // 配置相关后面会介绍到
    @Value("${user.age}")
    private String age;
    
    @GetMapping("test")
    public String test () {
        return "order 服務的返回的數據" + age + name;
    }
}
复制代码

  说到这里提一下Spring Cloud是基于Http协议实现的微服务之间的调用,所有我们注册服务的时候和平时写Web接口时类似;而我们Dubbo是基于RPC协议实现的微服务之间的调用,RPC协议通俗来讲是将参数序列化后调用远程方法,然后将返回结果序列化后返回给调用者,基于这一特性Dubbo注册服务是注册的Services类,这类似于往Spring容器中注册一个bean。

  至此Spring Cloud向Nacos注册服务就完成了,我们启动Order模块和Nacos看看效果吧。

  order模块启动后控制台的效果:

  打开注册中心看看:

  我们访问下order模块的服务试试:
  这里还没看到服务之间的调用过程,不要着急,等我们创建用户模块是就会讲解到呢。

Ⅳ、使用并管理Nacos的配置

  微服务中一个比较重要的概念就是注册中心,注册中心的职责就是统一管理服务的配置,降低微服务的运维成本。我们Nacos默认就集成了配置中心,并能够很好的在管理界面修改更新配置。下面就来试试怎么使用Nacos的配置中心。
  在application.yml文件中加入注册中心地址的配置,即spring.cloud.nacos.config.server-addr。在之前配置注册中心时已经展示过代码这里就不贴代码了。   在application.yml里面加上默认的初始配置如下:

user:
  username: zhangsan
  age: 654
复制代码

  在我们的OrderProviderTest服务提供类中使用配置,代码如下:

package com.fbl.order.provider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("orderProviderTest")
// RefreshScope注解是开启动态刷新配置,如果不加这个,在配置中心更改配置后,这里面的配置不会刷新。
@RefreshScope
public class OrderProviderTest {
	// 读取user.username的值
    @Value("${user.username}")
    private String name;

    @Value("${user.age}")
    private String age;

    @GetMapping("test")
    public String test () {
        return "order 服務的返回的數據" + age + name;
    }

}
复制代码

  启动order模块,看看配置的效果:
  在nacos增加一个配置,让项目里的配置发生改变,操作如下图:
  在nacos更新配置好,我们再看看服务的效果,发现username和age都已经发生了改变:
  至此配置中心的使用也演示完成,有兴趣的读者还可以继续研究下配置的多环境切换。

4、用户模块(ccm-user)

  现在开始构建用户模块(ccm-user),用户模块会调用订单模块的服务,这里我们将学习到两个服务之间的调用。

Ⅰ、创建pom文件

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.fbl</groupId>
        <artifactId>ccm-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ccm-user</artifactId>
    <name>ccm-user</name>
    <description>用户模块</description>

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

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        
        <!-- 相比order,user多了这个依赖,用于服务发现与调用 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
复制代码

  相比order,user多了spring-cloud-starter-openfeign依赖,用于服务发现与调用。

Ⅱ、构建user模块的目录


  其实项目的结构和order项目查不多。其中:
    consumer放的是调动其他服务的消费者;

Ⅲ、编写UserConsumerTest消费其他服务

  user项目的配置文件以及启动类的注解和order模块是差不错,这里就不重复讲解了。我们就直接展示服务消费者的编写过程,其具体代码如下:

package com.fbl.user.consumer;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;

// 注册到spring容器
@Service
// 调动名称为order模块的服务接口
@FeignClient(name = "order")
public interface UserConsumerTest {
	// 调用orderProviderTest/test服务
    @RequestMapping("orderProviderTest/test")
    String orderProviderTestRemote();
}
复制代码

  代码中可以看到我们是通过@FeignClient(name = "order"),@RequestMapping("orderProviderTest/test"),定位到order模块的orderProviderTest/test。这样就成为一个orderProviderTest/test服务的消费者,并注册到Spring容器中方便其他类调用。

Ⅳ、使用UserConsumerTest

  现在我们只需要编写一个服务,将UserConsumerTest注入,就可以使用这个order模块的服务。具体操作如下:

package com.fbl.user.provider;
import com.fbl.user.consumer.UserConsumerTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("userProviderTest")
public class UserProviderTest {
    @Autowired
    private UserConsumerTest userConsumerTest;
    @GetMapping("test")
    public String test() {
        return "用户模块调用:" + userConsumerTest.orderProviderTestRemote();
    }
}
复制代码

  我们现在调用下user模块的服务,看能不能调用到order模块的服务,效果如下:
  很明显我们已经拿到了订单模块服务返回的数据了,说明服务之间的调用已经可以了。

三、总结

  基于Spring Cloud、Nacos搭建微服务项目到这里就已经完成了,上面所有的代码都在gitee.com/fangbl/cclo…。 微服务的解决方案有很多,但是核心的内容总是这几个(注册中心,配置中心,服务网关,熔断器,服务生产者,服务消费者)。在众多的解决方案中alibaba提供的Spring Cloud Alibaba还是挺不错的(已经是Spring Cloud官方网站推荐的方案之一),其中Nacos注册中心就是其中重要的组件,Nacos不仅支持Spring Cloud同样也支持Dubbo,有兴趣的朋友可以在Dubbo项目中使用Nacos。后面我还会出一期在微服务项目相关的帖子,主要讲述构建流程引擎模块、构建服务网关、使用熔断器等。

分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改