2024年8月

在上周五 12:24-14:05 遭遇
大规模 DDoS 攻击
后,今天11:40左右开始遭遇疑似大规模 CC 攻击,明显的特征是有很多来自海外各个国家IP的异常高频次访问请求。

虽然我们针对海外访问临时采取了躲避措施,但攻击请求不仅限于海外,我们躲不过去。

非常抱歉,这次攻击给大家带来了麻烦,请大家谅解。

这次遇到的攻击与去年遇到的攻击类似,攻击者用心良苦。

去年攻击时,手下留情,在十一假期加班一次采用 DDoS + CC 联合攻击,因为是节假日,影响很小,我们没有缴保护费。

今年攻击时,特地选择在工作日,周五 DDoS 攻击,周末双休,周一改用 CC 攻击,影响很大,逼着我们缴保护费。

但是,用心良苦的攻击者们,不是我们不想缴保护费,实在是园子太困难,困难到
向开发者求救
,缴不起保护费啊,你们再怎么用心良苦地攻击,我们还是缴不起保护费啊,请放手吧。

公众号同步发布
https://mp.weixin.qq.com/s/0HecUB8MM1l4ZyvLd1RAkg

注:如果发现发现不能访问园子,可以在上面这篇公众号文章的评论中查看当前攻击情况

关于攻击的一些情况:

1)在 CC 攻击期间,有一台负载均衡的 QPS 高达近2.4w

1)

在TS开发中,经常会遇到后台数据字段比较多的情况,这时候需要一个个复制字段然后给他手动配置数据类型来完成我们的TS类型定义,相当麻烦。有什么快速的方法呢,我就目前遇到的两种情况分别写了JS脚本来处理后台数据,直接生成我们需要的数据格式。

脚本编写

1. 处理数据字典中的数据

一般数据字典表里的数据可能在excel文件里,也可能是在在线网页中,但它一般都是一个表格的形式,比如以下这种:
image

我们只需要复制前两列的内容,字段和字段类型。
明白我们的需求后,开始编写js脚本:

// 定义一个方法
const dealDictionaryKey = (string) => {
  // 复制出来的数据是多行的形式,所以我们以'\n'去分开每一行数据
  const strArr = string.split('\n');
  let newString = '';
  // 遍历每行数据
  for (let item of strArr) {
    // 空白行跳过
    if (item.trim() === '') continue;
    // 替换常用的后台数据类型为js类型,如果有遗漏请自行添加 /t是单元格字段和单元格类型之间的隔离符号
    item = item.replace(/\t/, ': ').replace('INTEGER', 'number').replace(/TIMESTAMP|DATE/, 'Date')
        .replace(/TEXT|VARCHAR\(\d*\)|CHAR\(\d*\)/, 'string');
    // 递归是因为可能一个字段里有多个下划线
    item = toUppercase(item);
    newString += item + ';\n';
  }
  console.log(newString)
}
// 递归字符串,替换字符串中每一个 下划线+小写字母 为 大写字母 的形式
const toUppercase = (str) => {
    const idx = str.indexOf('_');
    // 替换到没有下划线为止
    if(idx === -1) return str;
    // 下划线+小写字母
    const oldStr = str.substr(idx, 2);
    // 生成的大写字母
    const initial = str.substr(idx + 1, 1).toUpperCase();
    // 替换
    str = str.replace(oldStr, initial);
    str = toUppercase(str);
    return str;
}
// 赋值测试
const str =
`
department_id	INTEGER
department_name	VARCHAR(125)
department_type	CHAR(1)
	
	
create_user	VARCHAR(25)
create_time	TIMESTAMP
update_user	VARCHAR(20)
update_time	TIMESTAMP

`
// 执行
dealDictionaryKey(str);

执行结果

departmentId: number;
departmentName: string;
departmentType: string;
createUser: string;
createTime: Date;
updateUser: string;
updateTime: Date;

2. 处理后台代码中复制的数据

我们直接查看后台代码,然后复制出来进行处理。后台代码一般形式如下:
image

我们直接复制花括号中的内容,进行处理。js脚本编写如下:

// 定义一个方法
const dealServerKey = (str) => {
  // 复制出来的数据是多行的形式,所以我们以'\n'去分开每一行数据
  const strArr = str.split('\n')
  let newString = ''
  // 遍历每行数据
  for (let item of strArr) {
    // 空行跳过
    if (item.trim() === '' || item.indexOf('private') === -1) continue;
    // 删除无用的private字段
    item = item.replace('private', '').replace(';', '').trim();
    // 先将字段和类型分开
    const keys = item.split(' ');
    const type = keys[0].replace(/Integer|BigDecimal/, 'number').replace('String', 'string')
    // 处理后调换位置
    newString += keys[1] + ': ' + type + ';\n';
  }
  console.log(newString)
}
// 输入测试
const str =  `
    private Integer departmentId;

    private String departmentName;

    private String departmentType;

    private String departmentTypeName;

    private Date createTime;
`
// 执行
dealServerKey(str);

