当前位置: 首页 > news >正文

黑马商城-微服务笔记

image-20250412162934905

认识微服务

单体架构

image-20250412163937564

微服务架构

image-20250412171012550

image-20250412171659023

image-20250412171755023

微服务拆分

image-20250412172723018

image-20250412172923440

服务拆分原则

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

怎么拆分?
从拆分目标来说,要做到:
高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖。

从拆分方式来说,一般包含两种方式:
纵向拆分:按照业务模块来拆分
横向拆分:抽取公共服务,提高复用性

拆分服务

image-20250412180511427

image-20250412180716953

mkdir controller domain service mapper

远程调用

image-20250412211026185

image-20250412211413873

服务治理

注册中心原理

image-20250412215956194

Nacos:注册中心

image-20250412220451736

image-20250412221120953
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

服务发现与负载均衡

image-20250413124852103

OpenFeign

image-20250413131528632

image-20250413131904477

image-20250413132608043

连接池

image-20250413135231346

image-20250413135624659

最佳实践

image-20250413140935610 image-20250413141000997

image-20250413143053015

日志输出

image-20250413152947130

image-20250413153852302

网关路由

快速入门

image-20250413204214456

image-20250413204736700

image-20250413204805119

image-20250413204825789

image-20250413211237708

路由属性

网关路由对应的ava类型是RouteDefinition,其中常见的属性有:
id:路由唯一标示
uri:路由目标地址
predicates:路由断言,判断请求是否符合当前路由。
filters:路由过滤器,对请求或响应做特殊处理。

路由断言

image-20250414163626069

路由过滤器

image-20250414164004541

网关请求处理流程

image-20250414172725716

网关登录校验

image-20250414173813119

自定义过滤器

image-20250414174819694

image-20250414175347369

image-20250414181002766

image-20250414185555092

image-20250414195640151

实现登录校验

需求:在网关中基于过滤器实现登录校验功能
提示:黑马商城是基于WT实现的登录校验,目前相关功能在hm-service模块。我们可以将其中的WT
工具拷贝到gateway模块,然后基于GlobalFilter来实现登录校验。

网关传递用户

image-20250414215531210

@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());}
}

image-20250414222505034

OpenFeign传递用户

image-20250415124457258

image-20250415125525558

image-20250415134401450

配置管理

image-20250415135428582

image-20250415185644510 image-20250415185752473

image-20250415185925619

配置热更新

image-20250415210201596

image-20250415211247748

动态路由

要实现动态路由首先要将路由配置保存到Nacos,当Nacos中的路由配置变更时,推送最新配置到网关,实时更新网关
中的路由信息。
我们需要完成两件事情:
①监听Nacos配置变更的消息
②当配置变更时,将最新的路由信息更新到网关路由表

image-20250415213438182

image-20250415215843819

服务保护

image-20250416182703020

雪崩问题

image-20250416183810692

雪崩问题-解决方案

image-20250416192846887

image-20250416193145709

image-20250416193816026

服务保护技术

image-20250416194239521

sentinel

Sentinel是阿里巴巴开源的一款微服务流量控制组件。官网地址:https:/sentinelquard.io/zh-cn/index.html

image-20250416194504238

java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

我们在cart-service模块中整合sentinel,连接sentinel-dashboard控制台,步骤如下: 1)引入sentinel依赖

<!--sentinel-->
<dependency><groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2)配置控制台

修改application.yaml文件,添加下面内容:

spring:cloud: sentinel:transport:dashboard: localhost:8090

簇点链路

簇点链路,就是单机调用链路。是一次请求进入服务后经过的每一个被Sentinel.监控的资源链。默认Sentinel:会监控
SpringMVC的每一个Endpoint(htp接口)。限流、熔断等都是针对簇点链路中的资源设置的。而资源名默认就是接
口的请求路径:

image-20250416201708215

线程隔离

image-20250416204310424

