徐琦的博客

  • 首页

  • 归档

搬瓦工主机修改SWAP分区大小

发表于 2015-12-14 | 更新于 2019-01-14 | 分类于 VPS

搬瓦工主机的默认虚拟内存空间总是很小, 所以需要为其配置更大的虚拟内存

查看SWAP分区大小

free -h
free -m

删除所有SWAP分区

swapoff -a

添加一个swap文件作为swap

# 其中 bs是每块的大小,count是块的数量,bs*count,就是swap文件的大小了,这里就是1M*1024=1G。大家可以自己调整count的数量。
# 此外,/root/swapfile是swap文件的路径,可以根据需求修改。
dd if=/dev/zero of=/root/swapfile bs=1M count=1024
# 建议将权限设置为600
chmod 600 /root/swapfile

# 格式化swap文件
mkswap /root/swapfile

启用swap分区文件

swapon /root/swapfile

添加开机启动

修改 /etc/fstab 这个文件,添加或者修改这一行

/root/swapfile swap swap defaults 0 0

相对目录处理方法备忘

发表于 2015-10-21 | 更新于 2019-03-21 | 分类于 系统运维

一个项目往往除了包含程序文件外,还包含一些资源文件, 我们需要把这些程序和资源都打包到一个文件夹中, 并创建一个启动脚本来启动程序,并为程序指定资源位置,这时候就需要使用相对目录

Windows下的目录设置

windows下bat文件获取当前目录, 以及将bat所在目录设置为当前目录的方法

@echo off
echo %cd%
echo %~dp0
cd /d %~dp0
echo %cd%

将以上脚本存为C:\tmp\test\path.bat, 在C:\tmp\test目录下执行test\path.bat,运行结果为:

C:\tmp
C:\tmp\test\
C:\tmp\test

Linux下的目录设置

#!/bin/bash
pwd
basepath=$(cd `dirname $0`; pwd)
pwd
echo $basepath
cd $basepath
pwd

将以上脚本存为/tmp/test/path.sh, 在/tmp目录下执行test/path.sh,运行结果为:

/tmp
/tmp
/tmp/test
/tmp/test

Springboot指定静态资源目录

假设启用脚本、jar文件、html目录平级, 而网页文件放在html目录中,在windows下启动脚本如下

@echo off
cd /d %~dp0
java -jar xxx.jar --spring.resources.static-locations=file:%cd%\html

在Linux下启动脚本如下:

#!/bin/bash
basepath=$(cd `dirname $0`; pwd)
cd $basepath
java -jar xxx.jar --spring.resources.static-locations=file:${basepath}/html

自定义配置文件解析

发表于 2014-05-15 | 更新于 2019-01-25 | 分类于 工具开发

在制作小工具, 绿色软件时, 经常需要定义一些小型配置文件, 这些配置文件格式一般是针对具体业务的,是对业务的精准定义, 因此格式需要清爽简单。

需求描述

这里以一个端口转发的小工具的配置文件为例:

  • 支持若干组端口转发配置同时工作
  • 每组端口转发配置需要配置一个监听端口, 以及一组转发目标地址
  • 每组转发目标地址包括一个ip和一个端口

用json描述如下

[
{
"bind": 27011,
"forwarding":[
{"ip":"192.168.30.1", "port": 27011},
{"ip":"192.168.30.2", "port": 27011}
]
},
{
"bind": 27012,
"forwarding":[
{"ip":"192.168.30.1", "port": 27012},
{"ip":"192.168.30.2", "port": 27012}
]
},
{
"bind": 27013,
"forwarding":[
{"ip":"192.168.30.1", "port": 27013},
{"ip":"192.168.30.2", "port": 27013}
]
}
]

以上配置文件的意思是:
监听27011端口, 并将接收到的数据转发到192.168.30.1:27011上, 若该端口无法连接, 则发送到192.168.30.2:27011上.
接着的27012和27013端口也是同理。

直接使用json当配置文件当然是可以的, 而且有现成的类库解析,不过在这个场景下, 我更加倾向于使用自定义的格式, 这样看起来更加清爽, 比如以下格式:

27011|192.168.30.1:27011|192.168.30.2:27011
27012|192.168.30.1:27012|192.168.30.2:27012
27013|192.168.30.1:27013|192.168.30.2:27013

我们需要把这段文本解析为List<Tuple<IPEndPoint, List<IPEndPoint>>>,
下面就描述如何自己编写代码来解析这段文本

硬编码解析

static List<Tuple<IPEndPoint, List<IPEndPoint>>> GetConfig()
{
var result = new List<Tuple<IPEndPoint, List<IPEndPoint>>>();
var lines = File.ReadAllLines("forward.txt"); //从配置文件中读取所有行
foreach (var line in lines)
{
string[] fields = line.Split('|'); //按|进行切割
if (fields.Length < 2) continue; //过滤空行
IPEndPoint main = new IPEndPoint(IPAddress.Any, int.Parse(fields[0])); //提取监听端口
List<IPEndPoint> forwordingList = new List<IPEndPoint>();
for (int i = 1; i < fields.Length; i++) //提取转发列表
{
int index = fields[i].IndexOf(":");
if (index != -1)
{
forwordingList.Add(new IPEndPoint(IPAddress.Parse(fields[i].Substring(0, index)), int.Parse(fields[i].Substring(index+1))));
}
}
if (forwordingList.Count > 0) //过滤掉空列表
{
result.Add(new Tuple<IPEndPoint, List<IPEndPoint>>(main, forwordingList));
}
}
return result;
}