执行结果

departmentId: number;
departmentName: string;
departmentType: string;
departmentTypeName: string;
createTime: Date;

使用

脚本编写后,当然可以在浏览器控制台直接使用,每次的输入内容和调用执行需要手动替换。不过这样使用不太直观方便,我这边一般都会使用
在线的执行js工具
,百度谷歌一搜很多。
以下以我使用的
run-js
为例:
image

将脚本复制到左侧代码框内,然后每次去替换
str
的赋值,点击中间的
执行
按钮,结果即可在右侧展示。
最后复制右侧内容到代码里即可。

在处理PDF文件时,了解页面的大小、方向和旋转角度等信息对于PDF的显示、打印和布局设计至关重要。本文将介绍如何使用免费.NET 库通过C#来读取PDF页面的这些属性。

  • C# 读取PDF页面大小(宽度、高度)
  • C# 判断PDF页面方向
  • C# 检测PDF页面旋转角度


免费库
Free Spire.PDF for .NET
提供了接口来获取PDF页面信息,我们可以从官网
下载产品包
后手动添加引用,或者直接通过NuGet安装。

PM> Install-Package FreeSpire.PDF

输入文档如图:

C# 读取PDF页面大小(宽度、高度)

免费Spire.PDF提供了
PdfPageBase.Size.Width

PdfPageBase.Size.Height
属性来获取指定PDF页面的宽度和高度。
获取到的值默认单位为磅(point),如果想要将其转换为厘米、毫米等常见单位,可以通过
PdfUnitConvertor
类的
ConvertUnits(float value, PdfGraphicsUnit from, PdfGraphicsUnit to)
方法进行转换。

示例代码如下:

usingSystem;usingSystem.Text;usingSpire.Pdf;usingSpire.Pdf.Graphics;namespaceGetPDFPageSize
{
classProgram
{
static void Main(string[] args)
{
//加载PDF文件 PdfDocument pdf = newPdfDocument();
pdf.LoadFromFile(
"示例.pdf");//获取第一页 PdfPageBase page = pdf.Pages[0];//获取页面宽度和高度(默认单位为point) float pointWidth =page.Size.Width;float pointHeight =page.Size.Height;//创建PdfUnitConvertor对象用于转换单位 PdfUnitConvertor unitCvtr = newPdfUnitConvertor();//将单位从磅(point)转换为厘米 float centimeterWidth =unitCvtr.ConvertUnits(pointWidth, PdfGraphicsUnit.Point, PdfGraphicsUnit.Centimeter);float centimeterHeight =unitCvtr.ConvertUnits(pointHeight, PdfGraphicsUnit.Point, PdfGraphicsUnit.Centimeter);//将单位从磅(point)转换为毫米 float millimeterWidth =unitCvtr.ConvertUnits(pointWidth, PdfGraphicsUnit.Point, PdfGraphicsUnit.Millimeter);float millimeterHeight =unitCvtr.ConvertUnits(pointHeight, PdfGraphicsUnit.Point, PdfGraphicsUnit.Millimeter);//输出PDF页面宽高度信息 Console.WriteLine("该PDF页面大小为(以磅为单位): 宽度" + pointWidth + "pt, 高度" + pointHeight + "pt");
Console.WriteLine(
"该PDF页面大小为(以厘米为单位): 宽度" + centimeterWidth + "cm, 高度" + centimeterHeight + "cm");
Console.WriteLine(
"该PDF页面大小为(以毫米为单位): 宽度" + millimeterWidth + "mm, 高度" + millimeterHeight + "mm");

}
}
}

输出结果:

C# 判断PDF页面方向

页面的方向通常以横向或纵向表示。要判断指定PDF页面的方向:

  1. 先获取页面宽度和高度
  2. 再比较这两个值。(如果宽度大于高度,则页面方向为横向,反之则为纵向。)

示例代码如下:

usingSpire.Pdf;usingSystem;namespaceGetPDFPageOrientation
{
classProgram
{
static void Main(string[] args)
{
//加载PDF文档 PdfDocument pdf = newPdfDocument();
pdf.LoadFromFile(
"示例.pdf");//获取第一页 PdfPageBase page = pdf.Pages[0];//获取页面宽度和高度 float width =page.Size.Width;float height =page.Size.Height;//通过比较页面宽度和高度来判断页面方向 if (width >height)
{
Console.WriteLine(
"当前页面方向为横向。");
}
else{
Console.WriteLine(
"当前页面方向为纵向。");
}
}
}
}

输出结果:

