SpringCloud工程部署启动

简介: 本文介绍SpringCloud微服务工程搭建全过程,涵盖项目创建、数据库配置、服务部署及远程调用实现。通过两种方案导入工程,完成user-service与order-service模块开发,并利用RestTemplate实现跨服务数据调用,帮助理解微服务间通信机制及拆分逻辑。

1.工程搭建部署

方案一:完整工程导入

方案二:从零开始搭建

1.工程与module创建

1.1 父工程创建

1.2 子module创建

  • module名称:order-service、user-service
  • 无效文件夹删除,整体结构如图2

1.3 父pom资源引入

粘贴以下资源依赖,粘贴后maven会自动拉取依赖,如未拉取请手动刷新

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.9.RELEASE</version>
    <relativePath/>
</parent>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR10</spring-cloud.version>
    <mysql.version>5.1.47</mysql.version>
    <mybatis.version>2.1.1</mybatis.version>
    <lombok.version>1.18.20</lombok.version>
</properties>
<dependencyManagement>
    <dependencies>
        <!-- springCloud -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

1.4 子module资源引入

user-service

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
<build>
    <finalName>app</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
order-service

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

1.5 业务代码编写

1.user-service

application.yml配置文件

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
    username: root
    password: root123456
    driver-class-name: com.mysql.jdbc.Driver
mybatis:
  type-aliases-package: cn.itcast.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS

|--mapper

|-- UserMapper

package cn.itcast.user.mapper;
import cn.itcast.user.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
 * 用户持久层
 *
 * @author 
 * @date 2022-12-22 14:12
 */
public interface UserMapper {
    /**
     * 根据ID查找用户
     * @param id    用户ID
     * @return      用户实体信息
     */
    @Select("select * from tb_user where id=#{id}")
    User findById(@Param("id") Long id);
}

|--pojo

|-- User

package cn.itcast.user.pojo;
import lombok.Data;
/**
 * 用户实体
 *
 * @author 
 * @date 2022-12-22 14:07
 */
@Data
public class User {
    /**
     * 主键ID
     */
    private Long id;
    /**
     * 用户姓名
     */
    private String username;
    /**
     * 用户地址
     */
    private String address;
}

|--service

|-- UserService

package cn.itcast.user.service;
import cn.itcast.user.mapper.UserMapper;
import cn.itcast.user.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
 * 用户业务层
 *
 * @author 
 * @date 2022-12-22 14:13
 */
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    /**
     * 根据ID查找用户
     * @param id    用户ID
     * @return      用户实体信息
     */
    public User findById(Long id) {
        if (Objects.isNull(id)) {
            return null;
        }
        return userMapper.findById(id);
    }
}

|--web

|-- UserController

package cn.itcast.user.web;
import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 用户控制层
 *
 * @author 
 * @date 2022-12-22 14:15
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    /**
     * 根据ID查找用户
     * @param id    ID
     * @return      用户信息
     */
    @GetMapping("/{id}")
    public User findById(@PathVariable("id") Long id) {
        return userService.findById(id);
    }
}

|--UserApplication

package cn.itcast.user;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * 启动类
 *
 * @author 
 * @date 2022-12-22 14:00
 */
@MapperScan("cn.itcast.user.mapper")
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
        System.out.println("用户工程启动成功");
    }
}
2.order-service

application.yml配置文件

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
    username: root
    password: root123456
    driver-class-name: com.mysql.jdbc.Driver
mybatis:
  type-aliases-package: cn.itcast.order.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS

|--mapper

|-- OrderMapper

package cn.itcast.order.mapper;
import cn.itcast.order.pojo.Order;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
 * 订单持久层
 *
 * @author 
 * @date 2022-12-22 14:22
 */
public interface OrderMapper {
    /**
     * 根据ID查找订单
     * @param id    订单ID
     * @return      订单对象
     */
    @Select("select * from tb_order where id=#{id}")
    Order findById(@Param("id")Long id);
}

|--pojo

|-- Order

package cn.itcast.order.pojo;
import lombok.Data;
/**
 * 订单实体(vo并非dto)
 *
 * @author 
 * @date 2022-12-22 14:19
 */
