【SpringCloud Alibaba】(二)微服务环境搭建

1. 项目流程搭建

整个项目主要分为 用户微服务、商品微服务和订单微服务,整个过程模拟的是用户下单扣减库存的操作。这里,为了简化整个流程,将商品的库存信息保存到了商品数据表,同时,使用商品微服务来扣减库存。小伙伴们在实现时,也可以将商品库存信息单独开发一个微服务模块,主体逻辑和将库存信息放在商品微服务进行管理是一样的。各服务之间的调用流程如下:
在这里插入图片描述
用户微服务、商品微服务和订单微服务的整体流程为:用户通过客户端调用订单微服务的提交订单的接口后,订单微服务会分别调用用户微服务和商品微服务的接口来查询用户信息和商品信息,并校验商品库存是否充足,如果商品库存充足的话,就会保存订单。并且会调用商品微服务的扣减库存的接口来扣减库存

2. 技术选型

整个项目采用 SpringCloud Alibaba 技术栈实现,主要的技术选型如下所示:

  • 持久层框架:MyBatis、MyBatis-Plus
  • 微服务框架:SpringCloud Alibaba
  • 消息中间件:RocketMQ
  • 服务治理与服务配置:Nacos
  • 负载均衡组件:Ribbon
  • 远程服务调用:Fegin
  • 服务限流与容错:Sentinel
  • 服务网关:SpringCloud-Gateway
  • 服务链路追踪:Sleuth + ZipKin
  • 分布式事务:Seata
  • 数据存储:MySQL+ElasticSearch

3. 模块划分

为了方便开发和维护,同时为了模块的复用性,整体项目在搭建时,会将用户微服务、商品微服务和订单微服务放在同一个 Maven 父工程下,作为父工程的子模块,同时,将用户微服务、商品微服务和订单微服务都会使用的 JavaBean 单独作为一个 Maven 模块,以及各服务都会使用的工具类单独作为一个 Maven 模块

在这里插入图片描述
其中各模块的说明如下所示:

  • shop-springcloud-alibaba:Maven 父工程。
  • shop-bean:各服务都会使用的 JavaBean 模块,包含实体类、Dto、Vo 等 JavaBean。
  • shop-utils:各服务都会使用的工具类模块。
  • shop-order:订单微服务。
  • shop-product:商品微服务。
  • shop-user:用户微服务

4. 代码地址

代码码云地址

其中,数据库文件位于 db 文件夹下。

5. 模块开发

代码已托管到码云,这里只贴部分代码。

5.1 创建 maven 父工程

在IDEA中创建 Maven 工程,名称为 shop-springcloud-alibaba,创建后在项目的 pom.xml 文件中添加
StringBoot 与 SpringCloud alibaba 相关的配置,如下所示:

<?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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/>
    </parent>
    <packaging>pom</packaging>
    <groupId>com.zzc</groupId>
    <artifactId>shop-springcloud-alibaba</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>shop-springcloud-alibaba</name>
    <description>shop-springcloud-alibaba</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
        <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
        <logback.version>1.1.7</logback.version>
        <slf4j.version>1.7.21</slf4j.version>
        <common.logging>1.2</common.logging>
        <fastjson.version>1.2.51</fastjson.version>
        <mybatis.version>3.4.6</mybatis.version>
        <mybatis.plus.version>3.4.1</mybatis.plus.version>
        <mysql.jdbc.version>8.0.19</mysql.jdbc.version>
        <druid.version>1.1.10</druid.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <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>
        </dependencies>
    </dependencyManagement>

</project>

5.2 创建工具类模块

在父工程下创建工具类模块 shop-utils,作为整个项目的通用工具类模块。工具类模块的总体结构如下所示:
在这里插入图片描述
代码结构说明:

  1. HttpCode:HTTP 状态码封装类
  2. RestCtrlExceptionHandler:全局异常捕获类
  3. md5 包:通用 MD5 与密码加密类
  4. Result:数据响应类
  5. id 包:使用雪花算法生成 Id

5.3 创建其它微服务模块

5.3.1 创建实体类模块

在父工程下创建实体类模块 shop-bean,作为整个项目的通用实体类模块

