微服务开发与实战Day05 - 服务保护和分布式事务

news2024/11/15 9:00:28

一、服务保护和分布式事务

二、雪崩问题

微服务调用链路中某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。

1. 雪崩问题产生的原因是什么?

  • 微服务相互调用,服务提供者出现故障或阻塞;
  • 服务调用者没有做好异常处理,导致自身故障;
  • 调用链中的所有服务级联失败,导致整个集群故障

2. 解决问题的思路有哪些?

尽量避免服务出现故障或阻塞

  • 保证代码的健壮性
  • 保证网络畅通;
  • 能应对较高的并发请求

服务调用者做好远程调用异常的后备方案,避免故障扩散。

服务保护技术
SentinelHystrix
线程隔离信号量隔离线程池隔离/信号量隔离
熔断策略基于慢调用比例或异常比例基于异常比率
限流基于QPS,支持流量整形有限的支持
Fallback支持支持
控制台开箱即用,可配置规则、查看秒级监控、机器发现等不完善
配置方式基于控制台,重启后失效基于注解或配置文件,永远生效

3. 服务保护方案 - 请求限流

请求限流:限制访问微服务的请求并发量,避免服务因流量激增出现故障。

4. 服务保护方案 - 线程隔离

线程隔离:也叫做舱壁模式,模拟船舱隔板的防水原理。通过限定每个业务能使用的线程数量二将故障业务隔离,避免故障扩散。

5. 服务保护方案 - 服务熔断

服务熔断:由断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断该业务,拦截该接口的请求。熔断期间,所有请求快速失败,全都走fallback逻辑。

失败处理:定义fallback逻辑,让业务失败时不再抛出异常,而是返回默认数据或友好提示。

三、Sentinel

1. 初识Sentinel

Sentinel是阿里巴巴开源的一款微服务流量控制组件。官网地址:home | Sentinel

使用步骤:

(1)sentinel的安装

①下载jar包:Releases · alibaba/Sentinel · GitHub

②运行

将jar包放在任意非中文,不包含任何特殊字符的目录下,重命名为sentinel-dashboard.jar

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

③访问 http://localhost:8090

用户名和密码都是sentinel

(2)微服务整合

我们在cart-servicr模块中整合sentinel,连接sentinel-dashboard控制台,步骤如下:

①引入sentinel依赖 pom.xml(cart-service)

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

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

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8090 # sentinel的控制台地址

③访问cart-service的任意端点

重启cart-service,然后访问查询购物车接口,sentinel的客户端就会将服务访问的信息提交到sentinel-dashboard控制台。并展示出统计信息:

④点击簇点链路菜单,会看到下面的页面:

⑤簇点链路

簇点链路

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

Restful风格的API请求路径一般都相同,这会导致簇点资源名称重复。因此我们需要修改配置,把请求方式+请求路径作为簇点资源名称。

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8090 # sentinel的控制台地址
      http-method-specify: true # 开启请求方式前缀

重启CartApplication,访问购物车相关接口

2. 请求限流

使用步骤:

①在簇点链路后面点击流控按钮,即可对其做限流设置

②双击运行jemter

③把day05资料中的雪崩测试.jmx拖入jmeter

④启动测试

⑤察看结果树和汇总报告

3. 线程隔离

当商品服务出现阻塞或故障时,调用商品服务的购物车服务可能因此而被拖慢,甚至资源耗尽。所以必须限制购物车服务中查询商品这个业务的可用线程数,实现线程隔离。

使用步骤:

①在sentinel控制台中,会出现Feign接口的簇点资源,点击后面的流控按钮,即可配置线程隔离

(5个并发线程,如果单线程QPS为2,则5线程QPS为10)

②修改item-service的ItemController里的queryItemByIds进行测试

@ApiOperation("根据id批量查询商品")
@GetMapping
public List<ItemDTO> queryItemByIds(@RequestParam("ids") List<Long> ids){
  // 模拟业务延迟
  ThreadUtil.sleep(500);
  return itemService.queryItemByIds(ids);
}

③在cart-service里的application.yaml新增tomcat限制

server:
  port: 8082
  tomcat:
    threads:
      max: 50
    accept-count: 25
    max-connections: 100

④重启ItemApplication、ItemApplication2、CartApplication,进行线程隔离测试

⑤查询购物车异常,但其他服务正常

4. Fallback

使用步骤:

①将FeignClient作为Sentinel的簇点资源:application.yaml(cart-service)