C# 检测PDF页面旋转角度

通过
PdfPageBase.Rotation

属性可以获取指定PDF页面的旋转角度。如果为 0,则表示页面保持原来的方向。

示例代码如下:

usingSpire.Pdf;usingSystem;namespaceGetPDFPageOrientation
{
classProgram
{
static void Main(string[] args)
{
//加载PDF文档 PdfDocument pdf = newPdfDocument();
pdf.LoadFromFile(
"示例.pdf");//获取第一页 PdfPageBase page = pdf.Pages[0];//获取页面的旋转角度并输出结果 PdfPageRotateAngle rotationAngle =page.Rotation;string rotation =rotationAngle.ToString();

Console.WriteLine(
"当前页面旋转角度为:" +rotation);
}
}
}

输出结果:


– 如有任何疑问,可前往
论坛
交流。

数据库服务器运维的最佳实践涵盖了多个方面,包括硬件选择、系统配置、性能优化、安全管理、数据备份与恢复、高可用性和灾难恢复等。以下将详细阐述这些方面,并给出部分可执行的代码示例,但请注意,由于环境差异,某些代码可能需要调整才能直接运行。

1. 硬件选择

  • 处理器(CPU)
    :选择多核高主频的处理器,如Intel Xeon或AMD EPYC系列,以满足高并发和复杂查询的需求。
  • 内存(RAM)
    :根据数据库大小和并发需求配置足够的内存,以减少磁盘I/O操作。
  • 存储
    :使用SSD(固态硬盘)替代HDD(机械硬盘),并考虑RAID 10配置以提高性能和可靠性。
  • 网络接口卡(NIC)
    :选择高带宽、低延迟的网络接口卡,如10Gbps或更高。

2. 系统配置与优化

2.1 操作系统选择

Linux是大多数数据库服务器的首选操作系统,如CentOS、RHEL或Ubuntu。

2.2 内核参数调优

# 调整内存管理参数  
sysctl -w vm.swappiness=10  
  
# 调整I/O调度策略  
echo deadline > /sys/block/sda/queue/scheduler

2.3 文件系统选择

使用ext4、XFS或ZFS等高性能文件系统,并启用
noatime

nodiratime
选项。

# 挂载文件系统时启用noatime和nodiratime  
mount -o remount,noatime,nodiratime /

3. 数据库配置与优化

3.1 MySQL参数调整

# 查看当前参数设置  
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"  
  
# 修改InnoDB缓冲池大小  
mysql -u root -p -e "SET GLOBAL innodb_buffer_pool_size = 512M;"  
  
# 修改后需要重启MySQL服务或重启实例使设置生效

3.2 索引优化

定期分析查询日志,优化SQL查询语句,确保常用查询字段有合适的索引。

-- 示例:为某个表的某个字段添加索引  
ALTER TABLE my_table ADD INDEX idx_column_name (column_name);

4. 性能监控

使用Prometheus、Grafana等工具监控数据库的性能指标,包括CPU使用率、内存使用、磁盘I/O和网络流量等。

5. 数据备份与恢复

5.1 备份策略

制定全量备份和增量备份策略,确保数据可恢复性。

# 使用mysqldump进行逻辑备份  
mysqldump -u username -p database_name > backup.sql  
  
# 增量备份示例(需结合二进制日志)  
# 注意:增量备份的实现较复杂,这里仅提供概念

5.2 验证备份

定期验证备份文件的有效性,确保可以在必要时恢复数据。

6. 安全管理

6.1 权限管理

遵循最小权限原则,限制用户的数据库访问权限。

-- 示例:为用户分配特定表的查询和插入权限  
GRANT SELECT, INSERT ON mydb.mytable TO 'user'@'localhost';

6.2 加密

对敏感数据进行加密存储,使用SSL/TLS加密传输层通信。

# 在MySQL配置文件中启用SSL  
[mysqld]  
require_secure_transport=ON

6.3 审计日志

启用数据库的审计日志功能,记录关键操作的日志。

# 在MySQL配置文件中启用审计日志  
[mysqld]  
general_log=ON  
general_log_file=/var/log/mysql/general.log

7. 高可用性和灾难恢复

7.1 主从复制

设置主从复制,提高数据冗余度,减轻单点故障的影响。

7.2 集群部署

使用数据库集群技术(如MySQL Cluster、Oracle RAC)提高可用性。

8. 自动化与AI运维

使用自动化工具和AI技术进行预测性维护和故障诊断,减少人为错误。

9. 运维流程与文档

9.1 评估现状

了解当前数据库系统的状况,识别存在的问题。

9.2 制定计划

基于评估结果,制定改进计划。

9.3 逐步实施

按照计划逐步实施各项改进措施。

前言