@Data
public class Order {
    /**
     * 主键ID
     */
    private Long id;
    /**
     * 用户主键ID
     */
    private Long userId;
    /**
     * 商品名称
     */
    private String name;
    /**
     * 商品价格
     */
    private Long price;
    /**
     * 商品数量
     */
    private Integer num;
    /**
     * 用户信息
     */
    private User user;
}

|-- User

package cn.itcast.order.pojo;
import lombok.Data;
/**
 * 用户实体
 *
 * @author 
 * @date 2022-12-22 14:07
 */
@Data
public class User {
    /**
     * 主键ID
     */
    private Long id;
    /**
     * 用户姓名
     */
    private String username;
    /**
     * 用户地址
     */
    private String address;
}

|--service

|-- OrderService

package cn.itcast.order.service;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 订单业务层
 *
 * @author 
 * @date 2022-12-22 14:24
 */
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    /**
     * 根据ID查找订单
     * @param id    订单ID
     * @return      订单对象
     */
    public Order findById(Long id) {
        Order order = orderMapper.findById(id);
        return order;
    }
}

|--web

|-- OrderController

package cn.itcast.order.web;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 订单控制层
 *
 * @author 
 * @date 2022-12-22 14:25
 */
@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private OrderService orderService;
    /**
     * 根据ID查找订单
     * @param orderId    订单ID
     * @return           订单对象
     */
    @GetMapping("/{orderId}")
    public Order findById(@PathVariable("orderId")Long orderId) {
        return orderService.findById(orderId);
    }
}

|--OrderApplication

package cn.itcast.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * 启动类
 *
 * @author 
 * @date 2022-12-22 14:05
 */
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
        System.out.println("订单工程启动成功");
    }
}

2.数据库导入

导完如下

3.项目启动

3.1 启动并访问user-service

3.2 启动并访问order-service

观察发现,虽然order-service服务调用成功,但是里面的user对象却是空的。原因我们应该也可以了解到是因为User对象数据数存储在数据库:tb_user,而此工程连接数据库是:tb_order,查询此数据库时无法获取对应的User数据,怎么获取?获取的具体实现我们将在下一章节进一步铺开。

4.服务远程调用

分析现有链路调用关系可以发现:

想要订单信息中返回用户信息,只要在获取订单链路中追加对用户信息的获取、返回值的组装即可,由此引申出微服务之间的远程服务调用,具体调整可见下图。

时序图说明

上述图形为时序图,一般用来描述系统与系统之间的交互流程,主要是交互API、代码顺序、参数一般忽略,时序图不同于业务流程,更关注业务实现过程中系统前后依赖,数据请求与返回,以下为笔者实际工作场景示例:


服务远程调用实现

注入RestTemplate

此处推荐一个小的优雅工具:https://carbon.now.sh/

RestTemplate完成远程服务调用

在此笔者除了完成远程服务调用,同时对代码做了结构化、异常校验、函数封装。虽然此处逻辑并不复杂,但是对于主干逻辑简化和代码风格,希望能起到一个引导作用。

package cn.itcast.order.service;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.Objects;
/**
* 订单业务层
*
* @author 
* @date 2022-12-22 14:24
*/
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private RestTemplate restTemplate;
    /**
   * 根据ID查找订单
     * @param id    订单ID
     * @return      订单对象
     */
    public Order findById(Long id) {
        // step 1 : 查询订单原始数据
        Order order = orderMapper.findById(id);
        if (Objects.isNull(order)) {
            return null;
        }
        // step 2 : 查询用户数据
        User user = queryUserInfoById(order.getUserId());
        // step 3 : 组装用户数据
        if (Objects.nonNull(user)) {
            order.setUser(user);
        }
        // step 4: 数据返回
        return order;
    }
    /**
   * 根据用户ID查找用户信息
   * @param userId    用户ID
   * @return          用户信息
   */
    private User queryUserInfoById(Long userId) {
        String url = "http://localhost:8081/user/" + userId;
        return restTemplate.getForObject(url, User.class);
    }
}

重启并访问order-service

RestTemplate如何实现远程服务调用

5.总结

本节笔者带领大家完成了SpringCloud工程从0->1的搭建,当然你不想搭建也可以直接采用方案一,二者等效,至此读者们完成了一个微服务工程的搭建、部署、访问。同时在本节最后一章,笔者基于RestTemplate发起的http请求实现远程调用,实现当A系统想要获取B系统数据时的跨系统数据交互。然而RESTful API(进一步了解可移步:链接)访问并不是微服务的唯一解决方案,如Dubbo的交互一样可以实现,希望读者们能不限于此。

