Project

CompletableFuture异步编排

简介

  • CompletableFuture是java.utils.concurrent里的一个类

  • 作用:把串行执行的代码变为并行执行,提高代码执行速度

快速上手

创建异步编排对象

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);

线程串行方法

// 使线程串行执行,无入参,无返回值
public CompletableFuture<Void> thenRun(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor);

// 使线程串行执行,有入参,无返回值
public CompletableFuture<Void> thenAccept(Consumer<? super T> action);
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor);

// 使线程串行执行,有入参,有返回值
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor);

多任务组合

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

代码实例

public class CompletableFutureTest5 {

    @SneakyThrows
    public static void main(String[] args) {
        //动态获取服务器核数
        int processors = Runtime.getRuntime().availableProcessors();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                processors+1, // 核心线程个数 io:2n ,cpu: n+1  n:内核数据
                processors+1,
                0,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> "任务1", executor);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> "任务2", executor);
        CompletableFuture<String> future03 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务3";
        }, executor);

        // 串联起若干个线程任务, 没有返回值
        CompletableFuture<Void> all = CompletableFuture.allOf(future01, future02, future03);
        // 等待所有线程执行完成
        // .join()和.get()都会阻塞并获取线程的执行情况
        // .join()会抛出未经检查的异常,不会强制开发者处理异常 .get()会抛出检查异常,需要开发者处理
        all.join();
        all.get();
    }
}  

MongoDB

概述

  • MongoDB 与传统关系型数据库相比更加简单,架构为key-value结构
  • MySQL数据库:数据库-表-记录 MongoDB :数据库-集合-文档(记录)
  • 文档类似于JSON对象,结构成为BSON

安装与启动

1. 导入 MongoDB 官方 GPG 密钥

首先需要导入 MongoDB 官方的 GPG 密钥,以便系统能够验证下载包的完整性。

curl -fsSL https://pgp.mongodb.com/server-7.0.asc | \
   sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg \
   --dearmor

2. 添加 MongoDB 软件源

将 MongoDB 的软件源添加到系统的源列表中,这样就可以通过包管理器直接下载 MongoDB。

分布式事物锁

分布式锁解决司机抢单

因为上面写的司机抢单并没有考虑并发,类似于电商的超卖问题

**解决方案

  • 第一种 设置数据库事务的隔离级别,设置为Serializable,效率低下

  • 第二种 使用乐观锁解决,通过版本号进行控制

  • 第三种 加锁解决,学习过synchronized 及lock锁,本地锁,目前微服务架构,分布式部署方式。

本地锁的局限性

  • 我们使用锁一般都是,synchronized 及lock锁,这些都是本地锁,只在当前jvm生效,在微服务里面就是只有当个微服务生效
  • 举例演示 TestController
@Tag(name = "测试接口")  
@RestController  
@RequestMapping("/order/test")  
public class TestController {  
  
    @Autowired  
    private TestService testService;  
  
    @GetMapping("testLock")  
    public Result testLock() {  
        testService.testLock();  
        return Result.ok();  
    }  
}

TestServiceImpl

快速上手xxl-job

XXL-JOB入门案例

第一步

下载XXL-JOB示例代码,解压,使用idea打开

**项目组成:

  • admin:调度中心
  • core:公共依赖
  • sample
    • sample-frameless:不带框架的
    • sample-springboot:带springboot框架的

第二步

创建XXL-JOB使用数据库和相关表

Redis常见的几个问题

缓存雪崩

当大量缓存在同一时间失效或者过期,亦或者Redis故障宕机时,如果此时有大量用户数据访问,Redis 无法处理,于是全部请求都会直接访问数据库,导致数据库压力剧增,严重会导致数据库宕机,从而出现更严重的问题

常见解决方法:

  • 给各数据设置不相同的过期时间,尽量让他们不要在同一时间过期
  • 设置互斥锁,当发现需要查找的数据在Redis中找不到,就加上一个互斥锁,保证一个时间只有规定的次数查询数据库来构建缓存
  • 如果是宕机,那我们可以用熔断来解决,或者是拒绝服务

缓存击穿

缓存击穿跟缓存雪崩很相似,可以认为缓存击穿是缓存雪崩其中之一

如果缓存中某个热点数据过期了,此时大量的数据请求访问了该数据,就无法从缓存中读取,直接访问数据库,数据库很容易被高并发的请求冲垮,这就是缓存击穿问题。

常见解决方法:

  • 互斥锁方案,保证同一时间只有一个业务线程更新缓存
  • 不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间;

缓存穿透