流控是控制接收请求的速度,线程隔离是最多能接收请求的次数,就算流控设置的再慢,如果线程卡住了的话,不设置线程隔离也会导致资源占用

Fallback

image-20250416211623282

image-20250416211915707

image-20250416212024053

服务熔断

image-20250416220746683

分布式事务

image-20250417115029628

image-20250417121053834

Seata

image-20250417121330967

image-20250417121642616

image-20250417122432675

Docker部署

需要注意,要确保nacos、mysql都在hm-net网络中。如果某个容器不再hm-net网络,可以参考下面的命令将某容器加入指定网络:

docker network connect [网络名] [容器名]

在虚拟机的/root目录执行下面的命令:

docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.100.129 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2

image-20250417144304389

image-20250417145207773

XA模式

image-20250417181648451

seata:data-source-proxy-mode: XA

image-20250417194413872

AT模式

image-20250417201509554

异步通信

RabbitMQ

image-20250417204116873

image-20250417204221821

image-20250417204429397

image-20250417205634865

异步调用

image-20250417205958436

image-20250417210925730

异调用的优势是什么?
耦合度低,拓展性强
异步调用,无需等待,性能好
故障隔离,下游服务故障不影响上游业务
缓存消息,流量削峰填谷

异步调用的问题是什么?
不能立即得到调用结果,时效性差
不确定下游业务执行是否成功
业务安全依赖于Broker的可靠性

image-20250417211547904

追求可用性:Kafka、 RocketMQ 、RabbitMQ

追求可靠性:RabbitMQ、RocketMQ

追求吞吐能力:RocketMQ、Kafka

追求消息低延迟:RabbitMQ、Kafka

安装

docker run \-e RABBITMQ_DEFAULT_USER=itheima \-e RABBITMQ_DEFAULT_PASS=123321 \-v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \--network hm-net\-d \rabbitmq:3.8-management

架构

image-20250417215956333

image-20250417220149499

数据隔离

需求:在RabbitMQ的控制台完成下列操作:
新建一个用户hmall
为hmal用户创建一个virtual host
测试不同virtual host之间的数据隔离现象

java客户端

image-20250418131149174

image-20250418131756056

image-20250418132114120

image-20250418132210745

image-20250418160946501

Work Queues

image-20250418162641962

模拟WorkQueue,实现一个队列绑定多个消费者

image-20250418163535415
消费者消息推送限制

image-20250418165940449

Work模型的使用:

多个消费者绑定到一个队列,可以加快消息处理速度
同一条消息只会被一个消费者处理
通过设置prefetch来控制消费者预取的消息数量,处理完一条再处理下一条,实现能者多劳

Fanout交换机

image-20250418173713507

image-20250418174134837

Direct交换机

image-20250418185608250

image-20250418201850254
Topic交换机

image-20250418204619384

声明队列交换机

image-20250418210956089

image-20250418211309966

image-20250418215446099

消息转换器

image-20250419125812348

image-20250419130027395

image-20250419133851471

发送者消息可靠性

发送者重连

image-20250419151158368

image-20250419152052057

发送者确认

image-20250419153409947

image-20250419153704172

image-20250419153818353

image-20250419153949303

MQ的可靠性

image-20250419163303917

image-20250419180731096

image-20250419180829349

image-20250419180932824

Layz Queue

从RabbitMQ的3.6.0版本开始,就增加了Lazy Queue的概念,也就是惰性队列。
惰性队列的特征如下:
·接收到消息后直接存入磁盘,不再存储到内存
·消费者要消费消息时才会从磁盘中读取并加载到内存(可以提前缓存部分消息到内存,最多2048条)
在3.12版本后,所有队列都是Lazy Queue模式,无法更改。

image-20250419193256976

image-20250419193311662

消费者确认机制

image-20250419200946341

image-20250419201431027

image-20250419212133306

失败消息处理策略

image-20250419213621692

