微服务上(黑马)

news2024/11/24 2:24:59

文章目录

  • 微服务01
    • 1 认识微服务
      • 1.1 单体架构
      • 1.2 微服务
      • 1.3 SpringCloud
    • 2 微服务拆分
      • 2.1 熟悉黑马商城
      • 2.2 服务拆分原则
        • 2.2.1.什么时候拆
        • 2.2.2.怎么拆
      • 2.3 拆分服务
        • 2.3.1 拆分商品管理功能模块
        • 2.3.2 拆分购物车功能模块
      • 2.4 远程调用
        • 2.4.1 RestTemplate
        • 2.4.2.远程调用
      • 2.5 总结
    • 3 服务注册与发现
      • 3.1.注册中心原理
      • 3.2 Nacos注册中心
      • 3.3 服务注册
      • 3.4 服务发现
    • 4 OpenFeign
      • 4.1 快速入门
      • 4.2 连接池
      • 4.3 最佳实践
        • 4.3.1.思路分析
        • 4.3.2 扫描包
      • 4.4 日志
      • 4.5 总结

微服务01

1 认识微服务

1.1 单体架构

单体架构: 将业务的所有功能集中在一个项目中开发,打成一个包部署。

优点:

  • 架构简单
  • 部署成本低

缺点:

  • 团队协作成本高
  • 系统发布效率低
  • 系统可用性差(当某一个服务负载过大,比如访问量太大,导致此服务崩溃,由于所有服务公用一个资源空间,其它服务也会受到影响,崩溃)
    在这里插入图片描述

总结:单体架构适合开发功能相对简单,规模较小的项目。

1.2 微服务

微服务架构: 是服务化思想指导下的一套最佳实践架构方案。服务化,就是把单体架构中的功能模块拆分为多个独立项目。

优点:

  • 粒度小
  • 团队自治
  • 服务自治

1.3 SpringCloud

SpringCloud是目前国内使用最广泛的微服务框架。官网地址:https://spring.io/projects/spring-cloud.

SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的我开箱即用体验:

在这里插入图片描述

对于SpringBoot的版本也有要求
在这里插入图片描述

2 微服务拆分

2.1 熟悉黑马商城

在这里插入图片描述

2.2 服务拆分原则

2.2.1.什么时候拆
  • 创业型项目: 先采用单体架构,快速开发,快速试错。随着规模扩大,逐渐拆分。
  • 确定的大型项目: 资金充足,项目明确,可以直接选择微服务架构,避免后续拆分的麻烦。
2.2.2.怎么拆

之前我们说过,微服务拆分时粒度要小,这其实是拆分的目标。具体可以从两个角度来分析:

  • 高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
  • 低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强。

高内聚首先是单一职责,但不能说一个微服务就一个接口,而是要保证微服务内部业务的完整性为前提。目标是当我们要修改某个业务时,最好就只修改当前微服务,这样变更的成本更低。

一旦微服务做到了高内聚,那么服务之间的耦合度自然就降低了。

当然,微服务之间不可避免的会有或多或少的业务交互,比如下单时需要查询商品数据。这个时候我们不能在订单服务直接查询商品数据库,否则就导致了数据耦合。而应该由商品服务对应暴露接口,并且一定要保证微服务对外接口的稳定性(即:尽量保证接口外观不变)。虽然出现了服务间调用,但此时无论你如何在商品服务做内部修改,都不会影响到订单微服务,服务间的耦合度就降低了。

明确了拆分目标,接下来就是拆分方式了。我们在做服务拆分时一般有两种方式:

  • 纵向拆分: 按照业务模块来拆分
  • 横向拆分: 抽取公共服务,提高复用性

所谓纵向拆分,就是按照项目的功能模块来拆分。例如黑马商城中,就有用户管理功能、订单管理功能、购物车功能、商品管理功能、支付功能等。那么按照功能模块将他们拆分为一个个服务,就属于纵向拆分。这种拆分模式可以尽可能提高服务的内聚性。

横向拆分,是看各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务。例如用户登录是需要发送消息通知,记录风控数据,下单时也要发送短信,记录风控数据。因此消息发送、风控数据记录就是通用的业务功能,因此可以将他们分别抽取为公共服务:消息中心服务、风控管理服务。这样可以提高业务的复用性,避免重复开发。同时通用业务一般接口稳定性较强,也不会使服务之间过分耦合。