feign:
  sentinel:
    enabled: true

②FeignClient的Fallback有两种配置方式:

  • 方式一:FallbackClass,无法对远程调用的异常做处理
  • 方式二:FallbackFactory,可以对远程调用的异常做处理,通常都会选择这种。

案例:给FeignClient编写Fallback逻辑

假如我们有一个FiegnClient如下:

@FeignClient(value = "userservice")
public interface UserClient {
    @GetMapping("/user/{id})
    User findById(@PathVariable("id") Long id);
}

为其编写Fallback逻辑

使用步骤:

①自定义类,实现FallbackFactory,编写对某个FeignClient的fallback逻辑

@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        // 创建UserClient接口实现类,实现其中的方法,编写失败降级的处理逻辑
        return new UserClient() {
            @Override
            public User findById(Long id) {
                // 记录异常信息,可以返回空或抛出异常
                log.error("查询用户失败", throwable);
                return null;
            }
        };
    }
}

②将刚刚定义的UserClientFallbackFactory注册为一个Bean:

@Bean
public UserClientFallbackFactory userClientFallback() {
    return new UserClientFallbackFactory();
}

③在UserClient接口中使用UserClientFallbackFactory

@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

步骤:hm-api模块

①在hm-api模块下的client新建fallback.ItemClientFallbackFactory

package com.hmall.api.client.fallback;

import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.utils.CollUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;

import java.util.Collection;
import java.util.List;

@Slf4j
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
    @Override
    public ItemClient create(Throwable cause) {
        return new ItemClient() {
            @Override
            public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
                log.error("查询商品失败!", cause);
                return CollUtils.emptyList();
            }

            @Override
            public void deductStock(List<OrderDetailDTO> items) {
                log.error("扣减商品库存失败!", cause);
                throw new RuntimeException(cause);
            }
        };
    }
}

②在DefaultFeignConfig中新增

    @Bean
    public ItemClientFallbackFactory itemClientFallbackFactory() {
        return new ItemClientFallbackFactory();
    }

③ItemClient

@FeignClient(value = "item-service", fallbackFactory = ItemClientFallbackFactory.class)
public interface ItemClient {
// ... ...
}

④开启sentinel监控 -> cart-service模块的application.yaml

feign:
  sentinel:
    enabled: true

⑤重启CartApplication进行测试

⑥新增流控(注意要删除之前配置的流控规则)

5. 服务熔断

熔断是解决雪崩问题的重要手段。思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务,即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

点击控制台中簇点资源后的熔断按钮,即可配置熔断策略:

步骤:

①新增熔断规则

②线程隔离测试

四、分布式事务

1. 分布式事务

在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事务必须同时成功或失败,这样的事务就是分布式事务。其中的每个服务的事务就是一个分支事务。整个业务称为全局事务

下单业务,前端请求首先进入订单服务,创建订单并写入数据库。然后订单服务调用购物车服务和库存服务:

  • 购物车服务负责清理购物车信息
  • 库存服务负责扣减商品库存

2. 初始Seata

Seata是2019年1月蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。

官网地址:Apache Seata,其中的文档、博客中提供了大量的使用说明、源码分析。

分布式事务解决思路

解决分布式事务,各个子事务之间必须能感知到彼此的事务状态,才能保证状态一致。

2.1 Seata架构