image-20250419213823716

业务幂等性

image-20250419215858666

image-20250419220227298

image-20250419223546161

如何保证支付服务与交易服务之间的订单状态一致性?
首先,支付服务会正在用户支付成功以后利用MQ消息通知交易服务
完成订单状态同步。
其次,为了保证MQ消息的可靠性,我们采用了生产者确认机制、消
费者确认、消费者失败重试等策略,确保消息投递和处理的可靠性。同
时也开启了MQ的持久化,避免因服务宕机导致消息丢失。
最后,我们还在交易服务更新订单状态时做了业务幂等判断,避免
因消息重复消费导致订单状态异常。
如果交易服务消息处理失败,有没有什么兜底方案?

延迟消息

延迟消息:发送者发送消息时指定一个时间,消费者不会立刻收到消息,而是在指定时间之后才收到消息。

image-20250420124204477

死信交换机

当一个队列中的消息满足下列情况之一时,就会成为死信(dead letter):
·消费者使用basic.rejecti或basic.nack声明消费失败,并且消息的requeue参数设置为false
消息是一个过期消息(达到了队列或消息本身设置的过期时间),超时无人消费
要投递的队列消息堆积满了,最早的消息可能成为死信

如果队列通过dead-letter-exchange属性指定了一个交换机,那么该队列中的死信就会投递到这个交换机中。这个交
换机称为死信交换机(Dead Letter Exchange,简称DLX)。

image-20250420124957099

延迟消息插件

image-20250420133100272

安装

https://github.com/rabbitmq/rabbitmq-delayed-message-exchange

因为我们是基于Docker安装,所以需要先查看RabbitMQ的插件目录对应的数据卷。

docker volume inspect mq-plugins

结果如下:

[{"CreatedAt": "2024-06-19T09:22:59+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/mq-plugins/_data","Name": "mq-plugins","Options": null,"Scope": "local"}
]

插件目录被挂载到了/var/lib/docker/volumes/mq-plugins/_data这个目录,我们上传插件到该目录下。

img

接下来执行命令,安装插件:

docker exec -it mq rabbitmq-plugins enable rabbitmq_delayed_message_exchange

运行结果如下:

img

image-20250420134217855

image-20250420134301643

取消超时订单

image-20250420151606101

Elasticsearch分布式搜索

介绍

image-20250420165732329

image-20250420170048403

image-20250420170216145

image-20250420170555243

image-20250420170614203

image-20250420171201447

安装elasticsearch

通过下面的Docker命令即可安装单机版本的elasticsearch:

docker run -d \--name es \-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \-e "discovery.type=single-node" \-v es-data:/usr/share/elasticsearch/data \-v es-plugins:/usr/share/elasticsearch/plugins \-v es-config:/usr/share/elasticsearch/config \--privileged \--network hm-net \-p 9200:9200 \-p 9300:9300 \elasticsearch:7.12.1

安装Kibana

通过下面的Docker命令,即可部署Kibana:

docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=hm-net \
-p 5601:5601  \
kibana:7.12.1

倒排索引

image-20250420190151488

IK分词器

image-20250420192631955

image-20250420192756354

在线安装

docker exec -it es ./bin/elasticsearch-plugin  install https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip
image-20250420200953502

POST /_analyze
{
“analyzer”: “ik_smart”,
“text”: “传智播客开放全日制大学,刘德华这简直泰裤辣啊”
}

分词器的作用是什么?
创建倒排索引时,对文档分词
用户搜索时,对输入的内容分词
K分词器有几种模式?
ik smart:智能切分,粗粒度
ik max word:最细切分,细粒度IK分词器
如何拓展分词器词库中的词条?
利用config目录的IkAnalyzer…cfg.xml文件添加拓展词典
在词典中添加拓展词条

基础概念

image-20250420205805830

image-20250420210616077

Mapping映射属性

