如何从 0 搭建微服务?

大家好,我是曾续缘,今天和大家分享微服务架构的搭建过程。在这个过程中,我尽可能地减少了技术,以更直观的方式展示微服务架构的搭建过程。希望能给大家带来一些启发和帮助。

现在我们先看一下架构图。

如何从 0 搭建微服务?

1. 新建项目

如何从 0 搭建微服务?
如何从 0 搭建微服务?

2. 创建父项目

创建一个新的Maven模块,JDK版本选择1.8

创建父项目是为了方便管理多个子模块,如各个微服务子项目。父项目中可以定义共同的依赖、插件、配置等内容,子模块可以继承并继承这些内容,从而实现统一的管理和维护。

如何从 0 搭建微服务?

创建完后给pom文件增加<packaging>pom</packaging>

    <groupId>com.cengxuyuan</groupId>
    <artifactId>micro-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

<packaging>pom</packaging> 在 Maven 项目中的作用是定义项目的打包方式。在这里,pom 表示这是一个聚合(parent)项目,它本身不包含源代码,只是用来组织子模块。这种类型的项目在打包时不会生成任何实际的构建产物(如 JAR 或 WAR 文件),而是生成一个 POM 文件(.pom),该文件包含了项目的配置信息和对子模块的引用。这样可以方便地管理和构建多模块项目。

3. 创建基本项目

基本项目放通用的类,供其他微服务使用。

基本项目是用于存放通用的类和方法,供其他微服务项目使用,比如响应对象,工具类。

如何从 0 搭建微服务?

3.1. 指定父项目

指定父项目可以让当前项目继承父项目中的依赖和插件等,从而减少重复配置,提高代码维护效率。

common通用项目的pom文件中增加下面的标签,指定父项目。

  <parent>
        <groupId>com.cengxuyuan</groupId>
        <artifactId>micro-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../micro-parent</relativePath>
    </parent>

在 Maven 项目中,<parent> 标签用于指定当前项目的父项目。这里的父项目是 micro-parent

<parent> 标签包含以下信息:

  • <groupId>: 父项目的 groupId,它是项目的唯一标识符,通常是组织或项目的包名。
  • <artifactId>: 父项目的 artifactId,它是项目的唯一基础名。
  • <version>: 父项目的版本号。
  • <relativePath>: 父 pom.xml 文件相对于当前项目的路径。如果没有指定,Maven 会默认在父目录中查找。

这样,当前项目就可以继承父项目中定义的依赖和插件,避免了在每个子模块中重复配置相同的信息,提高了配置的复用性。

3.2. 创建响应对象

common通用项目创建一个响应对象,用于统一封装网络请求和响应。通过封装响应对象,可以方便地进行异常处理、错误码的定义以及数据的传输和解析等。这样可以提高开发效率,减少代码重复。

@Data
public class R<T> {

    private Integer code;
    private String msg;
    private T data;

    public static <T> R<T> success() {
        R<T> R = new R<T>();
        R.code = 0;
        return R;
    }
    ...
}

4. 创建第一个微服务项目

首先开发第一个micro-user微服务,和单体项目的开发过程相同。为了使用最少的技术,尽可能的简单,只定义了user实体类,只有一个业务方法:就是根据用户名查询用户信息,这只需要MySQL数据库和Mybatis-plus就可以实现。

4.1. 指定父项目

同样需要指定父项目。

   <parent>
        <groupId>com.cengxuyuan</groupId>
        <artifactId>micro-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../micro-parent</relativePath>
    </parent>
如何从 0 搭建微服务?

4.2. 增加MYSQL和Mybatis-plus依赖

添加必要的依赖项来操作MySQL数据库和使用Mybatis-plus简化数据访问层。

    <dependencies>
        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-Java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- mybatis plus的依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
    </dependencies>

4.2.1. 添加cloud的基础环境包

添加一个名为 “Spring-cloud-context” 的依赖项。用于开发基于 Spring Cloud 框架的项目,这样我们写bootstrap配置文件。

  <!--cloud的基础环境包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-context</artifactId>
        </dependency>