Seata事务管理中有三个重要角色:

  • TC(Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
  • TM(Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
  • RM(Resource Manager) - 资源管理器:管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态。

3. 部署TC服务

参考文档进行部署:Docs

使用步骤:

①准备数据库表:执行课前资料里提供的seata-tc.sql

②准备配置文件

把课前资料里的下图两个文件上传到虚拟机root根目录下

(注意要根据自己的实际情况更改application.yml里数据库的连接密码)

③加载seata镜像

④检测mysql和nacos是否在hm-net网络里,如果不在则添加进入

查看:

docker inspect nacos
docker inspect mysql

添加:

docker network connect hm-net nacos
docker network connect hm-net mysql

⑤在虚拟机/root下执行以下命令(根据自己的情况改IP地址)

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

⑥查看seata运行日志

 docker logs -f seata

⑦在nacos查看服务列表 http://192.168.126.151:8848/

⑧访问 http://192.168.126.151:7099/

用户名和密码都是admin

4. 微服务集成Seata

步骤:以cart-service模块为例

①在项目中引入Seata依赖 pom.xml(cart-service、item-service、trade-service)

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

②在application.yaml里添加配置,让微服务找到TC服务地址

注意,需要改成自己的虚拟机地址

同时,在cart-service、item-service、trade-service这三个模块的bootstrap,yaml里配置如下

spring:
  application:
    name: cart-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.126.151 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - data-id: shared-jdbc.yaml # 共享mybatis配置
          - data-id: shared-log.yaml # 共享日志配置
          - data-id: shared-swagger.yaml # 共享日志配置
          - data-id: shared-seata.yaml # 共享seata配置

application.yaml实例如下

server:
  port: 8081
hm:
  db:
    database: hm-item
  swagger:
    title: "黑马商城商品管理接口文档"
    desc: "黑马商城商品管理接口文档"
    package: com.hmall.item.controller

③重新启动CartApplication、TradeApplication、ItemApplication、ItemApplication2

5. XA模式

XA规范是X/Open组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的关系型数据库都对XA规范提供了支持。Seata的XA模式如下:

一阶段的工作:

①RM注册分支事务到TC

②RM执行分支业务sql但不提交

③RM报告执行状态到TC

二阶段的工作:

①TC检测各分支事务执行状态

  • 如果都成功,通知所有RM提交事务
  • 如果有失败,通知所有RM回滚事务

②RM接收TC指令,提交或回滚事务

XA模式的优点是什么?

  • 事务的强一致性,满足ACID原则
  • 常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
  • 依赖关系型数据库实现事务

实现XA模式

Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:

1. 修改application.yaml文件(每个参与事务的微服务),开启XA模式:

seata:
  data-source-proxy-mode: XA # 开启数据源代理的XA模式

在shared-seata.yaml里编辑

2. 给发起全局事务的入口方法添加@GlobalTransactional注解,本例中是OrderServiceImpl中的createOrder方法:

@Override
@GlobalTransactional
public Long createOrder(OrderFormDTO orderFormDTO) {
    // ... ...
}

CartServiceImpl的removeByItemIds()

@Override
@Transactional
public void removeByItemIds(Collection<Long> itemIds) {
    // ... ...
}

ItemServiceImpl里的deductStock()

@Override
@Transactional
public void deductStock(List<OrderDetailDTO> items) {
    // ... ...
}

3. 重启服务并测试

6. AT模式

Seata主推的是AT模式,AT模式同样是分阶段提交的事务类型,不过弥补了XA模型中资源锁定周期过长的缺陷。

阶段一RM的工作:

  • 注册分支事务
  • 记录undo-log(数据快照)
  • 执行业务sql并提交
  • 报告事务状态

阶段二提交时RM的工作:

  • 删除undo-log即可

阶段二回滚时RM的工作:

  • 根据undo-log恢复数据到更新前

简述AT模式与XA模式最大的区别是什么?

  • XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源;
  • XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚;
  • XA模式强一致;AT模式最终一致

实现AT模式

步骤:

①添加资料中的seata-at.sql到微服务对应的数据库中:hm-cart、hm-item、hm-trade三个数据库

②修改application.yaml文件,将事务模式修改为AT模式:(不填就默认为AT模式)

③其次,我们要利用@GlobalTransactional标记分布式事务的入口方法:

④重启服务并测试

7. 练习

除了下单业务以外,用户如果选择余额支付,前端会将请求发送到pay-service模块。而这个模块要做三件事情:

  • 直接从user-service模块调用接口,扣除余额付款
  • 更新本地(pay-service)交易流水表状态
  • 通知交易服务(trade-service)更新其中的业务订单状态

显然,这里也存在分布式事务问题。

步骤:

①添加资料中的seata-at.sql到微服务对应的数据库中:hm-pay、hm-trade、hm-user三个数据库

②修改application.yaml文件,将事务模式修改为AT模式(上面已经改过,这里就不演示了)

③在pay-service、trade-service、user-service这三个模块中引入seata的依赖

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

④同时,在pay-service、trade-service、user-service这三个模块的bootstrap,yaml里配置如下

spring:
  application:
    name: pay-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.126.151 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - data-id: shared-jdbc.yaml # 共享mybatis配置
          - data-id: shared-log.yaml # 共享日志配置
          - data-id: shared-swagger.yaml # 共享日志配置
          - data-id: shared-seata.yaml # 共享seata配置

④利用@GlobalTransactional标记分布式事务的入口方法:

com.hmall.pay.service.impl.PayOrderServiceImpl类的tryPayOrderByBalance

    @Override
    @GlobalTransactional
    public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
        // 1.查询支付单
        PayOrder po = getById(payOrderFormDTO.getId());
        // 2.判断状态
        if(!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())){
            // 订单不是未支付,状态异常
            throw new BizIllegalException("交易已支付或关闭!");
        }
        // 3.尝试扣减余额
        userClient.deductMoney(payOrderFormDTO.getPw(), po.getAmount());
        // 4.修改支付单状态
        boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
        if (!success) {
            throw new BizIllegalException("交易已支付或关闭!");
        }
        // 5.修改订单状态
       tradeClient.markOrderPaySuccess(po.getBizOrderNo());
    }