Vue 3.5.0-beta.3
版本中新增了一个
base watch
函数,这个函数用法和我们熟知的
watch API
一模一样。区别就是我们之前用的
watch API
是和Vue组件以及生命周期是一起实现的,他们是深度绑定的。而Vue3.5新增的
base watch
函数是一个新的函数,他的实现和Vue组件以及生命周期没有一毛钱关系。

欧阳写了一本开源电子书
vue3编译原理揭秘
,看完这本书可以让你对vue编译的认知有质的提升。这本书初、中级前端能看懂,完全免费,只求一个star。

@vue/runtime-core

vue3是模块化设计,他将核心功能拆分为多个独立的模块,如下图:
modules

比如
reactivity
模块中就是响应式的核心代码、
runtime-core
模块就是运行时相关的核心代码、
compiler-core
模块就是编译相关的核心代码。

并且这些模块还被单独当作npm包进行发布,命名规则是
@vue+模块名
。比如
reactivity
模块对应的npm包就是
@vue/reactivity
。如下图:
npm

所以如果我们只需要vue的响应式功能,理论上只需要导入
@vue/reactivity
包即可。比如我之前的文章:
涨见识了!脱离vue项目竟然也可以使用响应式API
,在这篇文章中我就介绍了如何脱离Vue项目,在
node.js
项目中使用vue的响应式API。

但是不知道你有没有注意到,在demo中我是
require("vue")
,而不是
require("@vue/reactivity")

因为
watch
不是由
@vue/reactivity
中导出的,而是由
@vue/runtime-core
中导出的,如果我只引入
@vue/reactivity
就会报错了。

const { ref, watch, watchEffect } = require("vue");

const count = ref(0);

// 模拟count变量的值修改
setInterval(() => {
  count.value++;
}, 1000);

watch(count, (newVal) => {
  console.log("触发watch", newVal);
});

watchEffect(
  () => {
    console.log("触发watchEffect", count.value);
  },
  {
    flush: "sync",
  }
);

watch
的实现是和vue组件以及生命周期深度绑定的,而vue组件以及生命周期明显是和响应式无关的。他们的实现是在
runtime-core
模块中,而非
reactivity
模块中,这也就是为什么
watch
的实现是放在
runtime-core
模块中。
runtime

据说性能是 Taro 10 倍的小程序框架
vuemini
底层也是依靠
@vue/reactivity
实现的,但是由于watch是由
@vue/runtime-core
中提供的,小程序框架却只引入了
@vue/reactivity
,所以作者不得不手写了一个
watch
函数。
vue-mini

重构watch函数

智子在写
Vue Vapor
时又拆了一个新的模块,叫做
runtime-vapor
。如果你不了解
Vue Vapor
,可以看看我之前的文章:
没有虚拟DOM版本的vue(Vue Vapor)

vue-vapor

他们遇到一个问题需要在
runtime-vapor
模块中使用watch函数,而watch函数是位于
runtime-core
模块中。但是又不应该在
runtime-vapor
模块中直接引用
runtime-core
模块,所以Vue Vapor团队的绚香音就将watch函数重构到了
reactivity
模块中,这样在
runtime-vapor
模块中直接使用
reactivity
模块中的watch函数就行了。

这也就是为什么需要重构watch函数到
reactivity
模块中。

在欧阳的个人看法中watch函数本来就是属于响应式中的一部分,他在
runtime-core
模块中反而不合理。在欧阳第一次看vue3源码时就在奇怪为什么没有在
reactivity
模块中找到watch函数的实现,而是在
runtime-core
模块中实现的。

当watch函数重构到
reactivity
模块后,小程序框架
vuemini
的作者也发了一篇帖子。
X

watch函数重构到
reactivity
模块后,小程序框架中手写的watch函数都不需要了,因为
reactivity
模块已经提供了。

看见
完了!这下 Vue Mini 真成 @vue/reactivity 套壳了...
这个评论后,对不起!
杨明山
大佬欧阳确实没忍住笑出了声。

总结

vue3.5版本中,Vue Vapor团队在
reactivity
模块中重构实现了一个watch函数。重构的这个watch函数和我们现在使用的watch函数用法是一样的,区别在于以前的watch函数的实现和Vue组件以及生命周期是深度绑定的,而重构的watch函数和Vue组件以及生命周期一毛钱关系都没有。

这个改动对于普通开发者可能没什么影响,但是对于下游项目,比如
Vue mini
来说还是很受益的。因为以前他们需要自己去手写watch函数,现在
reactivity
提供了后就不需要这些手写的watch函数了。

关注公众号:【前端欧阳】,给自己一个进阶vue的机会

另外欧阳写了一本开源电子书
vue3编译原理揭秘
,看完这本书可以让你对vue编译的认知有质的提升。这本书初、中级前端能看懂,完全免费,只求一个star。