4.3. 创建该服务的数据库

如何从 0 搭建微服务?

4.4. 创建用户表

创建用户表并插入数据.

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `user_id` int NOT NULL COMMENT '用户id',
  `username` varchar(30NOT NULL COMMENT '用户名称',
  `password` varchar(200NOT NULL COMMENT '密码',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`user_id`),
  KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4;

INSERT INTO `t_user` (`user_id``username``password``create_time`VALUES
(1'user1''password1''2024-03-01 08:00:00'),
(2'user2''password2''2024-03-01 09:00:00'),
(3'user3''password3''2024-03-01 10:00:00'),
(4'user4''password4''2024-03-01 11:00:00'),
(5'user5''password5''2024-03-01 12:00:00'),
(6'user6''password6''2024-03-01 13:00:00'),
(7'user7''password7''2024-03-01 14:00:00'),
(8'user8''password8''2024-03-01 15:00:00'),
(9'user9''password9''2024-03-01 16:00:00'),
(10'user10''password10''2024-03-01 17:00:00');
如何从 0 搭建微服务?

4.5. 增加配置文件

创建bootstrap.yml配置文件

在Spring Cloud项目中, bootstrap.yml配置文件在应用程序的启动阶段最先被加载, 然后才加载application配置文件, 为了简单, 我们在bootstrap.yml配置文件中写上配置即可.

server:
  servlet:
    context-path: /user
  port: 8001
spring:
  application:
    name: micro-user
  profiles:
    active: dev
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/micro-user?serverTimezone=UTC&useUnicode=true&useSSL=false
    username: root
    password: root

如何从 0 搭建微服务?

4.6. MVC架构搭建服务

4.6.1. 实现功能:根据用户名查询用户信息

4.6.2. 创建实体类

@Data
@TableName("t_user")
public class User {
    @TableId(value = "user_id", type = IdType.AUTO)
    private Integer userId;
    private String username;
    private String password;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
}

4.6.3. 创建Mapper层接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select("select * from t_user where username=#{name}")
    public User getUserByName(String name);
}

4.6.4. 创建服务接口

public interface UserService extends IService<User> {
    public User getUserByName(String name);
}

4.6.5. 实现服务接口

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    UserMapper userMapper;
    @Override
    public User getUserByName(String name) {
        return userMapper.getUserByName(name);
    }
}

4.6.6. 创建API接口

@RestController
public class UserController {
    @Autowired
    UserService userService;

    @GetMapping("/{name}")
    public R getUserByName(@PathVariable String name){
        return R.success(userService.getUserByName(name));
    }
}

4.7. 启动微服务

4.7.1. 创建Spring Boot应用入口

@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

浏览器访问http://localhost:8001/user/user1

返回数据,说明项目能运行.

如何从 0 搭建微服务?

现在我们实现了第一个微服务项目, 虽然这仍然类似于单体项目,但已集成了Spring Cloud框架相关的依赖,并使用了不同于单体项目的配置文件。一个单体项目还不能明显地体现出微服务的项目结构, 接下来将创建第二个微服务.

5. 创建第二个微服务

第二个服务是micro-post, 就是关于用户的帖子的, 同样为了简单, 只写了一个业务方法: 就是根据用户ID查询用户的帖子, 为了更简便, 只返回一篇帖子(如果存在的话).

5.1. 指定父项目

   <parent>
        <groupId>com.cengxuyuan</groupId>
        <artifactId>micro-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../micro-parent</relativePath>
    </parent>

5.2. 增加MYSQL和Mybatis-plus依赖

    <dependencies>
        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- mybatis plus的依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
    </dependencies>

5.2.1. 添加cloud的基础环境包

  <!--cloud的基础环境包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-context</artifactId>
        </dependency>
    <dependency>
            <groupId>com.cengxuyuan</groupId>
            <artifactId>micro-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

5.3. 创建post服务的数据库

不同的微服务之间使用不同的数据库.