当然,由于黑马商城并不是一个完整的项目,其中的短信发送、风控管理并没有实现,这里就不再考虑了。而其它的业务按照纵向拆分,可以分为以下几个微服务:

  • 用户服务
  • 商品服务
  • 订单服务
  • 购物车服务
  • 支付服务

2.3 拆分服务

工程结构有两种:

  • 独立Project(适用淘宝等大型复杂工程)
    在这里插入图片描述

  • Maven聚合(中小型企业 相对简单的工程)
    在这里插入图片描述

2.3.1 拆分商品管理功能模块

将hm-service中与商品管理相关功能拆分到一个微服务module中,命名为item-service

2.3.2 拆分购物车功能模块

将hm-service中与购物车有关的功能拆分到一个微服务module中,命名为cart-service

2.4 远程调用

在拆分的时候,我们发现一个问题:就是购物车业务中需要查询商品信息,但商品信息查询的逻辑全部迁移到了item-service服务,导致我们无法查询。

最终结果就是查询到的购物车数据不完整,因此要想解决这个问题,我们就必须改造其中的代码,把原本本地方法调用,改造成跨微服务的远程调用(RPC,即Remote Produce Call)。

因此,现在查询购物车列表的流程变成了这样:

在这里插入图片描述

代码中需要变化的就是这一步:

在这里插入图片描述

那么问题来了:我们该如何跨服务调用,准确的说,如何在cart-service中获取item-service服务中的提供的商品数据呢?

image-20240724092022546

前端向服务端查询数据,其实就是从浏览器远程查询服务端数据。比如我们刚才通过Swagger测试商品查询接口,就是向http://localhost:8081/items这个接口发起的请求:而这种查询就是通过http请求的方式来完成的,不仅仅可以实现远程查询,还可以实现新增、删除等各种远程请求。

2.4.1 RestTemplate

Spring给我们提供了一个ResTemplate工具,可以方便的实现Http请求的发送。其中提供了大量的方法,方便我们发送Http请求,例如:

可以看到常见的Get、Post、Put、Delete请求都支持,如果请求参数比较复杂,还可以使用exchange方法来构造请求。

使用步骤如下:

  • 注入RestTemplate到Spring容器

在这里插入图片描述

  • 发起远程调用
2.4.2.远程调用

接下来,我们修改cart-service中的com.hmall.cart.service.impl.``CartServiceImplhandleCartItems方法,发送http请求到item-service

可以看到,利用RestTemplate发送http请求与前端ajax发送请求非常相似,都包含四部分信息:

  • ① 请求方式
  • ② 请求路径
  • ③ 请求参数
  • ④ 返回值类型

2.5 总结

3 服务注册与发现

在上一章我们实现了微服务拆分,并且通过Http请求实现了跨微服务的远程调用。不过这种手动发送Http请求的方式存在一些问题。

试想一下,假如商品微服务被调用较多,为了应对更高的并发,我们进行了多实例部署,如图:

此时,每个item-service的实例其IP或端口不同,问题来了:

  • item-service这么多实例,cart-service如何知道每一个实例的地址?
  • http请求要写url地址,cart-service服务到底该调用哪个实例呢?
  • 如果在运行过程中,某一个item-service实例宕机,cart-service依然在调用该怎么办?
  • 如果并发太高,item-service临时多部署了N台实例,cart-service如何知道新实例的地址?

为了解决上述问题,就必须引入注册中心的概念了,接下来我们就一起来分析下注册中心的原理。

3.1.注册中心原理

流程如下:

  • 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心
  • 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)
  • 调用者自己对实例列表负载均衡,挑选一个实例
  • 调用者向该实例发起远程调用

当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?

  • 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求)
  • 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除
  • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表
  • 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表

在这里插入图片描述

3.2 Nacos注册中心

Nacos是目前国内企业中占比最多的注册中心组件。它是阿里巴巴产品,目前已经加入SpringCloudAlibaba中。

3.3 服务注册

服务注册步骤如下:

  • 引入nacos discovery依赖:

  • 配置Nacos地址

3.4 服务发现

消费者 需要连接nacos以拉取和订阅服务,因此服务发现的前两步与服务注册是一样,后面再加上服务调用即可:

  • 引入nacos discovery

  • 配置Nacos地址

  • 服务发现

