学习记录:DAY22

假日尾声:技术进阶与自我反思

前言


于是,假日迎来了它的尾声,把快乐和焦躁都留存在昨天。
我只觉情感的自相矛盾在加重,学习让我焦躁,纵欲无法填补空虚,于是我的心被拖入了无止尽的拉扯中。
我还没有找到必须留存的理由,但反之而言,我也远没有到可以决定自己生命自由的时刻。
还是闭上双眼,向前走吧,明日难测,却因昨日懊悔。
今天来统计一下剩余的进阶要求和已经完成的进阶要求,再针对性地完成。


日程


9点半,开始学习吧。

  • 上午:连接池动态扩缩容问题弄了一早上
  • 下午:完成了简单结果集映射
  • 晚上8点:做完结果集相关的blog
  • 嘻嘻,五一的学科作业还没写,我完蛋了。

学习内容


省流

  1. 连接池动态扩缩容
  2. 项目阶段性进度检查
  3. 简单结果集映射

1. 连接池动态扩缩容

1)动态扩容

在等待线程大于最大等待线程值时,临时将线程池的大小扩大1.5倍。

if (rawConn == null && waitCount.get() > hakimiConfig.getMaxWaitThreads()) {
    int temporaryMax = (int) (hakimiConfig.getMaxSize() * 1.5); // 扩容上限:maxSize 的 1.5 倍
    int current;
    do {
        current = createdCount.get();
        if (current >= temporaryMax) {
            break; // 已达到扩容上限
        }
    } while (!createdCount.compareAndSet(current, current + 1));
    if (current < temporaryMax) {
        log.warn("已启用动态扩容");
        try {
            rawConn = createPhysicalConnection();
        } catch (SQLException e) {
            createdCount.decrementAndGet(); // 创建失败时回滚计数器
            throw e;
        }
    }
}
2)动态缩容

这里使用了 ScheduledExecutorService 定时任务调度器,scheduleWithFixedDelay 会周期性地触发里面的回调函数。

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
// 启动缩容任务(每5分钟检查一次)
public void startShrinkTask() {
    scheduler.scheduleWithFixedDelay(() -> {
        try {
            int currentIdle = idleConnections.size();
            int minIdle = hakimiConfig.getMinIdle();

            // 仅当空闲连接 > minIdle 时才尝试缩容
            if (currentIdle <= minIdle) {
                return;
            }

            // 计算最多可回收的连接数(避免过度缩容)
            int maxShrink = currentIdle - minIdle;
            int shrunk = 0;

            while (shrunk < maxShrink) {
                // 非阻塞取出连接(避免长时间锁住队列)
                Connection conn = idleConnections.poll(10, TimeUnit.MILLISECONDS);
                if (conn == null) {
                    break; // 队列已空
                }

                // 检查是否超时
                if (isIdleTimeout(conn, hakimiConfig.getIdleTimeoutMillis())) {
                    closeConnection(conn);
                    createdCount.decrementAndGet();
                    log.warn("关闭空闲连接");
                    shrunk++;
                } else {
                    // 未超时,放回队列(避免误杀)
                    idleConnections.offer(conn);
                }
            }
        } catch (Exception e) {
            log.error("Shrink task error", e);
        }
    }, 300, 300, TimeUnit.SECONDS);
}

// 检查连接是否空闲超时
private boolean isIdleTimeout(Connection conn, long idleTimeoutMillis) {
    if (conn instanceof Proxy) {
        InvocationHandler handler = Proxy.getInvocationHandler(conn);
        if (handler instanceof HakimiConnectionPool.ConnectionInvocationHandler) {
            long idleTime = System.currentTimeMillis() -
                    ((HakimiConnectionPool.ConnectionInvocationHandler) handler).lastUsedTime;
            return idleTime > idleTimeoutMillis;
        }
    }
    return false;
}

2. 项目阶段性进度检查

已完成进阶需求
  • Maven 分模块化构建项目。
  • 完成数据库连接池,支持动态扩缩容(c3p0、druid)(并发安全(hard))。
  • 将常量配置转移到 yml 配置文件。
  • 输出日志文件。
  • 通过依赖注入(DI)进行控制反转(IOC),使用全局上下文进行全局对象的对象间依赖注入(使用注解)。
  • 实现 AOP 动态代理。
目前需要完成的进阶需求
  • SQL 构建器 + 自定义 SQL,高兼容性的结果映射。
  • DispatcherController 来统一接收数据,并转发给路径给对应 Controller 处理。
  • 自定义异常抛出,全局异常处理器,过滤器。
  • 后端以多线程模式运行,保证并发安全。(考虑方法级锁)
往后的进阶需求
  • 统一接收 JSON 格式数据,对数据进行混合加密(RSA + AES)。
  • 判题机制,可以运行 C++ 代码。
  • Nginx 部署前端 + Docker 容器技术。
  • 将项目部署到公网。

3. 简单结果集映射