shop-bean 模块的依赖相对来说就比较简单了,只需要依赖 shop-utils 模块即可。在 shop-bean 模块的 pom.xml 文件中添加如下配置:

<dependency>
    <groupId>com.zzc</groupId>
    <artifactId>shop-utils</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

5.3.2 创建三大微服务并完成交互

对于用户微服务、商品微服务和订单微服务来说,每个服务占用的端口和访问的基础路径是不同的,这里就将每个服务占用的端口和访问的基础路径整理成下表所示:

服务名称项目名称占用端口访问的基础路径备注
用户微服务shop-user8060/user用户的增删改查
商品微服务shop-product8070/product商品的增删改查
订单微服务shop-order8080/order订单的增删改查

创建微服务

创建名称为 shop-user 的 Maven 项目,由于我们在前面的文章中,已经完成了对项目整体结构的搭建,所以,在 shop-user 的 pom.xml 文件里添加如下依赖即可:

<dependency>
    <groupId>com.zzc</groupId>
    <artifactId>shop-bean</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

shop-productshop-order 微服务与 shop-user 类似,只是配置文件 application.yml 配置端口、访问路径不同。

6. 订单微服务下单接口

这里重点看看 shop-order 的下单接口:

@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrder(OrderParamVo orderParamVo) {
    if (orderParamVo.isEmpty()){
        throw new RuntimeException("参数异常: " + JSONObject.toJSONString(orderParamVo));
    }
    User user = restTemplate.getForObject("http://localhost:8060/user/get/" + orderParamVo.getUserId(), User.class);
    if (user == null){
        throw new RuntimeException("未获取到用户信息: " + JSONObject.toJSONString(orderParamVo));
    }
    Product product = restTemplate.getForObject("http://localhost:8070/product/get/" + orderParamVo.getProductId(), Product.class);
    if (product == null){
        throw new RuntimeException("未获取到商品信息: " + JSONObject.toJSONString(orderParamVo));
    }
    if (product.getProStock() < orderParamVo.getCount()){
        throw new RuntimeException("商品库存不足: " + JSONObject.toJSONString(orderParamVo));
    }
    Order order = new Order();
    order.setAddress(user.getAddress());
    order.setPhone(user.getPhone());
    order.setUserId(user.getId());
    order.setUsername(user.getUsername());
    order.setTotalPrice(product.getProPrice().multiply(BigDecimal.valueOf(orderParamVo.getCount())));
    baseMapper.insert(order);
    OrderItem orderItem = new OrderItem();
    orderItem.setNumber(orderParamVo.getCount());
    orderItem.setOrderId(order.getId());
    orderItem.setProId(product.getId());
    orderItem.setProName(product.getProName());
    orderItem.setProPrice(product.getProPrice());
    orderItemMapper.insert(orderItem);
    Result<Integer> result = restTemplate.getForObject("http://localhost:8070/product/update_count/" + orderParamVo.getProductId() + "/" + orderParamVo.getCount(), Result.class);
    if (result.getCode() != HttpCode.SUCCESS){
        throw new RuntimeException("库存扣减失败");
    }
    log.info("库存扣减成功");
}

saveOrder() 方法的实现中,实现的主要逻辑如下:

  1. 判断 orderParams 封装的参数是否为空,如果参数为空,则抛出参数异常。
  2. 通过 RestTemplate 调用用户微服务获取用户的基本信息,如果获取的用户信息为空,则抛出未获
    取到用户信息的异常。
  3. 通过 RestTemplate 调用商品微服务获取商品的基本信息,如果获取的商品信息为空,则抛出未获
    取到商品信息的异常。
  4. 判断商品的库存是否小于待扣减的商品数量,如果商品的库存小于待扣减的商品数量,则抛出商品
    库存不足的异常。
  5. 如果 orderParams 封装的参数不为空,并且获取的用户信息和商品信息不为空,同时商品的库存
    充足,则创建订单对象保存订单信息,创建订单条目对象,保存订单条目信息。
  6. 调用商品微服务的接口扣减商品库存

在上述实现的过程中,存在一个很明显的问题:那就是将用户微服务所在的IP和端口,以及商品微服务所在的IP和端口硬编码到订单微服务的代码中了。这样的做法存在着非常多的问题