// 2.查询商品
        // 2.1 根据服务名称获取服务的实例列表
        List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
        if(CollUtils.isEmpty(instances)){
            return;
        }
        // 2.2 手写负载均衡 从实例列表中挑选一个实例
        ServiceInstance serviceInstance = instances.get(RandomUtil.randomInt(instances.size()));
        // 2.3 利用ResTemplate发起http请求,得到http的响应
        ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                serviceInstance.getUri() + "/items?ids={ids}",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<ItemDTO>>() {
                },
                Map.of("ids", CollUtil.join(itemIds, ","))
        );
        //2.4 解析响应
        if(!response.getStatusCode().is2xxSuccessful()){
            //查询失败 直接结束
            return;
        }
//        List<ItemDTO> items = itemService.queryItemByIds(itemIds);
        List<ItemDTO> items = response.getBody();
        if (CollUtils.isEmpty(items)) {
            return;
        }

4 OpenFeign

在上一章,我们利用Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。但是远程调用的代码太复杂了:

而且这种调用方式,与原本的本地方法调用差异太大,编程时的体验也不统一,一会儿远程调用,一会儿本地调用。

因此,我们必须想办法改变远程调用的开发模式,让远程调用像本地方法调用一样简单。而这就要用到OpenFeign组件了。

其实远程调用的关键点就在于四个:

  • 请求方式
  • 请求路径
  • 请求参数
  • 返回值类型

所以,OpenFeign就利用SpringMVC的相关注解来声明上述4个参数,然后基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写,非常方便。

4.1 快速入门

OpenFeign是一个声明式的http客户端,是SpringCloud在Eureka公司开源的Feign基础上改造而来。其作用就是基于SpringMVC的常见注解,帮我们优雅地实现http请求的发送。

OpenFeign已经被SpringCloud自动装配,实现起来非常简单

  • 引入依赖,包括OpenFeign和负载均衡组件SpringCloudLoadBalancer

  • 通过@EnableFeignClients注解,启用OpenFeign功能

  • 编写FeignClient,这段代码就是代替了之前写的一大坨代码(发现实例,负载均衡,发送请求)

    这里只需要声明接口,无需实现方法。接口中的几个关键信息:

    • @FeignClient("item-service") :声明服务名称 localhost:8081 localhost8083

    • @GetMapping :声明请求方式 Get

    • @GetMapping("/items") :声明请求路径 /items

    • @RequestParam("ids") Collection<Long> ids :声明请求参数 /{ids}

      以上拼起来路径就是 Get方式的 https://localhost:8081/items?ids=1,2,3

    • List<ItemDTO> :返回值类型

    有了上述信息,OpenFeign就可以利用动态代理帮我们实现这个方法,并且向http://item-service/items发送一个GET请求,携带ids为请求参数,并自动将返回值处理为List<ItemDTO>

    我们只需要直接调用这个方法,即可实现远程调用了

  • 使用FeignClient,实现远程调用

4.2 连接池

OpenFeign对http请求做了优雅的封装,不过其底层发起http请求,依赖于其它的框架。这些框架可以自己选择,包括一下三种:

  • HttpURLConnection: 默认实现。不支持连接池
  • Apache HttpClient: 支持连接池
  • OKHttp: 支持连接池

这里使用的是okhttp

  • 引入依赖

    <!--OK http 的依赖 -->
    <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-okhttp</artifactId>
    </dependency>
    
  • 开启连接池

    feign:
      okhttp:
        enabled: true # 开启OKHttp功能
    

4.3 最佳实践

如果拆分了交易微服务(trade-service),它也需要远程调用item-service中的根据id批量查询商品功能。这个需求与cart-service中是一样的。

因此,我们就需要在trade-service中再次定义ItemClient接口,这不是重复编码吗? 有什么办法能加避免重复编码呢?

4.3.1.思路分析

避免重复编码的办法就是抽取。不过这里有两种抽取思路:

  • 思路1:抽取到微服务之外的公共module

  • 思路2:每个微服务自己抽取一个module
    在这里插入图片描述

方案1抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高。

方案2抽取相对麻烦,工程结构相对更复杂,但服务之间耦合度降低。

那么该选择方案:

我们知道拆分服务分为两种结构,一种是project独立结构,一种是Maven聚合结构。对于Project独立结构,本身每一个模块就是一个工程,耦合度低,也更方便采用方案2的方法;对于Maven聚合结构,由于耦合度稍微高一点,且本身就带有common通用模块,再多一个通用模块也无所谓,因此建议选择方案一。不过其实如果不嫌麻烦的话,还是建议方案二。

对于本使用了Maven结构的项目来说,选择方案1。

4.3.2 扫描包