这段代码的思路是从配置文件读出所有行, 然后对每行以分隔符|进行切割,切割出各个字段后, 再一一分析, 填充进目标数据结构。

可以看到, 硬编码解析的话,代码比较冗长, 而且语义不清, 如果这段代码不是自己亲手写的, 甚至还难以看懂。

正则表达式解析

在解析思路不变的情况下, 我们可以用正则表达式改造以上代码:

static List<Tuple<IPEndPoint, List<IPEndPoint>>> GetConfig1()
{
var result = new List<Tuple<IPEndPoint, List<IPEndPoint>>>();
Regex r = new Regex(@"^(\d+)(\|.+:\d+)+");
Regex r2 = new Regex(@"\G\|([\d\.]+):(\d+)");
var lines = File.ReadAllLines("forward.txt");

foreach (var line in lines)
{
if (r.IsMatch(line))
{
var match = r.Match(line);
IPEndPoint main = new IPEndPoint(IPAddress.Any, int.Parse(match.Groups[1].Value));
List<IPEndPoint> forwordingList = new List<IPEndPoint>();
foreach (Match match2 in r2.Matches(match.Groups[2].Value))
{
forwordingList.Add(new IPEndPoint(IPAddress.Parse(match2.Groups[1].Value), int.Parse(match2.Groups[2].Value)));
}
result.Add(new Tuple<IPEndPoint, List<IPEndPoint>>(main, forwordingList));
}
}
return result;
}

因为我们的示例比较简单, 使用正则表达式并未减少代码行数, 只不过使用正则表达式扩展性更强了

使用lambda表达式

为了压缩代码行数,以及更加清晰的语义表达, 我们还可以使用大杀器lambda表达式, 经过压缩后代码如下:

static List<Tuple<IPEndPoint, List<IPEndPoint>>> GetConfig2()
{
Regex r = new Regex(@"^(\d+)(\|.+:\d+)+");
Regex r2 = new Regex(@"\G\|([\d\.]+):(\d+)");

return File.ReadAllLines("forward.txt").ToList()
.Where( i => r.IsMatch(i))
.Select( i=> r.Match(i))
.Select( i=> new Tuple<IPEndPoint, List<IPEndPoint>>(
new IPEndPoint(IPAddress.Any, int.Parse(i.Groups[1].Value)),
(from Match j in r2.Matches(i.Groups[2].Value) select new IPEndPoint(IPAddress.Parse(j.Groups[1].Value), int.Parse(j.Groups[2].Value))).ToList()
)
).ToList();
}

可以看到代码行数下降了很多, 而且这种方式的表达更加清晰。完整代码

高可用的端口转发

发表于 2014-05-11 | 更新于 2019-03-21 | 分类于 系统运维

在windows下, 利用netsh可以实现端口转发功能, 能满足大部分日常需求, 但在某些情况下, 在后端节点不可用的时候, 需要将数据转发至另一节点, 这时就只能自行编码实现了。

本文将使用C#做一个简单的实现: 完整代码

解决思路

  • 因为需要转发数据, 所以程序肯定是要监听端口的
  • 为了避免数据混乱和复杂性, 这里每个端口只接收一个连接, 所以接收到新连接后, 关闭端口监听
  • 若长时间没有数据通讯, 则主动关闭连接, 释放资源
  • 开启两个工作线程, 一个叫代理线程, 一个叫转发线程

代理线程工作流程

代理线程负责接收外来连接, 并确保同一时间只有一个外来连接, 接收到连接后读取数据, 读取到数据后, 将数据转发到指定目标地址。

graph LR
    listen[监听]
    accept[接受连接]
    read[读取数据]
    forwarding[转发数据]
    listen-->accept
    accept-->read
    read-->forwarding
    forwarding-->read

主要代码:

server = new TcpListener(listen);
server.Start(1);
client = server.AcceptTcpClient();
server.Stop(); //停止监听, 同一时间只接收一个连接
server = null;
listenStream = client.GetStream();
listenStream.ReadTimeout = READ_TIMEOUT;
while (true)
{
int len = listenStream.Read(buffer, 0, BUFF_SIZE);
if (len > 0)
{
byte[] tmp = new byte[len];
Array.Copy(buffer, tmp, len);
Forwarding(tmp); //转发数据
}
else
{
//.. 处理连接中断
}
}

转发线程工作流程

转发线程负责连接目标地址, 一个连不上再去连接下一个, 当连上后, 等待接收数据, 接收到数据后, 将数据转发到代理连接上去。

主要代码:

client = new TcpClient();
var result = client.BeginConnect(currentEndPoint.Address, currentEndPoint.Port, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(CONNECT_TIMEOUT));
if (!success) //超时处理
{
Console.WriteLine("connect {0} timeout", currentEndPoint);
continue;
}
client.EndConnect(result);
forwardingStream = client.GetStream();
forwardingStream.ReadTimeout = READ_TIMEOUT;
while (true)
{
int len = forwardingStream.Read(buffer, 0, BUFF_SIZE);
if (len > 0)
{
byte[] tmp = new byte[len];
Array.Copy(buffer, tmp, len);
Response(tmp);
}
else
{
//.. 处理连接中断
}
}

JavaScript中的replaceAll

发表于 2012-04-06 | 更新于 2019-03-18 | 分类于 Javascript

总所周知,js的字符串替换函数replace()只能替换目标字符串, 网上给出的解法通常是:

String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.replace(new RegExp(search, 'g'), replacement);
};

这个方法的问题是因为使用了正则表达式, 所以search串中如果出现了正则表达式的关键字, 会出现意想不到的效果,甚至会报错

比如:

var aaa="s[aves[ave";
aaa.replaceAll("s[ave","slave"); //Uncaught SyntaxError: Invalid regular expression: /s[ave/:
aaa.replaceAll("s\\[ave","slave"); //只这样调用, 加入转义字符

这种方式实在恶心, 可以考虑使用split再join的方式

String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.split(search).join(replacement);
};

Sql Server 2005数据迁移备忘

发表于 2012-03-01 | 更新于 2019-01-25 | 分类于 数据库

项目中经常需要进行数据迁移, 这里的的迁移主要只指历史日志的迁移。

查询日志基本情况

--查询最早最晚的日志
SELECT * FROM [zlt-monitors].[dbo].[AlarmLogs] WHERE alarmLogId= (SELECT min(alarmLogId) FROM [zlt-monitors].[dbo].[AlarmLogs])

SELECT * FROM [zlt-monitors].[dbo].[AlarmLogs] WHERE alarmLogId= (SELECT max(alarmLogId) FROM [zlt-monitors].[dbo].[AlarmLogs])

SELECT * FROM [zlt-monitors].[dbo].[VarLogs] where id=( SELECT min( id) FROM [zlt-monitors].[dbo].[VarLogs])

SELECT * FROM [zlt-monitors].[dbo].[VarLogs] where id=( SELECT max( id) FROM [zlt-monitors].[dbo].[VarLogs])

--估算日志数量
select ( SELECT max( id) FROM [zlt-monitors].[dbo].[VarLogs]) - ( SELECT min( id) FROM [zlt-monitors].[dbo].[VarLogs])

--查看各个表空间占用情况
exec sp_MSForEachTable
@precommand=N'create table ##(
表名 sysname,
记录数 int,
保留空间 Nvarchar(10),
使用空间 varchar(10),
索引使用空间 varchar(10),
未用空间 varchar(10))',
@command1=N'insert ## exec sp_spaceused ''?''',
@postcommand=N'select * from ## order by 记录数 desc;drop table ## '

日志导出

--直接导出表
bcp [zlt-monitors].[dbo].[AlarmLogs] out D:\db\alarmlog.txt -c -T

--按sql导出
bcp "select * from [zlt-monitors].[dbo].[VarLogs] where (recordTime between '2010-01-01 00:00:00' and '2010-02-01 00:00:00')" queryout D:\db\varlog.2010-01.txt -c -T

--修正某些日期
bcp "SELECT [id],[varId],[varName],[varValue], DATEADD(d,36,[recordTime]) as recordTime,[synmark] FROM [zlt-monitors].[dbo].[VarLogs] where ([recordTime] between '2018-01-01' and '2018-01-29') " queryout D:\db\varlog.20180206.20180306.txt -c -T

bcp "SELECT [alarmLogId],[alarmType],[associateId],DATEADD(d,37,[alarmTime]) as alarmTime,[alarmStuts],[alarmValue],[varName],[remark],[alarmLevel],[linkagePolicies] FROM [zlt-monitors].[dbo].[AlarmLogs] where ([alarmTime] between '2018-01-01 00:00:00' and '2018-01-30 00:00:00')" queryout D:\db\alarmlog.20180207.20180307.txt -c -T

日志导入

导出的文件是文本格式的, 并且没有列头, 所以字段需要对应好

bcp [zlt-monitors].[dbo].[VarLogs] in D:\db\varlog.2010-01.txt -c -T

日志清理

--清除表数据
truncate table Varlogs

设置外键层叠删除

alter table AlarmConfirm drop constraint FK_ALARMCON_ALARMCONF_ALARMLOG

alter table AlarmConfirm add constraint FK_ALARMCON_ALARMCONF_ALARMLOG foreign key (alarmLogId) references AlarmLogs(alarmLogId) on delete cascade
12
徐琦

徐琦

专注于应用程序框架开发

16 日志
9 分类
11 标签
GitHub E-Mail
快速访问
  • 知乎
  • bilibili
  • 谷歌翻译
  • 网易公开课
  • stackoverflow
  • 中国大学MOOC
© 2008 – 2019 徐琦
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Pisces v6.7.0
Hosted by GitHub Pages