硬编码的问题

如果将用户微服务和商品微服务所在的IP地址和端口号硬编码到订单微服务中,会存在非常多的问题,其中,最明显的问题有三个,如下所示。

  1. 如果用户微服务和商品微服务的IP地址或者端口号发生了变化,则订单微服务将变得不可用,需要
    对同步修改订单微服务中调用用户微服务和商品微服务的IP地址和端口号。
  2. 如果系统中提供了多个用户微服务和商品微服务,则无法实现微服务的负载均衡功能。
  3. 如果系统需要支持更高的并发,需要部署更多的用户微服务和商品微服务以及订单微服务,如果将用户微服务和商品微服务的IP地址和端口硬编码到订单微服务,则后续的维护会变得异常复杂。

所以,在微服务开发的过程中,需要引入服务治理功能,实现微服务之间的动态注册与发现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/45986.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Rust vs Go:常用语法对比(十一)

题目来自 Rust Vs Go: Which Language Is Better For Developing High-Performance Applications?[1] 202. Sum of squares Calculate the sum of squares s of data, an array of floating point values. 计算平方和 package mainimport ( "math")func main() { da…

android数据的储存、文件的储存、SharedPreferences储存、SQLite的基本用法

一、文件的储存 1、将数据储存到文件中 Context类中提供了openfileOutput()方法&#xff0c;用来获取一个文件流&#xff0c;这个方法接收两个参数&#xff0c;第一个参数是文件名&#xff0c;在文件创建的时候使用的就是这个名称&#xff0c;注意这里指定的文件名不可以包含…

React AntDesign写一个导出数据的提示语 上面有跳转的路径,或者点击知道了,关闭该弹层

效果如下&#xff1a; 代码如下&#xff1a; ForwardDataCenterModal(_blank);export const ForwardDataCenterModal (target?: string) > {let contentBefore React.createElement(span, null, 数据正在处理中&#xff0c;请稍后前往);let contentAfter React.creat…

JAVA基础-集合(List与Map)

目录 引言 一&#xff0c;Collection集合 1.1,List接口 1.1.1&#xff0c;ArrayList 1.1.1.1&#xff0c;ArrayList的add&#xff08;&#xff09;添加方法 1.1.1.2&#xff0c;ArrayList的remove&#xff08;&#xff09;删除方法 1.1.1.3&#xff0c;ArrayList的contai…

网络超时导致namenode被kill的定位

交换机升级导致部分网络通信超时, 集群的namenode主从切换后,主namenode进程被杀死。 网络问题导致namenode与zk间的连接超时触发了hadoop集群的防脑裂机制而主动kill掉了超时的namenode进程。 日志分析发现zk和namenode之间的网络连接超时: 超时触发了namenode切换,并将超时…

游戏引擎UE如何革新影视行业?创意云全面支持UE云渲染

虚幻引擎UE&#xff08;Unreal Engine&#xff09;作为一款“殿堂级”的游戏引擎&#xff0c;占据了全球80%的商用游戏引擎市场&#xff0c;但如果仅仅将其当做游戏开发的工具&#xff0c;显然是低估了它的能力。比如迪士尼出品的电视剧《曼达洛人》、电影《狮子王》等等都使用…

白话机器学习笔记(三)评估已建立的模型

模型评估 在进行回归和分类时&#xff0c;为了进行预测&#xff0c;我们定义了函数 f θ ( x ) f_\theta(x) fθ​(x)&#xff0c;然后根据训练数据求出了函数的参数 θ \theta θ。 如何预测函数 f θ ( x ) f_\theta(x) fθ​(x)的精度&#xff1f;看它能否很好的拟合训练数…

【Django学习】(十五)API接口文档平台_项目流程分析_日志器_认证_授权

一、API接口文档平台 使用API接口文档不经可以很好的的维护接口数据&#xff0c;还给测试人员的接口测试工作带来了便利&#xff1b; 我们可以在全局配置文件中添加路由路径生成接口文档 1、使用docs接口文档维护接口 1.1在全局配置文件里指定用于支持coreapi的Schema # 指…

Linux の shell 流程控制

条件控制 # if then 如果else 没有语句 可以省略 if condition then#语句 fi# if then 。。。 else 。。。 fi if condition then#语句 else#语句 fi# if condition then#语句 elif condition2 then#语句 else#语句 fiif [ $a -gt $b ] thenecho "a > b&quo…

骆驼祥子思维导图

《骆驼祥子》简单介绍 《骆驼祥子》小说&#xff0c;以20世纪20年代的旧北京为背景。祥子所处的时代是北洋军阀统治的时代。今天我们就用ProcessOn 思维导图 来给大家解析这本名著。所有文章中的思维导图都可以到ProcessOn 模板社区获得。 1936年&#xff0c;老舍的一位山东大…

[vulnhub]DC2

文章目录 [vulnhub]DC2信息收集flag1flag2cewlwpscan flag3什么是rbash&#xff1f; flag4flag5git提权 总结 [vulnhub]DC2 信息收集 扫ip&#xff0c;有两种方式&#xff1a;arp、nmap nmap -sP 192.168.56.0/24 -T4arp-scan -l192.168.56.137 扫端口&#xff1a; nmap -…

Mendix 创客访谈录|综合业务展示大屏应用开发

本期创客 刘书智 西门子工业领域专家 我在西门子工厂自动化工程有限公司工作。一直从事SCADA产品的技术支持工作&#xff0c;已经过去17个年头了。赶上数字化发展的浪潮&#xff0c;不断学习各种IT技术&#xff0c;践行 IT与OT融合&#xff0c;希望借助自己的IT知识助力OT的发…

力扣1114.按序打印-----题目解析

题目描述 解析&#xff1a; class Foo {public int a 0;public Foo() {}public void first(Runnable printFirst) throws InterruptedException {// printFirst.run() outputs "first". Do not change or remove this line.printFirst.run();a;}public void second…

vue全局状态管理工具 Pinia 的使用

先了解一下关于Pinia的一些故事&#xff0c;面试把这些讲给面试官挺加分的&#xff0c;同时这是我持续学习下去的动力 1.为什么叫Pinia&#xff1f; 官网解释是西班牙语中的 pineapple&#xff0c;即“菠萝”&#xff0c;菠萝花是一组各自独立的花朵&#xff0c;它们结合在一起…

【C语言】函数----详解

&#x1f341; 博客主页:江池俊的博客 &#x1f4ab;收录专栏&#xff1a;C语言——探索高效编程的基石 &#x1f4bb; 其他专栏&#xff1a;数据结构探索 &#x1f3e9;代码仓库&#xff1a;江池俊的代码仓库 &#x1f3aa; 社区&#xff1a;C/C之家社区(欢迎大家加入与我一起…

安装VMware

D:\VMware\VMware Workstation\ 输入许可证

Sentinel 规则持久化到 Nacos

一、Sentinel规则管理模式&#x1f349; Sentinel的控制台规则管理有三种模式&#xff1a; 原始模式&#x1f95d; 原始模式&#xff1a;控制台配置的规则直接推送到Sentinel客户端&#xff0c;也就是我们的应用。然后保存在内存中&#xff0c;服务重启则丢失 pull模式&#…

一文详解Spring Bean循环依赖

一、背景 有好几次线上发布老应用时&#xff0c;遭遇代码启动报错&#xff0c;具体错误如下&#xff1a; Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name xxxManageFacadeImpl: Bean with name xxxManageFa…

实验数据origin作图使用经验总结

使用Origin绘制实验数据图表时&#xff0c;可以遵循以下经验总结&#xff1a; 选择合适的图表类型&#xff1a; 根据实验数据的性质和目的&#xff0c;选择合适的图表类型&#xff0c;例如散点图、折线图、柱状图、饼图等。确保图表类型能够清晰地展示数据趋势和关系。 规范坐…

常用API学习08(Java)

格式化 格式化指的是将数据按照指定的规则转化为指定的形式 。 那么为什么需要格式化&#xff1f;格式化有什么用&#xff1f; 以数字类为例&#xff0c;假设有一个比分牌&#xff0c;在无人得分的时候我们希望以&#xff1a;“00&#xff1a;00”的形式存在&#xff0c;那么…