[新手入门] SpringCloud微服务-从0到1

521 阅读4分钟

本篇文章将细述从0到1搭建一个微服务项目的整个过程.

  • 如何通过IDEA新建spring boot项目
  • 如何优化项目配置得到更好的开发体验
  • 如何使用Spring Cloud Openfeign进行远程调用
  • 如何集成Nacos实现服务发现与注册
  • OpenFeign如何整合Nacos进行服务动态调用与负载均衡

本文源代码仓库地址: z2o

  • 本文对部分功能的原理不会做解释, 如果需要可评论区提问

准备

  • IDEA 2024.x (可以通过ToolBox下载对应版本, 无Utl版可选择社区版或EAP版)
  • Nacos 2.4.x (可以暂时使用本人提供的共享Nacos服务, 请注意该服务会在2024年12月底停止)

正式开始

项目初始化

打开IDEA的New project/新建项目页面

如何打开这个页面?

  • 如果是第一次进入或上次一关闭项目是通过Close project/关闭项目的则打开IDEA时会弹出Welcome界面, 在Welcome界面点击Create new project/新建项目, 则会弹出新建项目页面
  • 如果IDEA已经打开了某个项目, 则点击左上角 File/文件 -> 选择New/新建 -> 选择project/项目, 则会弹出新建项目页面

New project/新建项目页面左侧选择Spring Boot, 并配置项目的基本信息

注意事项

  • 如果是国内建议修改spring boot initialize配置.
  • 如果本地的没有JDK或者JDK < 17的话可以在选择JDK的下拉菜单中直接下载

image.png

填完消息后点击Next/下一步, 进入选择依赖项页面(按需选择)

image.png

然后点击Create/创建就能新建Spring Boot项目了

配置项目

Maven配置优化

编辑pom.xml文件, 覆盖原来在最下面的</build>标签(添加该配置后就无需配置mavensettings.xml文件)

<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>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
                <encoding>${project.build.sourceEncoding}</encoding>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-deploy-plugin</artifactId>
            <version>3.1.3</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
                <encoding>${project.build.sourceEncoding}</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>


<profiles>
    <profile>
        <id>Default</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <repositories>
            <repository>
                <id>public</id>
                <name>aliyun nexus</name>
                <url>https://maven.aliyun.com/repository/public</url>
                <releases>
                    <enabled>true</enabled>
                </releases>
            </repository>
            <repository>
                <id>central-repos</id>
                <name>Central Repository</name>
                <url>https://repo.maven.apache.org/maven2</url>
            </repository>
            <repository>
                <id>central</id>
                <url>https://maven.aliyun.com/nexus/content/groups/public</url>
                <releases>
                    <enabled>true</enabled>
                </releases>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>snapshots</id>
                <url>https://maven.aliyun.com/nexus/content/groups/public</url>
                <releases>
                    <enabled>false</enabled>
                </releases>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
        </repositories>
    </profile>
</profiles>

添加后需要执行一次mvn clean install命令

image.png

软件包作用说明

image.png

SpringBoot项目的软件包大体分为四类,

  • 业务代码: src.main.java.<groupId>
  • 资源文件: src.main.java.resources
  • 测试代码: src.main.test
  • 编译结果: target (通过执行mvn compile / mvn install生成)

spring配置修改

resources包下原有的application.properties重命名为application.yml, 并使用一下内容覆盖原有内容

server:
  servlet:
    # 声明请求当前服务接口时的前缀
    context-path: /z2o
  # 声明请求当前服务接口时的端口, 默认8080
  port: 8000

spring:
  application:
    name: z2o

在执行完这一步后可以测试性地启动项目, 看是否有问题

接口声明

添加后续需要使用的四个 类/接口

image.png

SimpleController中声明接口并启动项目

@RestController
@RequestMapping("/simple")
public class SimpleController {

    @GetMapping("/hello/spring_boot")
    public String helloSpringBoot() {
        return "Hello Spring Boot!";
    }

}

声明完成后点击方法名左侧的图标 直接生成请求, 然后点击执行即可请求该接口

