Spring Cloud 搭建微服务

概述

框架版本

框架名称 版本号
Spring Boot 2.2.7.RELEASE
Spring Cloud Hoxton.SR6
Spring Cloud Alibaba 2.2.6.RELEASE

基础依赖

依赖名称 说明
spring-boot-starter-actuator 提供微服务的监控支持
spring-cloud-starter-alibaba-nacos-discovery 提供基于 Nacos 的注册中心支持,完成服务的注册与发现。
spring-cloud-starter-alibaba-nacos-config 提供基于 Nacos 的配置中心支持,完成服务的配置文件管理。

基础支撑环境

基础支撑环境需要开发者自行安装,安装方法请参考其他文档或网络上的一些资料。

环境 版本 说明
Nacos 2.0.2 由阿里云提供的开源微服务引擎,主要提供微服务的注册、发现、配置、Bus等功能。
Sentinel Dashboard 1.8.2 由阿里云提供的 Sentinel 控制台,为开发者实现限流、降级等操作提供可视化界面。

业务微服务搭建

业务微服务是为分布式系统提供业务支持,是开发者编写代码最多也是最频繁的服务。

创建微服务项目

打开 Intellij IDEA,找到File -> New -> Project,会打开如下窗口:

image-20210728105704118.png

点击下一步,开始填写项目的基本信息,我们以创建用户服务为例,配置如下:
image-20210728105932961.png

如果不了解各个配置项的含义,请先学习 Maven,然后再来看此文档。配置完成后点击Finish完成项目创建。创建完成后,项目的目录结构如下:
image-20210728110432580.png

创建必须的文件和包

创建配置文件

src/main/resources目录下分别创建application.yamlbootstrap.yaml配置文件,application 和 bootstrap 的区别就是 bootstrap 的加载优先于 application,并且 bootstrap 中的配置不能被本地属性覆盖。因此,当我们使用配置中心来管理配置时 bootstrap.yaml必须创建。当然,如果我们所有的想要将所有的配置都依托于配置中心来管理,我们也可以只创建一个bootstrap.yaml配置文件。

总结:一些公共属性,在运行期可以通过程序来修改的配置在application.yaml文件中,如:业务参数等。而作为引导加载的一些不可覆盖的属性配置在bootstrap.yaml文件中,如:数据库配置、加解密的密钥等。不过一般情况下建议,可以修改的属性采用数据库来存储,比如建立一个系统参数表。

创建基础包和启动类

基础包是当前微服务所使用包的根,后续所有创建类和子包都包含在根包下。找到src/main/java目录,创建一个名为com.cloud的根包,一般情况下除了遵循域名倒写的规范外,尽量保证名称与pom.xmlgroupId一致,增强代码的可移植性。

在根包com.cloud下创建一个AppStart.java的类作为启动类,注意:启动的类的名称不要太过于奇葩,能够见名知意即可,另外,所有微服务的启动类名称最好保持统一,这样便于团队协作时减少沟通。

配置相关依赖

打开项目根目录下的pom.xml文件,配置所需的依赖 jar 包和 maven 的相关插件。

基本的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cloud</groupId>
    <artifactId>cloud-user-service</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <!-- pom 文件的属性配置 -->
    <properties>
    </properties>

    <!-- Spring Boot 版本控制(统一SpringBoot组件版本) -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath />
    </parent>
  
    <!-- 依赖管理器 -->
    <dependencyManagement>
    </dependencyManagement>

    <!-- 配置相关依赖 -->
    <dependencies>
    </dependencies>

    <!-- Maven 的构建配置 -->
    <build>
        <!-- 配置相关插件 -->
        <plugins>
        </plugins>
    </build>
</project>