mapping是对索引库中文档的约束,常见的mapping.属性包括:
type:字段数据类型,常见的简单类型有:
字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
数值:long、integer、short、byte、double、float,
布尔:boolean
日期:date
对象:object

​ index:是否创建索引,默认为true
​ analyzer:使用哪种分词器
​ properties:该字段的子字段

索引库操作

image-20250420213400823

image-20250420213802922

# 新增索引库
PUT /heima
{"mappings": {"properties": {"info": {"type": "text","analyzer": "ik_smart"},"age": {"type": "byte"},"email": {"type": "keyword","index": false},"name": {"type": "object", "properties": {"firstNmae": {"type": "keyword"},"lastNmae": {"type": "keyword"}}}}}
}# 查询索引库
GET /heima# 删除索引库
DELETE /heima# 修改索引库
PUT /heima/_mapping
{"properties": {"info": {"type": "byte"}}
}

文档操作CRUD

# 新增文档
POST /heima/_doc/1
{"info": "黑马程序员Java讲师","email": "zy@itcast.cn","name": {"firstName": "云","lastName": "赵"}
}# 查询文档
GET /heima/_doc/1# 删除文档
DELETE /heima/_doc/1# 全量修改
PUT /heima/_doc/2
{"info": "黑马程序员Python讲师","email": "zs@itcast.cn","name": {"firstName": "四","lastName": "赵"}
}# 增量修改
POST /heima/_update/1
{"doc": {"email": "ZhaoYun@itcast.cn"}
}

批量处理

image-20250421182002257

# 批量新增
POST /_bulk
{"index": {"_index":"heima", "_id": "3"}}
{"info": "黑马程序员C++讲师", "email": "ww@itcast.cn", "name":{"firstName": "五", "lastName":"王"}}
{"index": {"_index":"heima", "_id": "4"}}
{"info": "黑马程序员前端讲师", "email": "zhangsan@itcast.cn", "name":{"firstName": "三", "lastName":"张"}}# 批量删除
POST /_bulk
{"delete":{"_index":"heima", "_id": "3"}}
{"delete":{"_index":"heima", "_id": "4"}}

JavaRestClient

image-20250421193216409

客户端初始化

image-20250421193435528

<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

商品Mapping映射

image-20250421202440795

索引库操作

PUT /items
{"mappings": {"properties": {"id": {"type": "keyword"},"name":{"type": "text","analyzer": "ik_max_word"},"price":{"type": "integer"},"stock":{"type": "integer"},"image":{"type": "keyword","index": false},"category":{"type": "keyword"},"brand":{"type": "keyword"},"sold":{"type": "integer"},"commentCount":{"type": "integer","index": false},"isAD":{"type": "boolean"},"updateTime":{"type": "date"}}}
}

image-20250421202846414

image-20250421203159933

文档的crud

image-20250421214542515

修改文档数据有两种方式:
·方式一:全量更新。再次写入id一样的文档,就会删除旧文档,添加新文档。与新增的javaAPI一致。
·方式二:局部更新。只更新指定部分字段。

image-20250422124449893

文档操作的基本步骤:
,初始化RestHighLevelClient
创s建XxxRequest。XXX是Index、Get、Update、Delete
·准备参数(Index和Update时需要)
发送请求。调用RestHighLevelClient#.XXx()方法,XXx是
index、get、update、delete
·解析结果(Get时需要)

批处理

image-20250422125133769

DSL查询

image-20250422182620904

image-20250422185324928

全文检索

image-20250422190301517

精确查询

image-20250422191011111

复合查询
image-20250422191938952

image-20250422192617395

GET /items/_search
{"query": {"match": {"name": "华为"}}
}# 要搜索手机,但品牌必须是华为,价格必须是900~1599,那么可以这样写
GET /items/_search
{"query": {"bool": {"must": [{"match": {"name": "手机"}}],"filter": [{"term": {"brand.keyword": { "value": "华为" }}},{"range": {"price": {"gte": 90000, "lt": 159900}}}]}}
}
排序和分页