当定义的FeignClient不在SpringBootApplicaiton的扫描包范围时,这些FeignClient无法使用,有两种方式解决

方式一:指定FeignClient所在包

方式二:指定FeignClient字节码

4.4 日志

OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:

  • NONE: 不记录任何日志信息,这是默认值
  • BASIC: 仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS: 在BASUC的基础上,额外记录了请求和响应的头信息
  • FULL: 记录所有请求和响应的明细,包括头信息、请求体、元数据

由于Feign默认的日志级别是NONE,所以默认我们看不到请求日志

要自定义日志级别需要声明一个类型为Logger.Level的Bean,在其中定义日志级别:

但此时这个Bean并未生效,要想配置某个FeignClient的日志,可以在@FeignClient注解中声明:

如果想要全局配置,让所有FeignClient都按照这个日志配置,则需要在@EnableFeignClients注解中声明:

4.5 总结

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-21 SpringBoot微服务的发布与部署(3种方式)

基于 SpringBoot 的微服务开发完成之后&#xff0c;现在到了把它们发布并部署到相应的环境去运行的时候了。 SpringBoot 框架只提供了一套基于可执行 jar 包&#xff08;executable jar&#xff09;格式的标准发布形式&#xff0c;但并没有对部署做过多的界定&#xff0c;而且为…

25届最近5年广东工业大学自动化考研院校分析

广东工业大学 目录 一、学校学院专业简介 二、考试科目指定教材 三、近5年考研分数情况 四、近5年招生录取情况 五、最新一年分数段图表 六、初试大纲复试大纲 七、学费&奖学金&就业方向 一、学校学院专业简介 二、考试科目指定教材 1、考试科目介绍 2、指定教…

【在排序数组中查找元素的第一个和最后一个位置】python刷题记录

R2-分治 有点easy的感觉&#xff0c;感觉能用哈希表 class Solution:def searchRange(self, nums: List[int], target: int) -> List[int]:nlen(nums)dictdefaultdict(list)#初始赋值哈希表&#xff0c;记录出现次数for num in nums:if not dict[num]:dict[num]1else:dict[…

(深层与双向)循环神经网络

一、深层循环神经网络 1、对于循环神经网络 2、对于深层&#xff0c;要得到更多的非线性&#xff0c;就像多层感知机&#xff08;MLP&#xff09;。 &#xff08;1&#xff09;浅层与深层对比 这是具有&#x1d43f;个隐藏层的深度循环神经网络&#xff0c; 每个隐状态都连续…

【QT】QT 系统相关(事件、文件、多线程、网络、音视频)

一、Qt 事件 1、事件介绍 事件是应用程序内部或者外部产生的事情或者动作的统称。在 Qt 中使用一个对象来表示一个事件。所有的 Qt 事件均继承于抽象类 QEvent。事件是由系统或者 Qt 平台本身在不同的时刻发出的。当用户按下鼠标、敲下键盘&#xff0c;或者是窗口需要重新绘制…

《python程序语言设计》第6章14题 估算派值 类似莱布尼茨函数。但是我看不明白

这个题提供的公式我没看明白&#xff0c;后来在网上找到了莱布尼茨函数 c 0 for i in range(1, 902, 100):a (-1) ** (i 1)b 2 * i - 1c a / bprint(i, round(4 / c, 3))结果 #按题里的信息&#xff0c;但是结果不对&#xff0c;莱布尼茨函数到底怎么算呀。

【计算机网络】TCP协议详解

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 1、引言2、udp和tcp协议的异同3、tcp服务器3.1、接口认识3.2、服务器设计 4、tcp客户端4.1、客户端设计4.2、说明 5、再研Tcp服务端5.1、多进程版5.2、多线程版 5、守护进程化5.1、什么是守护进程5.2…

Javascript面试基础6(下)

获取页面所有checkbox 怎样添加、移除、移动、复制、创建和查找节点 在JavaScript中&#xff0c;操作DOM&#xff08;文档对象模型&#xff09;是常见的任务&#xff0c;包括添加、移除、移动、复制、创建和查找节点。以下是一些基本的示例&#xff0c;说明如何执行这些操作&a…

【网络世界】HTTP协议

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 概念 &#x1f4c1; URL &#x1f4c2; urlencode 和 urldecode &#x1f4c1; 协议格式 &#x1f4c1; 方法 &#x1f4c2; GET/get &#x1f4c2; POST/post &#x1f4c1; 常见的报头 &#x1f4c1; 状态码 &…