在本章节,笔者希望大家对于微服务的拆分,服务调用关系有个初步认知,本例中当order-service调用user-service时,前者就充当了服务调用方,后者则是服务提供方,这种调用关系在后续我们将会大量使用。


思考问题

  • 如何实现服务远程调用?
  • 服务远程调用解决了什么问题?
  • 还有哪些技术可以实现远程服务调用?
相关文章
|
11月前
|
人工智能 监控 Java
请求限流
本文介绍如何使用Sentinel实现接口限流与降级,通过配置QPS阈值保护商品查询接口,并结合JMeter进行压测验证。同时讲解了线程隔离机制,包括信号量隔离的应用,确保系统在高并发下的稳定性。
请求限流
|
11月前
|
消息中间件 存储 缓存
RabbitMQ工作模型
工作队列模型通过多个消费者共同消费一个队列中的消息,实现任务的并行处理。默认情况下,消息平均分配给消费者,可能导致处理能力不同的消费者负载不均。通过设置`prefetch=1`,可实现“能者多劳”,即处理速度快的消费者自动接收更多消息,提升整体效率。发布订阅模型则通过交换机(Exchange)将一条消息转发给多个队列,支持Fanout、Direct、Topic等类型交换机,实现广播或多条件路由消息,满足不同业务场景需求。
|
11月前
|
监控 Java Sentinel
熔断降级
熔断降级是防止服务雪崩的核心机制,通过Sentinel实现。熔断由客户端断路器统计异常或慢请求比例,超阈值后拦截请求;降级则返回默认数据保障体验。结合使用可快速失败、避免级联故障。
|
11月前
|
项目管理 开发者
业务架构图
本文系统阐述了业务架构图的核心概念与绘制方法,涵盖业务定义、架构分层(组织层、应用层、能力层、基础层)、模块划分及功能分解,并结合医院场景示例,说明如何通过分层、分模块、分功能构建清晰的业务视图,提升客户理解与开发效率。
|
11月前
|
消息中间件 Java 数据安全/隐私保护
RabbitMQ入门
RabbitMQ是基于Erlang开发的开源消息中间件,支持AMQP协议,实现应用间解耦与异步通信。其核心组件包括生产者、消费者、队列、交换机和虚拟主机,可通过Docker快速部署并结合SpringAMQP实现消息收发。
|
11月前
|
存储 缓存 负载均衡
Nacos注册中心
本文介绍Nacos的安装部署、服务注册与发现、权重控制、集群隔离及临时/持久实例等核心功能,涵盖从环境搭建到高级配置的完整实践,助力微服务架构高效管理。
 Nacos注册中心
|
11月前
|
缓存 Java 数据库
Spring Boot中使用监听器
本文系统介绍了Web监听器的概念及在Spring Boot中的应用,涵盖监听Servlet上下文、Session会话与Request请求的实战案例,并讲解自定义事件与监听器的实现方式,适用于数据缓存、在线人数统计、用户行为追踪等场景,具有较强的实用价值。
|
11月前
|
Java 关系型数据库 MySQL
Spring Boot事务配置管理
本文介绍了Spring Boot中事务的使用及常见陷阱。通过@Transactional注解可轻松实现事务管理,确保数据操作的原子性。重点剖析了三大易踩坑点:异常类型不匹配导致未回滚、try-catch吞掉异常、事务与锁范围不一致引发并发问题,助你在实际项目中有效规避风险。
|
11月前
|
XML Java 数据库连接
Spring Boot集成MyBatis
本文系统讲解Spring Boot集成MyBatis的两种方式:基于XML和注解。涵盖依赖配置、yml设置、驼峰命名映射,并详解@Select、@Insert等注解用法及@Param、@Results问题解决方案,结合实战示例,具有较强实用性,适用于日常开发参考。
|
11月前
|
前端开发 JavaScript Java
Spring Boot中使用拦截器
本文详解Spring Boot拦截器的原理与使用,涵盖定义、配置、静态资源处理及登录校验、注解控制等实战场景,助你掌握AOP思想在请求拦截中的应用。