如何从 0 搭建微服务?

5.4. 创建文章表

DROP TABLE IF EXISTS `t_post`;
CREATE TABLE `t_post` (
  `post_id` int NOT NULL COMMENT '文章id',
  `user_id` int NOT NULL COMMENT '用户id',
  `title` varchar(200NOT NULL COMMENT '文章标题',
  `content` text NOT NULL COMMENT '文章内容',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`post_id`),
  KEY `idx_post_id` (`post_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4;

INSERT INTO `t_post` (`post_id``user_id``title``content``create_time`VALUES
(11'Exciting News''Content of post 1 by user 1.''2024-03-01 08:00:00'),
(22'Tech Innovations''Content of post 2 by user 2.''2024-03-01 09:00:00'),
(33'Health and Wellness''Content of post 3 by user 3.''2024-03-01 10:00:00'),
(44'Travel Destinations''Content of post 4 by user 4.''2024-03-01 11:00:00'),
(55'Cooking Tips''Content of post 5 by user 5.''2024-03-01 12:00:00'),
(66'Financial Advice''Content of post 6 by user 6.''2024-03-01 13:00:00'),
(77'DIY Projects''Content of post 7 by user 7.''2024-03-01 14:00:00'),
(88'Sports Commentary''Content of post 8 by user 8.''2024-03-01 15:00:00'),
(99'Art and Culture''Content of post 9 by user 9.''2024-03-01 16:00:00'),
(1010'Educational Resources''Content of post 10 by user 10.''2024-03-01 17:00:00');

5.5. 增加配置文件

server:
  servlet:
    context-path: /post
  port: 8002
spring:
  application:
    name: micro-post
  profiles:
    active: dev
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/micro-post?serverTimezone=UTC&useUnicode=true&useSSL=false
    username: root
    password: root

5.6. MVC架构搭建服务

5.6.1. 实现功能:根据用户ID查询用户文章

现实中一个用户可能有多篇文章,这里为了简便,不使用List和封装Page参数,这里只查询一篇文章。

5.6.2. 创建实体类

@Data
@TableName("t_post")
public class Post {
    @TableId(value = "post_id", type = IdType.AUTO)
    private Integer postId;
    private Integer userId;
    private String title;
    private String content;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
}

5.6.3. 创建Mapper层接口

@Mapper
public interface PostMapper extends BaseMapper<Post> {
    @Select("select * from t_post where user_id=#{userId} limit 1")
    public Post getPostByUserId(Integer userId);
}

5.6.4. 创建服务接口

public interface PostService extends IService<Post> {
    public Post getPostByUserId(Integer userId);
}

5.6.5. 实现服务接口

@Service
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements PostService {
    @Autowired
    PostMapper PostMapper;

    @Override
    public Post getPostByUserId(Integer userId) {
        return PostMapper.getPostByUserId(userId);
    }
}

5.6.6. 创建API接口

@RestController
public class PostController {
    @Autowired
    PostService postService;

    @GetMapping("/user/{userId}")
    public R getPostByUserId(@PathVariable Integer userId){
        return R.success(postService.getPostByUserId(userId));
    }
}

5.7. 启动微服务

5.7.1. 创建Spring Boot应用入口

@SpringBootApplication
public class PostApplication {
    public static void main(String[] args) {
        SpringApplication.run(PostApplication.class, args);
    }
}

浏览器访问http://localhost:8002/post/user/1

返回数据

如何从 0 搭建微服务?

6. 搭建Nacos

访问:http://192.168.101.65:8848/nacos/

账号密码:nacos/nacos

创建命名空间

如何从 0 搭建微服务?

6.1. 服务注册

6.1.1. 添加依赖

micro-userpom中添加

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

6.1.2. 配置nacos地址

server:
  servlet:
    context-path: /user
  port: 8001
spring:
  application:
    name: micro-user
  profiles:
    active: dev
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/micro-user?serverTimezone=UTC&useUnicode=true&useSSL=false
    username: root
    password: root

  cloud:
    nacos:
      server-addr: 192.168.101.100:8848
      discovery:
        namespace: micro-dev
        group: micro-project

同样给post服务添加nacos配置

6.1.3. 启动服务

如何从 0 搭建微服务?

6.2. 实现配置中心

6.2.1. 添加配置依赖

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

6.2.2. 修改本地配置文件

将部分配置移到nacos上配置,也就是将注释掉的配置放到nacos上配置

#server:
#  servlet:
#    context-path: /user
#  port: 8001
spring:
  application:
    name: micro-user
  profiles:
    active: dev
#  datasource:
#    driver-class-name: com.mysql.cj.jdbc.Driver
#    url: jdbc:mysql://localhost:3306/micro-user?serverTimezone=UTC&useUnicode=true&useSSL=false
#    username: root
#    password: root

为了能从nacos上获取注释掉的配置,需要增加下面的config配置

  cloud:
    nacos:
      server-addr: 192.168.101.100:8848
      discovery:
        namespace: micro-dev
        group: micro-project
      config:
        namespace: micro-dev
        group: micro-project
        file-extension: yaml
        refresh-enabled: true

6.2.3. 在nacos上配置

如何从 0 搭建微服务?

6.2.4. 启动服务

可以运行

同样的道理,给post服务的配置也放到nacos中

6.2.5. 公用配置

比如说log4j日志配置,swagger开发文档配置,远程调用配置等,后面讲到,配置大概如下:

      config:
        namespace: micro-dev
        group: micro-project
        file-extension: yaml
        refresh-enabled: true
        # 用于共享的配置文件
        shared-configs:
          - data-id: feign-${spring.profiles.active}.yaml
            group: micro-shared
            refresh: true

6.3. 搭建网关

6.3.1. 创建网关项目

6.3.1.1. 指定父项目

 <parent>
        <groupId>com.cengxuyuan</groupId>
        <artifactId>micro-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../micro-parent</relativePath>
    </parent>

6.3.1.2. 添加依赖

  <dependencies>
        <!--网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--服务发现中心-->
        <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>

        <!-- 排除 Spring Boot 依赖的日志包冲突 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

6.3.1.3. 添加配置文件

spring:
  application:
    name: micro-gateway
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.101.100:8848
      discovery:
        namespace: micro-dev
        group: micro-project
      config:
        namespace: micro-dev
        group: micro-project
        file-extension: yaml
        refresh-enabled: true


6.3.1.4. 在nacos上配置网关路由

server:
  port: 8888 # 网关端口
spring:
  cloud:
    gateway:
      routes: # 网关路由配置
        - id: user-api # 路由id,自定义,只要唯一即可
          uri: lb://micro-user # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
        - id: post-api
          uri: lb://micro-post
          predicates:
            - Path=/post/**

如何从 0 搭建微服务?

6.3.1.5. 创建启动类

@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

6.3.1.6. 启动

通过网关服务访问其他微服务,在浏览器上访问http://localhost:8888/user/user1

如何从 0 搭建微服务?

7. 使用Feign实现远程调用

user服务调用post服务,根据username查询用户的文章。

7.1. 添加依赖

        <!-- Spring Cloud 微服务远程调用 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>

7.2. 在nacos配置feign配置文件

如何从 0 搭建微服务?
feign:
  client:
    config:
      default: # default全局的配置
        loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息

  hystrix:
    enabled: true
    command:
      default:
        execution:
          isolation:
            thread:
              timeoutInMilliseconds: 10000 # 设置hystrix的超时时间为10秒 
  circuitbreaker:
    enabled: true
  httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

7.3. 在user服务引用配置文件

      config:
        namespace: micro-dev
        group: micro-project
        file-extension: yaml
        refresh-enabled: true
        shared-configs:
          - data-id: feign-${spring.profiles.active}.yaml
            group: micro-shared
            refresh: true

7.4. 编写feign服务接口

@FeignClient(value = "micro-post")
public interface PostServiceClient {

    @GetMapping(path = "/post/user/{userId}")
    R getPostByUserId(@PathVariable("userId") Integer userId);

}

在Spring Cloud版本Hoxton及以后(包括你使用的Spring Cloud OpenFeign 2.2.6.RELEASE),@PathVariable注解不能缺少变量名称,我们需要显式地指定@PathVariable中的名称。

7.5. 启动类上添加注解

@EnableFeignClients(basePackages = {"com.cengxuyuan.feignclient"})
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

7.6. 在user服务上测试

7.6.1. 注入client并编写测试方法

@RestController
public class UserController {
    @Autowired
    UserService userService;
    @Autowired
    PostServiceClient postServiceClient;

    @GetMapping("/{name}")
    public R getUserByName(@PathVariable String name){
        return R.success(userService.getUserByName(name));
    }

    @GetMapping("/feign/{name}")
    public R testFeign(@PathVariable String name){
        User user = userService.getUserByName(name);
        R post = postServiceClient.getPostByUserId(user.getUserId());
        return post;
    }
}

7.6.2. 启动服务进行测试

在浏览器输入http://localhost:8001/user/feign/user1

得到输出结果,可以知道访问user微服务和post微服务成功。

如何从 0 搭建微服务?

8. 实现熔断降级

nacos中的feign-dev.yaml增加如下配置, 前面已经增加了,这里说明一下作用

feign:
  hystrix:
    enabled: true
  circuitbreaker:
    enabled: true

这段配置表示你希望启用Feign集成的Spring Cloud Circuit Breaker支持。这允许你使用Spring Cloud Circuit Breaker提供的抽象来管理断路器行为,而不是直接依赖于Hystrix或其他具体实现。这种方式提供了更大的灵活性,因为你可以根据需要更换不同的断路器实现,而不必修改业务代码。

8.1. 指定FallbackFactory 实现降级

@FeignClient(value = "micro-post", fallbackFactory = PostServiceClientFallbackFactory.class)
public interface PostServiceClient {

    @GetMapping(path = "/post/user/{userId}")
    R getPostByUserId(@PathVariable("userId") Integer userId);

}

8.2. 实现FallbackFactory

这种方法可以拿到异常信息。

@Component
public class PostServiceClientFallbackFactory implements FallbackFactory<PostServiceClient> {
    @Override
    public PostServiceClient create(Throwable throwable) {
        return new PostServiceClient() {
            @Override
            public R getPostByUserId(Integer userId) {
                return R.error("getPostByUserId降级");
            }
        };
    }
}

8.3. 启动服务进行测试

首先访问http://localhost:8001/user/feign/user1,是可以正常访问的,

现在关闭post微服务来认为制造异常,观察是否执行熔断降级。

重新访问http://localhost:8001/user/feign/user1,可以看到,出现异常后执行了FallbackFactory中的降级处理。

如何从 0 搭建微服务?

9. 总结

通过上面的教程,我们已经学习了如何构建一个基于Spring Cloud框架的微服务架构项目,并且掌握了各种工具和技术的使用方法。

在微服务的搭建过程中,我们使用了Nacos作为服务注册中心,在Nacos上配置路由信息和共享配置。同时,我们还使用Spring Cloud Gateway创建网关,并且在Nacos上配置网关路由信息。

在远程调用方面,我们使用了Feign实现远程调用其他微服务,并且在Nacos上配置Feign的配置文件。最后,我们还学习了如何使用熔断降级机制来处理异常情况,保证系统的稳定性。

总的来说,本教程展示了微服务的基本架构,涵盖了以下内容:

  • 创建并配置Nacos作为服务注册中心和配置中心
  • 使用Spring Cloud Gateway创建网关,并配置网关路由信息
  • 使用Feign实现微服务之间的远程调用,并进行相应的配置
  • 实现熔断降级机制以提高系统的稳定性

希望这次的教程能够为你的技术之路添砖加瓦。

原文始发于微信公众号(曾续缘):如何从 0 搭建微服务?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/256728.html

(0)
东东的头像东东

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!