配置文件结构说明:

  • properties:一般用于配置依赖的版本信息或一些配置的属性参数;
  • parent:配置当前 Maven 项目的父依赖,这里一般配置的是spring-boot-starter-parent
  • dependencyManagement:依赖管理器,一般用于配置 Spring Cloud 的dependencies,为后续使用 Spring Cloud 相关组件提供统一的版本;
  • dependencies:依赖列表,这里配置的都是具体的依赖,比如我们使用的 web支持、数据库支持、工具包等;
  • build:Maven 的构建配置,一般这里会配置一些打包插件和打包时的一些规则,比如 springboot 的打包插件、maven 的打包插件、打包时跳过单元测试等。

完整的配置文件如下:

<?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.cloud</groupId>
    <artifactId>cloud-user-service</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <!-- pom 文件的属性配置 -->
    <properties>
        <java.version>1.8</java.version>
        <charset>UTF-8</charset>
    </properties>

    <!-- Spring Boot 版本控制(统一SpringBoot组件版本) -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath />
    </parent>

    <!-- 依赖管理器 -->
    <dependencyManagement>
        <dependencies>
            <!-- Spring Cloud 原生依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR6</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.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 配置相关依赖 -->
    <dependencies>
        <!-- Spring Boot 监视器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- Spring Boot Web 支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Cloud Nacos 注册中心依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- Spring Cloud Nacos 配置中心依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    </dependencies>

    <!-- Maven 的构建配置 -->
    <build>
        <!-- 配置相关插件 -->
        <plugins>
            <!-- 打包时跳过单元测试 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>

            <!-- Spring Boot Maven 打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <!-- Maven 打包插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${charset}</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

上文中只是包含了能够支持 Spring Cloud 微服务最基本的依赖,至于其他的一些依赖并不是所有服务都需要,后面讲到具体的知识点时会单独介绍。

配置 Nacos

实际开发过程中,我们可能会共用 Nacos 服务,所以本章节介绍搭建一个 Spring Cloud 的微服务需要在 Nacos 上配置哪些东西。

注意:同一个项目,最好保证在同一个命名空间,同一个组下,否则无法实现服务调用。虽然有其他办法解决,但处理起来比较麻烦。

创建命名空间

如果我们很多个项目共用一个 Nacos 服务,可以在 Nacos 中创建相应的命名空间来区分和管理微服务。进入 Nacos 控制台,找到命名空间,点击新建命名空间,填入参数说明如下:

  • 命名空间ID(不填则自动生成):命名空间的唯一标识,如果不填写,系统会默认使用 UUID 作为命名空间ID。此处一般都选择自动生成,如果为了方便记忆也可以手工填写。
  • 命名空间名:这里填写的是命名空间的名字,此名称会显示在其他的一些配置面板上。
  • 描述:这里填写的是对命名空间的一个简单描述。

本次我们创建了一个cloud的命名空间。

创建微服务的配置文件

在 Nacos 控制台找到配置管理 -> 配置列表,上方选择我们上一步创建好的命名空间,然后点击最右侧的+按钮,如下图所示:

image-20210728165112368.png

在创建页面需要填入的参数说明如下:

  • Data ID:配置文件的唯一标识,一般格式为:微服务名称-部署环境类型.后缀名。实际使用中后缀也可以省略不写。
  • Group:配置文件的分组,可以对配置文件进行分组管理,其实主要是为了便于查询。默认为:DEFAULT_GROUP。
  • 描述:配置文件的描述信息。
  • 配置格式:包含 TEXT、JSON、XML、YAML、HTML、Properties 几种配置书写格式。
  • 配置内容:具体的配置内容。

我们以创建用户服务的配置文件为例,具体填写的参数如下:

  • Data ID:cloud-user-service-dev.yaml,其中 dev 是指我们的开发环境,后面会介绍它在框架搭建中的作用。
  • Group:BUSINESS_GROUP,这里我们定一个了一个业务组,如果微服务不多(20个以内)则不需要单独去指定组。
  • 描述:用户服务配置。
  • 配置格式:YAML。
  • 配置内容:test: hello test,此配置只是为了测试,后面我们会将具体微服务的配置移植到这里。