image-20250422194521905

image-20250422202253436

深度分页问题

image-20250422203626471

image-20250422204448415

高亮显示

image-20250422205247510

image-20250422210035988

image-20250422210321596

JavaRestClient查询

image-20250422210956999

image-20250422211854464

构建查询条件

image-20250422213426309

image-20250422214108105

需求:利用javaRestClient:实现搜索功能,条件如下:
搜索关键字为脱脂牛奶
品牌必须为德亚
价格必须低于300

@Test
void testSearch() throws IOException {// 创建request对象SearchRequest request = new SearchRequest("items");// 配置参数request.source().query(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "脱脂牛奶")).filter(QueryBuilders.termQuery("brand.keyword", "德亚")).filter(QueryBuilders.rangeQuery("price").lt(30000)));// 发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);//System.out.println("response = " + response);// 解析结果parseResponseResult(response);
}

排序和分页

@Test
void testSortAndPage() throws IOException {// 模拟前端传递的分页参数int pageNo = 1, pageSize = 5;// 创建request对象SearchRequest request = new SearchRequest("items");// 配置参数request.source().query(QueryBuilders.matchAllQuery());request.source().from((pageNo - 1) * pageSize).size(pageSize);request.source().sort("sold", SortOrder.DESC).sort("price", SortOrder.ASC);// 发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);//System.out.println("response = " + response);// 解析结果parseResponseResult(response);
}

高亮显示

image-20250422224220403

数据聚合

image-20250423125844553

DSL聚合

image-20250423130423058

image-20250423132249596

image-20250423133152292

RestClient聚合

image-20250423133634055

@Test
void testAgg() throws IOException {SearchRequest request = new SearchRequest("items");// 配置参数request.source().size(0);String brandAggName = "brandAgg";request.source().aggregation(AggregationBuilders.terms(brandAggName).field("brand.keyword").size(10));// 发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 解析结果Aggregations aggregations = response.getAggregations();Terms brandTerms = aggregations.get(brandAggName);List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();for (Terms.Bucket bucket : buckets) {System.out.println("brand: " + bucket.getKeyAsString());System.out.println("count: " + bucket.getDocCount());}
}

相关文章:

  • 云原生时代的双轮驱动
  • Unity 将Excel表格中的数据导入到Mysql数据表中
  • 4.23刷题记录(栈与队列专题)
  • 将输入帧上下文打包到下一个帧的预测模型中用于视频生成
  • RocketMQ 核心架构速览
  • Elasticsearch复习笔记
  • 数据集 | 苹果目标检测数据集
  • 车载功能测试-车载域控/BCM控制器测试用例开发流程【用例导出方法+优先级划分原则】
  • 如何避免AI编造虚假文献
  • CSS-跟随图片变化的背景色
  • 从GPT-5到Claude 3:大模型竞赛的下一站是什么?
  • 【ROS2】机器人操作系统安装到Ubuntu简介
  • 基于STM32、HAL库的MCP42010T数字电位器驱动程序设计
  • WebGL简介
  • rust编程学习(三):8大容器类型
  • Transformer 零基础实践教程 - 0 - 前言与环境配置
  • Rust 学习笔记:Rust 简介
  • element-ui transfer 组件源码分享
  • 远程控制Firefox浏览器实例的挑战与Playwright的CDP和Selenium Marionette解决方案
  • STM32 SysTick定时器
  • 对话地铁读书人|超市营业员朱先生:通勤时间自学心理学
  • 王珊珊读《吾自绝伦》|摘掉皮普斯的“假发”
  • 港澳航天员最早2026年飞天
  • 拍北京地铁上的读书人第七年:数字风吹散读书人了吗?
  • 王毅同英国外交大臣拉米通电话
  • 图忆|温州旅沪先贤的家国情怀