Web3 职场新手指南:从技能到素养,求职者如何脱颖而出?

随着 2024 年步入下半年&#xff0c;Web3 行业正在经历一系列技术革新。通过改进的跨链交互机制和兼容性&#xff0c;逐步消除市场碎片化的问题。技术的进步为开发者和用户都打开了新的前景。然而&#xff0c;复杂的技术和快速变化的市场环境也让许多新人望而却步。求职者如何找…

编译固件 -- 自用

编译环境 先安装编译环境 git clone <编译仓库路径> git checkout <编译主分支> 更新/下载 然后就是一样的更新下载 ./scripts/feeds update -a ./scripts/feeds install -a 然后直接编译 feeds/puppies/rom/scripts/make.sh 对应型号 make Vs 这里的对应型号可…

gitee的fork

通过fork操作&#xff0c;可以复制小组队长的库。通过复制出一模一样的库&#xff0c;先在自己的库修改&#xff0c;最后提交给队长&#xff0c;队长审核通过就可以把你做的那一份也添加入库 在这fork复制一份到你自己的仓库&#xff0c;一般和这个项目同名 现在你有了自己的库…

Footprint Analytics 助力 Core 区块链实现数据效率突破

Core 是一个基于比特币并兼容 EVM 的 Layer 1 区块链&#xff0c;正通过其创新解决方案引革新特币金融。作为首个引入非托管 BTC 质押协议及全球首个发行收益型 BTC ETP 产品的区块链&#xff0c;Core 站在了区块链技术的最前沿。通过利用超过 50% 的比特币挖矿哈希算力&#x…

24暑假算法刷题 | Day22 | LeetCode 77. 组合,216. 组合总和 III,17. 电话号码的字母组合

目录 77. 组合题目描述题解 216. 组合总和 III题目描述题解 17. 电话号码的字母组合题目描述题解 77. 组合 点此跳转题目链接 题目描述 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输…

python爬虫入门小案例

python爬虫 以下内容仅供学习交流,请勿用作其他用途,若涉及隐私和版权问题,请及时联系我删除 闲来无事,学了学爬虫小知识,适合入门,文笔拙劣,还望见谅 爬虫是什么: 爬取网页上的文字,图片,视频,音频 自动化操作浏览器,比如填写表单,打卡,提高工作效率爬虫的注意事项: 爬虫…

lua 游戏架构 之 游戏 AI (九)ai_mgr Ai管理

定义ai_mgr的类&#xff0c;用于管理游戏中实体的AI组件。 先定义 AI行为枚举和优先级&#xff1a; lua 游戏架构 之 游戏 AI &#xff08;八&#xff09;ai_tbl 行为和优先级-CSDN博客https://blog.csdn.net/heyuchang666/article/details/140712839?spm1001.2014.3001.55…

MySQL环境的配置文件json

突然了解到&#xff0c;使用json文件去进行环境的配置&#xff0c;这样修改参数的时候就只需要去改json文件中的内容&#xff0c;不需要去修改代码中的内容&#xff0c;其他人的MySQL和我的MySQL也不同&#xff0c;这时其他人只需要修改json文件中的内容&#xff0c;清晰明了&a…

基于微信小程序+SpringBoot+Vue的核酸检测服务系统(带1w+文档)

基于微信小程序SpringBootVue的核酸检测服务系统(带1w文档) 基于微信小程序SpringBootVue的核酸检测服务系统(带1w文档) 在目前的情况下&#xff0c;可以引进一款医院核酸检测服务系统这样的现代化管理工具&#xff0c;这个工具就是解决上述问题的最好的解决方案。它不仅可以实…

2024年开发者最爱用的Bug跟踪工具

国内外主流的10款BUG管理软件对比&#xff1a;PingCode、Worktile、禅道&#xff08;ZenTao&#xff09;、Bugzilla、Tapd、CODING、Teambition、Testin、Tower、乐道。 在软件开发的世界里&#xff0c;管理和跟踪Bug是一个让许多开发者头疼的问题。选择一个合适的Bug管理工具不…

C++题目_逃生路线总数(dfs)

题目描述 2021年夏天&#xff0c;LSH开开心心的骑着电动车出去玩&#xff0c;结果一不留神&#xff0c;他骑着电动车进入了一只恶犬的领地。恶犬发现它的领地被LSH侵犯了&#xff0c;立马去追LSH&#xff0c;准备咬他一大口。LSH慌忙逃窜&#xff0c;但是他的电动车电量即将耗…