image.png

image.png

使用Openfeign进行远程调用

在这一步 我们通过请求接口来得到另一个接口的返回值, 这就是Feign接口的主要用途与用法

编辑SimpleControllerClient

@FeignClient(
    contextId = "SimpleControllerClient",
    value = "z2o",
    path = "/z2o/simple",
    url = "localhost:${server.port:8080}")
public interface SimpleControllerClient {

  @GetMapping("/hello/spring_boot")
  String helloSpringBoot();
}

编辑SimpleController

@RestController
@RequestMapping("/simple")
public class SimpleController {

    @Resource private SimpleControllerClient simpleControllerClient;

    @GetMapping("/hello/spring_boot")
    public String helloSpringBoot() {
        return "Hello Spring Boot!";
    }


    @GetMapping("/hello/feign")
    public String helloFeign() {
        return simpleControllerClient.helloSpringBoot();
    }

}

同上请求新声明的controller中的/hello/feign接口即可得到/hello/spring_boot的返回值.

Feign使用优化

在正式项目中不会每个服务以及仓库都定义一套Feign, 而是会将Feign归类抽离到一个公共仓库中, 这样只需要声明一套Feign接口就能在多个仓库/服务中使用.

编辑SimpleApiClient

@FeignClient(
    contextId = "SimpleApiClient",
    value = "z2o",
    path = "/z2o/simple/api",
    url = "localhost:${server.port:8080}")
public interface SimpleApiClient {

  @GetMapping("/hello/open_feign")
  String helloOpenFeign();
}

编辑SimpleApi

@RestController
@RequestMapping("/simple/api")
public class SimpleApi implements SimpleApiClient {

    @Override
    public String helloOpenFeign() {
        return "It's random number: " + Math.random();
    }
}

在这个示例中, 通过实现Feign定义的接口来声明其所调用的端点(即接口).

实现类中不需要再次声明方法对应的请求路径, 只需要声明类的请求路径即可

Feign整合Nacos

引入Nacos依赖并配置

在pom.xml文件中添加如下配置

添加dependencyManagement

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2023.0.1.0</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

添加dependency

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

image.png

application.yml文件中配置Nacos参数

spring:
  application:
    name: z2o
  cloud:
    nacos:
      discovery:
        enabled: true
        server-addr: 120.77.239.177:8848

编辑SimpleApiClient(去掉@FeignClient中的url参数)

@FeignClient(
    contextId = "SimpleApiClient",
    value = "z2o",
    path = "/z2o/simple/api")
public interface SimpleApiClient {

  @GetMapping("/hello/open_feign")
  String helloOpenFeign();
}

编辑SimpleController

@RestController
@RequestMapping("/simple")
public class SimpleController {

    @Resource private SimpleControllerClient simpleControllerClient;

    @GetMapping("/hello/spring_boot")
    public String helloSpringBoot() {
        return "Hello Spring Boot!";
    }


    @GetMapping("/hello/feign")
    public String helloFeign() {
        return simpleControllerClient.helloSpringBoot();
    }
    
    @Resource private SimpleApiClient simpleApiClient;
    
    @GetMapping("/hello/nacos_feign")
    public String helloNacosFeign() {
        return simpleApiClient.helloOpenFeign();
    }

}

重启项目, 并请求helloNacosFeign接口, 可以通过Feign调用SimpleApi中声明的接口

可以到Nacos中查看是否成功注册(如果有其他人注册了相同的服务名 可通过修改服务名进行区别化, 或者查看集群服务的节点IP中是否有你的IP [由于是内网 可能IP重复] )

也可通过注册不同group实现区别化, 详见git提交记录

image.png

image.png

解析

在原有的Feign声明中, 由于指定了Feign调用端口的URL 所以无法路由到其他IP的相同服务, 也无法进行负载均衡.

通过而引入Nacos则很好的解决了这个问题, 一次Feign调用将通过服务名(o2z)拉取对应注册到Nacos中的集群列表, 并通过nacos中配置的权重对请求进行路由