当用户访问的数据不存在,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,那么此时大量的请求都落在数据库中,导致数据库压力骤增

常见解决方法

  • 限制非法请求,进行参数校验,对于不合法的参数请求直接抛出异常返回给客户端。
  • 缓存空值或者默认值,当发现缓存穿透的现象时,可以针对查询的数据,在缓存中设置一个空值或者默认值,这样后续请求就可以从缓存中读取到空值或者默认值,返回给应用,使其不会继续查询数据库
  • 使用布隆过滤器,过滤器说数据不存在,那么数据库中一定不会有这个数据。如果说数据存在,并不一定证明数据库中存在这个数据,有误判的几率,只不过几率非常小。(底层是hash,有极小概率误判)

莫名其妙的报错

太吓人了可恶,今天写完运行的时候报了两个错

D:\Desktop\hhsqdmz\Project\Substitute driver\代码\daijia-parent\service-client\service-map-client\src\main\java\com\atguigu\daijia\map\client\MapFeignClient.java:19:5
java: 找不到符号
  符号:   类 Result
  位置: 接口 com.atguigu.daijia.map.client.MapFeignClient

D:\Desktop\hhsqdmz\Project\Substitute driver\代码\daijia-parent\service-client\service-map-client\src\main\java\com\atguigu\daijia\map\client\MapFeignClient.java:3:40 java: 程序包com.atguigu.daijia.common.result不存在

然后我按照报错原因按个排插,看了半天没发现问题,最后我使用了最原始的方法:回溯

我按照步骤挨个回退,一路删删删,最后发现问题居然是ALT+Enter,快速的加入了一个依赖在最外层父项目,然后那个依赖本身就存在,两个相同的依赖导致出错,并且报错原因和真实原因毫不相关

气煞我也!

规则引擎

规则引擎概述

  • 规则引擎,全称为业务规则管理系统,英文名为BRMS

  • 就是把业务里经常变动的代码给抽离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策

  • 主流产品:drools、VisualRules、iLog

drools

概述

drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。

drools官网地址:https://drools.org/

drools源码下载地址:https://github.com/kiegroup/drools

基础使用

创建spring boot工程

引入drools依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>drools</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>17</java.version>
        <drools.version>8.41.0.Final</drools.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-core</artifactId>
            <version>${drools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>${drools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-decisiontables</artifactId>
            <version>${drools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-mvel</artifactId>
            <version>${drools.version}</version>
        </dependency>
    </dependencies>

</project>

创建drools配置类

@Configuration
public class DroolsConfig {

    private static final KieServices kieServices = KieServices.Factory.get();
    //制定规则文件的路径
    private static final String RULES_CUSTOMER_RULES_DRL = "rules/order.drl";

    @Bean
    public KieContainer kieContainer() {
        //获得Kie容器对象
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_CUSTOMER_RULES_DRL));

        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        kieBuilder.buildAll();

        KieModule kieModule = kieBuilder.getKieModule();
        KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());

        return kieContainer;
    }
}
  • 定义了一个 KieContainer的Bean ,KieContainer用于通过加载应用程序的/resources文件夹下的规则文件来构建规则引擎。

mybatis-plus 组件策略

默认的组件策略 ASSIGN_ID 雪花算法

  • mp有默认的组件策略 ASSIGN_ID,会生成一个唯一的值,包含数字

  • 雪花算法:使用一个64位的长型的数字作为全局唯一ID

  • 表对应类型 bigint 或者 varchar类型

ASSIGN_UUID

  • ASSIGN_ID 还有另一个相近的策略 ASSIGN_UUID,生成唯一的uuid值,包含数字和字母

  • 表对应字段的类型 varchar(32) 类型,实体类字段对应 String

自增策略 AUTO

  • 想要主键自增需要配置以下操作

校园闪送项目笔记

搭建前端环境

注册微信开发者账号

打开微信公总平台,按照流程一步步注册:https://mp.weixin.qq.com/

然后去申请开通三个我们项目会用到的接口 ![[Pasted image 20250417092751.png]]

以及我们需要用到的插件 ![[Pasted image 20250417093027.png]]

https://fuwu.weixin.qq.com/search?tab=3&type=&serviceType=3&page=1&kw=腾讯位置服务地图选点 fuwu.weixin.qq.com/search?tab=3&type=&serviceType=3&page=1&kw=微信同声传译

安装 node.js 和微信开发者工具

下载node.js:https://nodejs.org/en/download 版本选择16.20.0 ![[Pasted image 20250417093149.png]]

微信开发者工具也是点击下载一步步走就行