配置完成后点击发布按钮保存配置文件信息。

编写bootstrap.yaml配置文件

配置文件内容如下:

server:
  port: 10001
spring:
  profiles:
    active: dev
  application:
    name: cloud-user-service
  output:
    ansi:
      enabled: always
---
spring:
  profiles: dev
  cloud:
    nacos:
      server-addr: 172.16.1.180:8848
      discovery:
        namespace: 02ed4eca-207e-4e27-9e9e-b486080f6f1c
      config:
        namespace: 02ed4eca-207e-4e27-9e9e-b486080f6f1c
        file-extension: yaml
        prefix: ${spring.application.name}

配置说明

  • server.port:Spring Boot 内置容器的端口号,如果是 Tomcat 则为 Tomcat 端口号。

  • spring.profiles.active:当前激活的配置,dev-开发环境。上线时还会添加 pro-生产环境。

  • spring.application.name:微服务名称。

  • spring.output.ansi.enabled:一般取值为always,即输出带彩色的日志。

  • spring.profiles:配置文件标识,spring.profiles.active的值与此对应。

  • spring.cloud.nacos.server-addr:Nacos 服务的地址。

  • spring.cloud.nacos.discovery.namespace:注册中心的命名空间,即注册到哪个命名空间下,此处填写的是命名空间ID。

  • spring.cloud.nacos.discovery.group:注册中心组名,即注册到哪个组下。

  • spring.cloud.nacos.config.namespace:配置中心的命名空间,即拉取哪个命名空间下的配置。

  • spring.cloud.nacos.config.file-extension:配置中心创建的配置文件扩展名。

  • spring.cloud.nacos.config.prefix:配置中心创建的配置文件前缀,一般为微服务名称。

特别说明:prefix、profiles、file-extension构成一个完整的配置文件名,此名称与配置中心创建的配置文件 Data ID 要保持一致。按照上文的配置方式,即配置中心的 Data ID为:cloud-user-service-dev.yaml

编写启动类

在根包com.cloud下创建一个AppStart的类,完整代码如下:

package com.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication(scanBasePackages = {"com.cloud"})
@EnableDiscoveryClient
public class AppStart {
    public static void main(String[] args) {
        SpringApplication.run(AppStart.class, args);
    }
}

注解说明

  • @SpringBootApplication(scanBasePackages = {"com.cloud"})注解指定了 Spring Boot 应用程序的扫描的根包,即我们最开始的时候创建的根包路径。

  • @EnableDiscoveryClient注解的作用是在微服务中开启注册中心客户端。

微服务测试

创建一个com.cloud.api.TestApi类,代码如下:

package com.cloud.api;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test/*")
public class TestApi {
    @Value("${test:123}")
    private String test;

    @GetMapping("hello")
    public Object testCase() {
        return test;
    }
}

代码中读取了配置中心的配置文件内容,并通过接口返回。代码编写完成后,启动微服务。启动之后,进入 Nacos 控制台,找到服务管理 -> 服务列表,如果看到微服务注册到列表中,即说明微服务的注册发现功能可用。页面如下:

image-20210728165211988.png

打开浏览器输入:http://127.0.0.1/test/hello,显示页面如下:
image-20210728173758557.png

页面中打印的内容是我们在前面创建的配置文件中的配置内容,如果一致,说明配置中心功能可用。

网关微服务搭建

本次使用的是 Spring Cloud Gateway 作为微服务网关。

网关微服务的搭建相对于业务微服务的搭建大同小异,搭建步骤可以参考业务微服务的搭建步骤。本章节重点介绍不同的部分。

配置相关依赖

注意:Gateway 使用的是 Netty 提供网络服务,并且使用 Webflux 实现的响应式编程。因此在 pom.xml 文件中不可以配置spring-boot-starter-web

按照业务微服务的搭建方法新建一个springcloud-gateway项目,然后在 pom.xml 配置文件中添加 Gateway 依赖

<!-- Spring Cloud Gateway 依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

编写bootstrap.yaml配置文件

本次我们以前面创建的用户服务为例,来实现网关的路由。

server:
  port: 9527
spring:
  profiles:
    active: dev
  application:
    name: springcloud-gateway
  output:
    ansi:
      enabled: always
---
spring:
  profiles: dev
  cloud:
    nacos:
      server-addr: 172.16.1.180:8848
      discovery:
        namespace: 02ed4eca-207e-4e27-9e9e-b486080f6f1c
        register-enabled: false
      config:
        file-extension: yaml
        prefix: ${spring.application.name}
        namespace: 02ed4eca-207e-4e27-9e9e-b486080f6f1c
    gateway:
      routes:
        - id: cloud-user-service
          uri: lb://cloud-user-service
          predicates:
            - Path=/user/**
      default-filters:
        - StripPrefix=1

配置说明:

  • spring.cloud.nacos.discovery.register-enabled:是否开启服务注册,如果为 false,则只发现不注册。网关不需要让其他服务发现,但要能够发现其他服务,因此这里设置为 false,即不向注册中心注册网关服务。

  • spring.cloud.gateway.routes:网关路由配置

    • id:路由ID,一般为微服务名称,没有实际的作用,只是作为路由规则的唯一标识。
    • uri:路由地址,支持多种协议。其中lb://使用的是负载均衡协议,因此后面的地址为要路由到的微服务名字,因为对微服务进行负载是很常见的,如果使用 http 协议,则需要为每个负载节点配置路由规则。如果使用负载均衡协议,则只需要为服务创建路由规则,而无需关心具体的负载节点。此协议也是最常用的协议。
    • predicates:路由断言,一个路由规则中可以有很多个断言,这里的- Path就是其中一种,代表路径匹配断言。其他的规则官网上都有,可以自己去查阅。
  • spring.cloud.gateway.default-filters:网关的默认过滤器,此过滤器对有所有路由规则有效,如果是但对为某个路由规则编写过滤器,可以在routes下配置filters

    • StripPrefix:跳过前缀,其值是指访问路径的 level,比如:/user/test/hello,如果 StripPrefix 为 1,则实际访问微服务的路径为 /test/hello,即第一级被跳过。默认为0,如果为 0,则通过网关访问到微服务的实际路径为 /user/test/hello,如果微服务没有提供此路径的接口,则返回 404。

网关测试

创建一个com.cloud.filter.TestFilter的过滤器类,具体代码如下:

package com.cloud.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 测试过滤器
 */
@Component
public class TestFilter implements GlobalFilter, Ordered {
    /**
     * 过滤规则
     * @param exchange 信息交换对象,内部包含 HTTP 请求的相关实例
     * @param chain 过滤链
     * @return 返回 Mono 对象
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 此处编写过滤规则
        System.out.println("==========> 我是测试过滤器");

        return chain.filter(exchange);
    }

    /**
     * 排序规则
     * @return 返回排序编号(值越小,执行顺序越早)
     */
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

实际开发中,我们如果需要编写具体的过滤规则都按照此方法编写。代码编写完成后,启动网关服务。修改用户服务的测试接口代码如下:

package com.cloud.api;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test/*")
public class TestApi {
    @Value("${test:123}")
    private String test;

    @GetMapping("hello")
    public Object testCase() {
        return "用户服务返回:" + test;
    }
}

这样,我们在通过网关路由时,就可以清楚的看到是否真的调用了用户服务的接口。修改完毕后记得重启用户服务。

打开浏览器,输入http://127.0.0.1:9527/user/test/hello,返回如下页面:

image-20210728173758557.png

从页面中,我们可以看到,网关确实调用了用户服务,也正常返回了用户服务测试接口的信息。接下来查看日志输出,如下图所示:


image-20210728174024704.png

这里可以看到,我们在网关过滤器输出的一段话,在控制台被打印出来了,证明我们编写的过滤器也是可用的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容