trade-service模块下的OrderServiceImpl

user-service模块下的UserServiceImpl

⑤把user表的balance字段的类型改为无符号整型

ALTER TABLE `user` MODIFY `balance` INT UNSIGNED DEFAULT NULL COMMENT '账户余额';

⑥问题:在PayOrder里amount的单位为,而扣减余额的时候单位按来算了,或者balance字段的单位也是分???不太清楚

在UserMapper里修改如下进行模拟测试

⑦重启服务进行测试

把Jack用户的余额改为2000,进行测试

ALTER TABLE `user` MODIFY `balance` FLOAT UNSIGNED DEFAULT NULL COMMENT '账户余额';

支付密码:123

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

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

相关文章

OpenGauss数据库-9.模式管理

第1关&#xff1a;创建模式 gsql -d postgres -U gaussdb -W passwd123123; CREATE DATABASE test_db; \c test_db passwd123123 CREATE SCHEMA new_schema AUTHORIZATION gaussdb; CREATE TABLE new_schema.mytable (my_id int, my_info varchar(36)); 第2关&#xff1a;管理…

asyncio协程提高执行效率

from fastapi import FastAPI import asyncioapp FastAPI()async def task1():# 模拟执行任务1print("开始执行任务1")await asyncio.sleep(1)print("结束执行任务1")return "Result from Task 1"async def task2():# 模拟执行任务2print("…

Linux:进程优先级

文章目录 基本概念查看进程优先级PRI & NI 更改优先级Linux中进程优先级的实现原理上下文 基本概念 CPU资源分配的先后顺序&#xff0c;就是指进程的优先权&#xff08;priority&#xff09;。 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的Linux很有用&a…

【ARM Cache 与 MMU 系列文章 7.4 -- ARMv8 MMU 配置 寄存器使用介绍】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 MMU 转换控制寄存器 TCR_ELxTCR_ELx 概览TCR_ELx 寄存器字段详解TCR 使用示例Normal MemoryCacheableShareability MMU 内存属性寄存器 MAIR_ELxMAIR_ELx 寄存器结构内存属性字段Devic…

AIGC绘画设计提示词篇:MJ绘画提示词midjourney

开通账号半个多月了&#xff0c;把这小半个月用到的比较好的提示词分享给大家。先说两个功能点 一、Vary(Subtle) & Vary(Strong)功能: Vary(Subtle)是对图片进行微小的调整,点击“Vary(Subtle)”后新生成的图片与原图的变化差异极小,仅在细节处做了细微调整。 点击“Var…

佐西卡在美国InfoComm 2024展会上亮相投影镜头系列

6月12日至14日&#xff0c;2024美国视听显示与系统集成展览会将在拉斯维加斯会议中心盛大开幕。这场北美最具影响力的视听技术盛会&#xff0c;将汇集全球顶尖的视听解决方案&#xff0c;展现专业视听电子系统集成、灯光音响等领域的最新技术动态。 在这场科技盛宴中&#xff0…

UE5 Sequencer 使用指导 - 学习笔记

https://www.bilibili.com/video/BV1jG411L7r7/?spm_id_from333.337.search-card.all.click&vd_source707ec8983cc32e6e065d5496a7f79ee6 Sequencer 01 1.1 调整视口 调整窗口数量 调整视口类型为Cinematic视口 视口显示网格&#xff0c;或者条件参考线 1.2 关卡动画与…