1)关键方法:预包装代理方法
private static <T> BiFunction<ResultSet, Integer, T> createMapper(Class<T> targetClass) {
    try {
        // 获取或创建构造函数句柄
        Constructor<T> constructor = targetClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        MethodHandle constructorHandle = MethodHandles.lookup().unreflectConstructor(constructor); // 使用MethodHandle包装构造器,比传统反射调用性能更高

        // 从缓存获取或创建字段信息
        Map<String, MethodHandle> setters = Cache.SETTER_CACHE
                .computeIfAbsent(targetClass, KatSimpleMapper::createSetters);

        Map<String, Class<?>> fieldTypes = Cache.FIELD_TYPE_CACHE
                .computeIfAbsent(targetClass, KatSimpleMapper::getFieldTypes);

        return (rs, index) -> {
            try {
                @SuppressWarnings("unchecked")
                T instance = (T) constructorHandle.invoke(); // 创建目标对象实例

                for (Map.Entry<String, MethodHandle> entry : setters.entrySet()) {
                    String fieldName = entry.getKey();
                    String columnName = namingStrategy.convert(fieldName);

                    try {
                        Object value = rs.getObject(columnName);
                        if (value != null) {
                            Class<?> fieldType = fieldTypes.get(fieldName);
                            Object convertedValue = convertType(value, fieldType); // 类型转换
                            entry.getValue().invoke(instance, convertedValue); // 设置属性值
                        }
                    } catch (SQLException e) {
                        // 列不存在时跳过
                    }
                }
                return instance;
            } catch (Throwable e) {
                throw new RuntimeException("Mapping failed for " + targetClass.getName(), e);
            }
        };
    } catch (Exception e) {
        throw new RuntimeException("Mapper creation failed for " + targetClass.getName(), e);
    }
}
2)包装方法:负责将触发代理方法,将mapper结果映射到结果集中
public static <T> List<T> map(ResultSet rs, Class<T> targetClass) throws SQLException {
    @SuppressWarnings("unchecked")
    BiFunction<ResultSet, Integer, T> mapper = (BiFunction<ResultSet, Integer, T>)
            Cache.MAPPER_CACHE.computeIfAbsent(targetClass, KatSimpleMapper::createMapper);

    List<T> results = new ArrayList<>();
    while (rs.next()) {
        results.add(mapper.apply(rs, 1));
    }
    return results;
}
3)辅助方法
  • 数据库字段类型到java字段类型的转换
private static Object convertType(Object value, Class<?> targetType) {
    if (value == null) return null;
    if (targetType.isInstance(value)) return value;

    // 数值类型转换
    if (value instanceof Number number) {
        if (targetType == Double.class || targetType == double.class) {
            return number.doubleValue();
        }
        if (targetType == Float.class || targetType == float.class) {
            return number.floatValue();
        }
        if (targetType == Integer.class || targetType == int.class) {
            return number.intValue();
        }
        if (targetType == Long.class || targetType == long.class) {
            return number.longValue();
        }
        if (targetType == Short.class || targetType == short.class) {
            return number.shortValue();
        }
    }

    // 日期类型转换
    if (value instanceof java.sql.Date sqlDate) {
        if (targetType == LocalDate.class) {
            return sqlDate.toLocalDate();
        }
        if (targetType == LocalDateTime.class) {
            return sqlDate.toLocalDate().atStartOfDay();
        }
    }

    // 布尔类型转换
    if (value instanceof Boolean bool) {
        if (targetType == Integer.class || targetType == int.class) {
            return bool ? 1 : 0;
        }
        if (targetType == Double.class || targetType == double.class) {
            return bool ? 1.0 : 0.0;
        }
    }

    // 时间戳转换
    if (value instanceof Timestamp timestamp) {
        if (targetType == LocalDateTime.class) {
            return timestamp.toLocalDateTime();
        }
        if (targetType == LocalDate.class) {
            return timestamp.toLocalDateTime().toLocalDate();
        }
    }

    return value;
}
  • 创建setter方法并缓存
private static <T> Map<String, MethodHandle> createSetters(Class<T> targetClass) {
    try {
        Map<String, MethodHandle> setters = new HashMap<>();
        MethodHandles.Lookup lookup = MethodHandles.lookup(); // 获取MethodHandles.Lookup实例,用于方法/字段查找

        for (Field field : targetClass.getDeclaredFields()) {
            try {
                MethodHandle setter = lookup.unreflectSetter(field); // 尝试通过标准setter方法获取MethodHandle
                setters.put(field.getName(), setter);
            } catch (IllegalAccessException e) {
                // 如果字段没有setter,尝试直接设置字段值
                field.setAccessible(true);
                MethodHandle setter = lookup.unreflectSetter(field);
                setters.put(field.getName(), setter);
            }
        }
        return Collections.unmodifiableMap(setters); // 返回不可修改的Map
    } catch (Exception e) {
        throw new RuntimeException("Failed to create setters for " + targetClass.getName(), e);
    }
}

结语



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值