Ubuntu22.04之解决:emacs无法输入中文问题(二百四十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

全域推广是什么意思?业务范围有哪些?

随着全域时代的到来&#xff0c;与全域相关的各种概念不断涌现&#xff0c;引发了一轮又一轮的热议。在此背景下&#xff0c;全域推广一经出现便一跃成为了互联网的有一大热词&#xff0c;以全域推广是什么意思为代表的相关问题也成为了多个创业者社群中的热点话题。 相关资料显…

Windows10上安装LabVIEW社区版

LabVIEW全称Laboratory Virtual Instrument Engineering Workbench(实验室虚拟仪器工作台)。LabVIEW是美国国家仪器有限公司(National Instruments, NI)最核心的软件产品。LabVIEW是图形化编程语言&#xff0c;与C等文本编程语言相比&#xff0c;它在编程过程中有更详细的提示信…

电影票小程序API接口的实现方法

电影票小程序API接口的实现方法涉及后端服务的开发和设计。以下是一些关于如何实现电影票小程序API接口的基本步骤和考虑因素&#xff1a; 1. 需求分析 确定需要哪些API接口来支持小程序的功能&#xff0c;如查询电影、影院、座位信息&#xff0c;购票&#xff0c;查看订单等…

彩虹图纸管理软件的图纸电子化管理解决方案?

彩虹图纸管理软件的图纸电子化管理解决方案为企业提供了一套全面、高效的图纸管理方案。以下是该解决方案的详细概述&#xff1a; 1、图纸扫描与数字化&#xff1a; 彩虹图纸管理软件支持将纸质图纸进行扫描&#xff0c;生成高质量的电子文件&#xff0c;如DWG、PDF等格式。 …

逻辑题 :谁是凶手?

设 &#xff1a; A 甲是凶手 这个是题中1的 如果甲不是凶手 我们假设A条件是甲是凶手&#xff0c;取反就可是甲不是凶手&#xff0c;B 乙是凶手 这个是题中1的 如果乙或者是凶手 我们假设B条件乙是凶手C 乙是知情人 这个是题中1的 或者是知情人 我们假设C条件乙是知情人D …

【运维自动化-配置平台】如何使用云资源同步功能(腾讯云为例)

云资源同步是通过apikey去单向同步云上的主机资源和云区域信息&#xff0c;目前支持腾讯云和亚马逊云。主要特性 1、蓝鲸配置平台周期性的单向只读同步云主机和vpc&#xff08;对应蓝鲸云区域&#xff09;信息&#xff0c;第一次全量&#xff0c;后面增量 2、默认同步到主机池…

Vue3相关语法内容,组件传值,事件监听,具名插槽。

1、Vue3相关语法内容 赋值语句(ref、reactive系列)组件传值(父子&#xff0c;子父)watch&#xff0c;watchEffect监听slot具名插槽 1、赋值语法&#xff08;ref&#xff0c;reactive&#xff09; 1.1、ref 、isRef、 shallowRef、triggerRef、customRef 支持所有的类型&…

LLVM Cpu0 新后端8 尾调用优化 Stack Overflow Exception异常

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…

Matlab进阶绘图第59期—棒棒糖图

​棒棒糖图本质上是柱状图的一种变体。 棒棒糖图通过在每根柱子顶端添加圆点&#xff0c;以表示数据之间的相对位置。 此外&#xff0c;一般还会对每根棒棒糖按数值大小进行排序&#xff0c;从而更加方便阅读。 本文利用自制的Lollipop工具进行棒棒糖图的绘制&#xff0c;先…

计算机网络(2) 网络层:IP服务模型

一.Internet Protocol在TCP/IP四层模型中的作用 第三层网络层负责数据包从哪里来到哪里去的问题。传输层的数据段提交给网络层后&#xff0c;网络层负责添加IP段&#xff0c;包含数据包源地址与目的地址。将添加IP段的数据包交由数据链路层添加链路头形成最终在各节点传输中所需…

双tab 栏 实现表格高度自适应 遇到的一些问题

默认的显式的tab高度自适应无问题&#xff0c;但是另外的显式有问题 原因 非active的tab默认是不加载的&#xff0c;所以读取到的table是0&#xff0c;基于此计算出来的高度自适应必然会出问题 第一步解决&#xff1a;避免懒加载 使用el-tab中自带的lazy属性&#xff0c;避免懒…

【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版9(附带项目源码)

最终效果 系列导航 文章目录 最终效果系列导航前言选择植物简单绘制选择植物面板渲染卡牌数据 点击选中和移除卡牌修改UI代码控制 开始战斗源码结束语 前言 本节主要实现添加选择植物功能 选择植物 简单绘制选择植物面板 每个卡牌插槽和前面植物卡牌类似&#xff0c;并配置…