CMake是跨平台的构建工具,公司主要使用这个工具生成,掌握了CMake用法觉得自动化构建还是很容易的
1 | - project |
注释: 行注释,以#
开头
变量
command (args …)
set(var a;b;c) <=> set(var a b c)
add_executable(${var}) <=> add_executable(a b c)
条件语句
1 | if(var) #var非empty,0,FALSE |
1 | 第一种: |
1 |
|
1 |
|
1 | 1. MESSAGE({SEND_ERROR|STATUS|FATAL_ERROR} "this is message") #打印编译信息,FATAL_ERROR会停止编译 |
workspace
编译common库
编译具体项目
1 | #include <iostream> |
1 | cmake_minimum_required(VERSION 2.8) |
1 | //生成VS2015 64位的应用,默认是x86 |
syntax
声明可以选择protobuf的编译器版本(v2和v3)syntax="proto2";
选择2版本,各个字段必须明确标注编号以确定序列化后二进制数据字段的位置syntax="proto3";
选择3版本,没有强制使用字段编号required
optional
repeated
proto Type | C++ Type | Notes |
---|---|---|
int32 | int32 | 有符号32位整型数,非固定长度编码,编码效率比sint32低 |
int64 | int64 | 有符号64位整型数,非固定长度编码 |
uint32 | uint32 | 无符号32位整型数,非固定长度编码 |
uint64 | uint64 | 无符号64位整型数,非固定长度编码 |
sint32 | int32 | 有符号32位整型数,非固定长度编码,效率较高 |
sint64 | int64 | 有符号64位整型数,非固定长度编码,效率较高 |
fixed32 | uint32 | 无符号,固定4字节编码,数据大于2^28时,效率比uint32高 |
fixed64 | uint64 | 无符号,固定8字节编码,数据大于2^56时,效率比uint32高 |
sfixed32 | int32 | 有符号,固定4字节编码 |
sfixed64 | int64 | 有符号,固定8字节编码 |
bool | bool | 布尔值 |
float | float | 浮点数 |
double | double | 浮点数 |
string | string | 必须为UTF-8编码或者7bit ASCII字符串 |
bytes | string | 字节数组 |
字段类型 | 二进制类型 | 二进制编码值 |
---|---|---|
int32,int64,uint32,uint64,sint32,sint64,bool,enum | Varint(可变长度int) | 0 |
fixed64,sfixed64,double | 64bit固定长度 | 1 |
string,bytes,inner messages(内部嵌套),packaed repeated fields(repeated字段) | Length-delimited | 2 |
groups(deprecated) | Start group | 3 |
groups(deprecated) | Endd group | 4 |
fixed32,sfixed32,float | 32bit固定长度 | 5 |
1 | dec(300) |
536870911
.filed_num<<3|wire_type
,编码后的二进制长度是变长
的Zigzag
方法来避免上述问题Zigzag
映射规则1 | Zigzag(n) = (n<<1)^(n>>31) ,n为sint32时 |
Original(原始值) | (编码后的值)EncodeAs |
---|---|
0 | 0 |
-1 | 1 |
1 | 2 |
-2 | 3 |
2 | 4 |
-3 | 5 |
2147483647 | 4294967294 |
-2147483648 | 4294967295 |
string,bytes都属于length-delimited编码,length-delimited(wire_type=2)的编码方式:key+length+content
完整示例
type.proto
1 | message IntType { |
初始值
1 | //int |
二进制表示
IntType
| 类型 | key | EncodedAs key | value | EncodedAs value | EncodedAs String | Notes |
| :–: | :–: | :————: | :—: | :————-: | :—: | :—: |
| int32 | 1 | 1<<3 OR 0 = 00001000
| -1 | 11111111(9 times) 00000001 | 00001000 11111111(9times) 00000001 | varints编码 |
| int64 | 2 | 2<<3 OR 0 = 00010000
| -1 | 11111111(9 times) 00000001 | 00010000 11111111(9times) 00000001 | varints编码 |
| sint32 | 3 | 3<<3 OR 0 = 00011000
| -1 | 00000001 | 00011000 00000001| 无符号变换 |
| sint32 | 4 | 4<<3 OR 0 = 00100000
| -1 | 00000001 | 00100000 00000001| 无符号变换 |
| sfixed32 | 5 | 5<<3 OR 5 = 00101101
| -1 | 11111111(4 times) | 00011000 11111111 11111111 11111111 11111111 | 固定4byte长度 |
| sfixed32 | 6 | 6<<3 OR 1 = 00110001
| -1 | 11111111(8 times) | 00011000 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 | 固定8byte长度 |
UIntType
| 类型 | key | EncodedAs key | value | EncodedAs value | EncodedAs String | Notes |
| :–: | :–: | :————: | :—: | :————-: | :—: | :—: |
| uint32 | 1 | 1<<3 OR 0 = 00001000
| 1 | 00000001 | 00001000 00000001| 无符号 |
| uint64 | 2 | 2<<3 OR 0 = 00010000
| 2 | 00000010 | 00010000 00000010| 无符号 |
| fixed32 | 3 | 3<<3 OR 5 = 00011101
| 1 | 00000001 00000000(3 times) | 00011101 00000001 00000000(3 times) | 固定4byte长度 |
| fixed64 | 4 | 4<<3 OR 1 = 00100001
| 2 | 00000010 00000000(7 times) | 00011101 00000001 00000000(7 times) | 固定8byte长度 |
FType
| 类型 | key | EncodedAs key | value | EncodedAs value | EncodedAs String | Notes |
| :–: | :–: | :————: | :—: | :————-: | :—: | :—: |
| float | 1 | 1<<3 OR 5 = 00001101
| 0.1 | 11001101 11001100 11001100 00111101 | 00001101 11001101 11001100 11001100 00111101 | IEEE浮点数,4byte |
| double | 2 | 2<<3 OR 1 = 00010001
| 0.2 | 10011010 10011001 10011001 10011001 10011001 10011001 11001001 00111111 | 00010001 10011010 10011001 10011001 10011001 10011001 10011001 11001001 00111111| IEEE浮点数,8byte |
| bool | 3 | 3<<3 OR 0 = 00011000
| false | 00000000 | 00011000 00000000 | 固定1byte长度 |
BSType
| 类型 | key | EncodedAs key | value | EncodedAs value | EncodedAs String | Notes |
| :–: | :–: | :————: | :—: | :————-: | :—: | :—: |
| string | 1 | 1<<3 OR 2 = 00001010
| “hello,world.” | 00001100(length) 01101000 01100101 01101100 01101100 01101111 00101100 01110111 01101111 01110010 01101100 01100100 00101110(ASCII) | length+content编码 |
| bytes | 2 | 2<<3 OR 2 = 00010010
| “h”,”0x1”,”d”,”ILV”,”0xf” | 00000001 01101000 00010010 00000011 00110000 01111000 00110001 00010010 00000001 01100100 00010010 00000011 01001001 01001100 01010110 00010010 00000011 00110000 01111000 01100110 | 符号分割 |
message
定义中可以嵌套定义message
,enum
package
相当于C++的namespace
,外部引用时需要package名import
外部proto文件即可使用1 | //user.proto |
1 | //room.proto |
protoc --cpp_out=${DIR} proto-file...
protoc --cpp_out=${DIR} base-proto-file deliver-proto-file...
cmake
自动化编译1 | FIND_PACKAGE(Protobuf REQUIRED) |
set_xxx()
设置required
,optional
字段值add_xxx()
添加repeated
字段值set_xxx(int,x)
设置repeated
中元素的值required
字段需要初始化,可以通过IsInitialized
来检查是否完成message对象的初始化SerializedAsString()
,SerializedToString(std::string*)
序列化为std::stringSerializedToArray(void*,int)
序列化为byte数组SerializedToOstream(ostream*)
序列化到输出流ByteSize()
获取二进制字节序的大小,可用于初始化存放容器ParseFromString(std::string& data)
从字符串中反序列化ParseFromArray(const void *,int)
从字节序中反序列化ParseFromIstream(istream*)
从输入流中反序列化has_xxx()
用于检查相应字段是否存在数据xxx_size()
用于确定repeated
字段是否存在,0表示未序列化xxx()
返回required/optional
字段的const
值,只读模式,返回repeated
列表的指针,用于修改mutable_xxx()
返回字段指针,用于修改xxx(int)
返回repeated
字段列表的元素,只读mutable_b()
指针来初始化该字段,使用栈上的对象会导致程序异常google::protobuf::ShutdownProtobufLibrary()
以清理内存,否则会造成内存泄漏 在实际运用场景里,经常会遇到把大量数据有序输出,文件的排序等等问题。一般的排序算法针对数据量不大的情况下,二叉树排序和快速排序针对现实中的随机数据时间复杂度为O(NlgN),性能表现已经很不错了,因此这两种方法被广泛使用。
但是,在企业级数据量情况下,这两种排序耗费的时间就会显著增长,以至需要非常长的时间才能得到结果,而且在排序的过程中,由于插入和选择结点的操作会使得CPU满负荷工作,这对于长期工作的服务器来说是不利的。
因此,在这种海量数据排序的需求刺激下,产生了一种简易的”哈希”方法,那就是位图排序。
位图排序的思想非常简单,那就是将一个个数据映射到它在有序的数轴上的一点,这个数轴可以是一个数组,数字的大小就是哈希因子,所以在遍历一遍数据集,设置好这些数字的点表示之后,我们就可以根据相应位置的状态,反过来来确定数据集中是否有这个数字,由于我们是有序的遍历的,因此查找出来的数字也是有序的,这样间接地达到了我们的目的。
位图很像数学中的数轴的概念,有哪些数字,就会在相应的位置标记它们,最后数据都是有序的。而且这种方法,一个数字占用的位置只有一个bit,对于0~2<<32的4字节非负整数范围来说,最大的数为42亿,在实际应用场景下基本足够,而所需要的空间占用大概只需要2<<32/8/1024^2=512M的内存,在内存占用上和效率上都有着无可比拟的优势。
说了这么多,当然需要实际测试一番,才能证明位图排序真的在海量数据排序上具有优势。
例如在我的服务器上,随机生成1000W个整数,现在需要将它们排序。那么对于快速排序,二叉树排序和位图排序,分别需要多少时间呢?
- 机器环境GCC5.3.1,编译参数-std=c++11 -O3
- 源代码详见topcoder
###1. 快排
###2. 二叉树排序
###3. 位图排序
在实际运用]]>
]]>
- 随着互联网使用的时间增长,自己在网上的账号越来越多,但因为之前就注重安全功能,
所以我很少使用同一套账号系统。所以账号数量越来越多导致我难以记住,而且很多信息比
如网银包含有账号之外的安全信息。虽然chrome有记住密码功能,但一些安全问题,保密安全
码它都无能为力。- 之前考虑过使用1Password等软件,有些是有使用环境限制,而且不知道运作方式,不知道会不
会丢失或被攻击者窃取个人信息,这也是我想自己完全掌握自己的个人信息安全的原因。- 基于以上因素,我萌生了开发一套在线密码管理系统,这样既省略了同步因素,还能随时查询,
不用的时候导出文件就可以了。
- 随着互联网]]>
新年之前,给自己画了很多美好的期许,如同中学生涯的放假期间,带回去很多课本和试卷,
结果又原封不动地带回学校一样,这些定下的计划鲜有完成。大概也就是没有手生而已。我把
这种现象叫做心理上的自我安慰。
半个月过去了,我又回到那个朝九晚十的生活轨道,赶紧在进度报告之前,完成一些工作。经过
几天的不断写代码和测试,也算是完成了以下任务:
这大概是我学习生涯以来,如此有总结性和计划性的时刻,办公的电脑桌面文件夹里,都是各种各
样的todo-list和note。
下一步主要就是实现caont-rs冗余加/解码了,因此需要了解迦罗瓦运算,详细地阅读原作者的
实现了。
过渡期就是如此麻烦,还需要留心即将到来的暑期实习招聘,一边需要做准备了。毕竟不想再经历本科
毕业时候的迷茫和毫无准备的仓促。
快到猴年春节了,这一年过得很累,发生了太多的事,压力也大了起来,未来的工作,现在
的生活…都交织在我的脑海里。很想回去舒服地躺在床上看看书,或者就是发一下午呆。可
惜时间不等人,两周里还有很多要完成的事。
不过,能过第一个想回家吃妈妈做的菜的春节,我的心情还是有些愉快迫切的、
最近的两周老板也没怎么来,我手头的事情也比较棘手,有些像刺猬无从下嘴的感觉。检讨
一下工作就是零星做了些面试算法题,leetcode英文题对于目前浮躁的我来说有些难,DP、背包
也没有看,回家需要复习一下算法了。文件系统,加密模块,也需要做个雏形出来。
很想潇潇洒洒地在家里四处游荡,和朋友去爬山,然后静静坐在山峰最高处,静静地注视着芸
芸众生、人间烟火,做着无边无际的梦。
There are n bulbs that are initially off. You first turn on all the bulbs. Then, you turn off every second bulb. On the third round, you toggle every third bulb (turning on if it’s off or turning off if it’s on). For the nth round, you only toggle the last bulb. Find how many bulbs are on after n rounds.
Example:
Given n = 3.
At first, the three bulbs are [off, off, off].
After first round, the three bulbs are [on, on, on].
After second round, the three bulbs are [on, off, on].
After third round, the three bulbs are [on, off, off].
So you should return 1, because there is only one bulb is on.
就是说给n个开关分别对应控制这n个灯泡的亮灭,步骤如下:
分析完题目后,首先想到的就是把开关这些控制流程自己模拟
一遍,用布尔数组模拟开关,最后统计为true的开关数就可以
了。
那就开始写吧,leetcode支持c/c++,感觉够了,至于能不能
用STL库还不清楚,自己动手写一些常用数据结构,虽说性能有
损失,但是为了AC,也只能自己注意性能了,尽量不要手动分配
大的数组。
1 | int bulbswitcher(int n){ |
上传到leetcode jude,不一会儿出现了Limit except,发现它的测试数是10000000,
总用时1+s,这么大数字居然超时了.我自己在线下测试的结果:
普通编译:
开启-O2 优化
可以发现这种算法复杂度O(n^2),数字一大性能就不行了。普通编译结果也是超过1s,看来leetcode没有做优化,并且超时时间设置1s,
如此只能再想其他解法.
总觉得这样转换的状像是符合某种数学规律,也许是常年的理工教育培养了这种直觉,
我就试一试究竟有没有规律,基于现有代码很好改动。1
2
3
4
5void test(int max){
for(int i=0;i<max;++i){
printf("%3d:%3d\n",i,bulbswitcher(i));
}
}
打印结果:
居然有这么多一样的,看起来像台阶一样,必须满足某种条件才能增加一级.
那我再改进一下试探方法,为了一查究竟这么多相同的数字有什么关系,以及开关
数n和最后结果的关系,采用哈希表去做,c没有,其实开个大数组也行,我这里用
的是Go。1
2
3
4
5
6
7
8
9
10
11
12func test(n int){
m:=make(map[int]int,n)
for i:=0;i<1000;i++{
m[bulbswitcher(i)]++
}
//无序表,对key排序
sort.SortInts(map_keys)
for _,k:=range map_keys{
fmt.Printf("%3d:%3d\n",k,m[k])
}
}
运行结果:
果然有关系!原来最后的结果对应的原始开关数n是一个奇数等差数列:1
2
3
4
5第i级,ret表示n在这一级内,则最终的结果
cnt表示有多少个n是对应ret这个结果的,也就是n的范围
关系如下:
2*ret+1 = cnt
=> ret = (cnt-1)/2
现在我们只要求出这个范围cnt就行了。由于k对应的最后的结果,而v对应n,表明我们需要判断这个n落在哪一级内,
也就是将v加起来,直到加到第i级超过n的值,那么n就是在这一级内。
基于这种思想有代码:1
2
3
4
5
6
7
8
9
10int magic(int n){
if(n<=0){
return 0;
}
int i,cnt=0;
for(i=3;cnt<n;i+=2){ //第i级所包含的原始开关数范围,cnt统计n之前的数量
cnt+=i;
}
return (i-3)>>1;//因为数列没有cnt=1对应的ret
}
改进之后算法复杂度为O(n/2)=>O(n),现在上传改进的代码,这次通过了,耗时0s,但是在分布图里,
居然只打败了同样用C的6.83%,看来很多人结果比我好啊,也不知道
它的测试数字是不是都是一样的。
看一下能不能继续改进。对了,奇数等差数列不是有求和公式吗?我们知道n,直接求它在那一级的范围
内,不就是ret的值么?避免了一个个去加然后判断,这就是最终版本的方法。
因为奇数2n-1的求和公式:1
2
3
4
5
6
7
8对于上面的关系有:
sum = (2*ret+1+3)*ret/2
=> = ret*ret + 2*ret
所以反推有:
-> n <= ret*ret
=> ret = sqrt(n) 向下取整
这种解法C函数表示就是floor(sqrt(n))
所以改进的代码:1
2
3
4
5
6int final_magic(int n){
if(n<=0){
return 0;
}
return floor(sqrt(n));//隐式转型
}
这样简单多了。然后上传leetcode测试结果,35个测试全部通过,
耗时0ms,居然还是打败6.88%,郁闷
为了测试的方便,我将代码改写成go,然后使用自带test,之前2方案
比较快,所以以它为基准测试其他2种正确性。开始的时候都是手写的测试
case,用map去对比,本想设置测试上限10000000,但是超过了10分钟,出
现了panic,10万以上第一种方法就很慢了。
测试代码部分:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const(
MAX = 10000 //测试范围
)
//通用测试方法,传入测试函数参数
func test(t *testing.T,Func func(int) int){
for i:=0;i<=MAX;i++{
if Func(i)!=Magic(i){
//结果出错
t.Errorf("%3d:%3d calc failed.",i,Func(i))
}
}
}
TestCommon(t){
test(t,Common)
}
TestFinalMagic(t)
性能测试,代码类似:1
2
3
4
5
6
7
8
9
10//通用性能测试方法
func bench(b *testing.B,Func func(int) int){
for i:=0;i<=MAX;i++{
Func(i)
}
}
BenchmarkCommon(b)
BenchmarkMagic(b)
BenchmarFinalkMagic(b)
测试结果:
第一种最符合程序员解决问题的思维,也很容易理解。后面的方法已经脱离了问题,变成了纯粹的数学问题
无奈第一种性能要差得多。这大概就是提醒我们,抽象生活问题成数学问题,然后去解决它吧。很惭愧,我
没有通过分析问题去提炼其中的数学规律,而是凭感觉试出来的,
最后,学到的是go中的函数参数了,顺便复习了C语言的一些内容。
There are n bulbs that are initially off. You first ]]>
昨天完成了nodejs版本的图片服务器staticserv
之后,当时没有对请求的类型做判断,这只是我自己使用,如果是做成通用模块,需要考虑防范不合适的请求。之前,
首先考虑的是Go版本的服务器,只不过自己没有弄清go的资源地址都是相对于项目的GOPATH的地址,还是使用老方法\a\b\c.txt
去读取文件,结果都无法读到文件,所以写了nodejs版本。今天学习了path/filepath
模块之后,终于掌握
了正确写法。比如要读取”./a/b.txt”,需要这么写:1
2absPath:_:=filepath.Abs("./a/b.txt")
bytes,err:=ioutil.ReadFile(absPath)
flag
模块解析src参数来实现,比如1 | ./serv -src /tmp |
测试中对于”//“这种错误也能兼容,因为它使用的是Join转化路径,而不是拼接字符串。
关键代码如下:1
2relativePath := req.URL.Path
filename, err := filepath.Abs(filepath.Join(*curDir, relativePath))
1 | //忽略favicon请求: |
路径是否正确直接判断上文中的err就好了,这里就不表述。实验时发现请求的是目录,服务
器立马输出错误,然后退出了。这怎么行,一点都不健壮,所以要判断是不是文件:1
2
3
4
5
6
7
8
9
10
11
12fileinfo, e := os.Stat(filename)
//文件不存在
if os.IsNotExist(e) {
log.Println(filename, " is not exists.")
res.WriteHeader(404)
return
}
//不是文件,返回500错误
if fileinfo.IsDir() {
res.WriteHeader(500)
return
}
content-type
,对于不是图片的请求,直接返回了text/plain
:1 | extname := filepath.Ext(reqfile) |
发送数据就比较简单了,为了不在传递参数时拷贝,我用了拷贝指针的办法:1
2
3
4
5var data []byte
//读取文件
ReadFileFromPath(&data, filename)
res.WriteHeader(200)
res.Write(data)
这里主要用到了apache的ab工具,以及开源软件webbench。
两者都在同一环境下,均没有做优化。
1 | ab -c 200 -n 4000 http://localhost:8080/fact10000.png |
测试结果如下:
go 版本:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Document Path: /img/fact10000.png
Document Length: 27488 bytes
Concurrency Level: 200
Time taken for tests: 2.493 seconds
Complete requests: 4000
Failed requests: 0
Total transferred: 110276000 bytes
HTML transferred: 109952000 bytes
Requests per second: 1604.80 [#/sec] (mean)
Time per request: 124.626 [ms] (mean)
Time per request: 0.623 [ms] (mean, across all concurrent requests)
Transfer rate: 43205.78 [Kbytes/sec] received //网络传输速度,排除网路问题
/*网络上消耗的时间的分解*/
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 4 39.6 0 1003
Processing: 3 117 143.3 56 1184
Waiting: 2 65 41.9 50 173
Total: 4 121 147.3 57 1184
*下面的内容为整个场景中所有请求的响应情况。在场景中每个请求都有一个响应时间,
其中 50% 的用户响应时间小于 57 毫秒,66 % 的用户响应时间小于 100 毫秒,
最大的响应时间小于 1184 毫秒*/
Percentage of the requests served within a certain time (ms)
50% 57
66% 109
75% 150
80% 157
90% 290
95% 406
98% 583
99% 739
100% 1184 (longest request)
nodejs版本:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28Concurrency Level: 200
Time taken for tests: 3.019 seconds
Complete requests: 4000
Failed requests: 0
Total transferred: 110352000 bytes
HTML transferred: 109952000 bytes
Requests per second: 1325.07 [#/sec] (mean)
Time per request: 150.935 [ms] (mean)
Time per request: 0.755 [ms] (mean, across all concurrent requests)
Transfer rate: 35699.34 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 24 150.8 0 1003
Processing: 67 119 50.4 103 420
Waiting: 67 118 50.3 103 420
Total: 71 143 156.3 105 1116
Percentage of the requests served within a certain time (ms)
50% 105
66% 125
75% 141
80% 145
90% 160
95% 303
98% 1093
99% 1099
100% 1116 (longest request)
二者情况是差不多的,go略微好于nodejs。
1 | Requests per second: 1604.80 [#/sec] (mean) //吞吐率 |
nodejs:1
2
3
4
5
6
7
8
9
10
11Requests per second: 1325.07 [#/sec] (mean)
Time per request: 150.935 [ms] (mean)
Time per request: 0.755 [ms] (mean, across all concurrent requests)
Requests per second: 1156.69 [#/sec] (mean)
Time per request: 172.908 [ms] (mean)
Time per request: 0.865 [ms] (mean, across all concurrent requests)
Requests per second: 1231.65 [#/sec] (mean)
Time per request: 162.383 [ms] (mean)
Time per request: 0.812 [ms] (mean, across all concurrent requests)
可以看出二者在吞吐率上,Go好于nodejs,平均等待时间上二者相差无几。
1 | webbench -t 30 -c 200 http://127.0.0.1:8080/img/fact10000.png |
go的表现:1
2
3
4
5
6
7
8
9Speed=111574 pages/min, 51317124 bytes/sec. //速度,111574页/每分钟,51317124字节/秒
Speed=100538 pages/min, 46228920 bytes/sec.
Speed=103186 pages/min, 47440784 bytes/sec.
资源占用:
VIRT RES SHR CPU MEM TIME
364756 26252 4336 S 61.9 0.0 0:15.26 serv
364884 25960 4044 S 58.7 0.0
364884 25960 4044 R 57.9 0.0
nodejs的表现:1
2
3
4
5
6
7
8
9Speed=78864 pages/min, 36259112 bytes/sec.
Speed=42868 pages/min, 19710708 bytes/sec.
Speed=36340 pages/min, 16708262 bytes/sec.
资源占用:
VIRT RES SHR CPU MEM TIME
955320 74196 14252 R 73.6 0.1 0:10.49 node
960768 79408 14252 R 33.2 0.1
961604 79256 14252 R 34.7 0.1
二者都是0错误,可以看出nodejs在速度和资源占用率都比Go表现的差,
速度大概只有go的一半不到,内存占用率却是go的3倍左右,CPU使用率
稍好于go。
从测试里可以看到,Go的http性能是好于nodejs的,不过对于这么
简单的应用,可能无法完全反应Go和nodejs的http性能高低吧,但是go的
http的模块我觉得很好用,写代码也是同步方式,不像nodejs我写到逻辑越来
越复杂的时候就有点越发要好好学一学异步编程的思想,这种思维转换一时还
无法适应,所以我往往选择javascript先完成一部分功能,打开思路后使用Go
去实现。
另外今天的收获就是学会了做网络测试了,这对我来说又增长了能力。
这几天博客搭建起来后,美化和主题也设置得差不多了,就是在写作时,
有时候需要插入图片,图片使用的相对地址会在hexo生成博客时失效,总不
可能插入一个图片就在它生成后去相应的html里改引用地址吧,再说上传的时
候Hexo可能默认不会上传。
所以这种情况下,就需要一个文件服务器去为我们生成的博客传输图片。
七牛的静态文件服务听说性能很高,也是用我最近学习的golang写的。不少
人都是推荐这种服务,所以我就去七牛官网看了一下,有免费试用版,每个月
请求一万次到十万次,对我来说够用了。想注册的时候有以下两点要求我比较介意:
国内的服务很多都把个人信息卖了,导致我总是收到垃圾短信和推销电话,因此现在就奉行
能不注册就不注册的态度。另外,我写个博客还要去七牛上上传,两边还得对上号,再说自己的
需求就是插入静态内容,也不复杂,为什么不自己做呢?自己动手,丰衣足食,还可以后续改进。
所以有了下面的做法。
当浏览到包含插入图片的博客时,相当于发起了一次http请求,如果返回的是200,那就是说明
取到了图片,所以我们文件服务这边就是写一个http服务,监听这些请求,发送图片的数据过去就
行了。具体逻辑如下:
本来先写Go版本的,结果测试时,go中读取文件的”ioutil”和”os”模块都不能读取多层目录下的
文件内容,为了尽快上线,我用了nodejs,里面的http模块也很好用,异步不需要等待的特点正好适合
I/O操作,前期也没有考虑多个请求就开多个操作,或者是否Javascript异步模型不需要这些东西来提
高性能。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32var http = require('http');
var fs = require('fs');
var url = require('url');
var path = require('path');
/**
* 访问根目录在当前目录,
* 例如要访问img下的fact.png,就使用http://[云地址]:8080/img/fact.png
*
**/
http.createServer(function(req,res){
var rootPath = ".";
//屏蔽favicon请求
if(req.url!=='/favicon.ico'){
var filename = rootPath+req.url;
var img_type = path.extname(req.url).split('.')[1];
//读取相应的文件
fs.readFile(filename,'binary',function(err,file){
if(err){
res.writeHead(500,{"Content-Type":"text/plain"});
res.write(err+'\n');
console.log('request:',req.url,'\t\t[Failed].');
res.end();
}else{//返回数据
res.writeHead(200,{"Content-Type":"image/"+img_type});
res.write(file,"binary");
console.log('request:',req.url,'\t\t[Ok].');
res.end();//等待发送完成
}
});
}
}).listen(8080);
然后将这段代码发在云上运行,并将博客里要插入的图片放在rootPath下,更新
博客后,再访问服务端果然出现了request ok的信息,gitcafe和github上的博客在浏
览时,也成功加载了图片。说明我的想法是正确的。
服务器写的还是很简陋的,总的来说有以下需要改进:
学校的报告一个接一个,等一个个忙完想休息一下,结果导师说我之前给他的报告不行,
不是要搜集论文,而是要找美国的存储实验室。1个月10到20个,还要有介绍,这就必须
要搜到实验室之后仔细看别人的实验室情况,有点大海捞针的意思。每天一来实验室面对
这些,春节过后还要找实习,必须要复习一些东西,另外之前的计划工作也需要完成,心态
就越发变得急躁了。
从15年12月初开始就忙碌了起来,当时要考数学和英语,另外还有学院的一个项目,写报告,
复习,确定每天的工作计划,感觉事情就像流水线一样过来,必须要确定轻重缓急,看过很多书籍
和电影说把每天的工作列成一个清单,按重要程度分为20%和80%,完成那20%重要的事情。这样
才是有效率,不会忙了一天还不知道忙的什么。当时就觉得这个方案很有必要,开始每天在桌面上
写清单了,做完一个标记完成。可是时间一长,桌面图标多得惨不忍睹,也不知道具体一项任务写到
哪里去了,还是有点乱糟糟的。所以萌生了统一写博客里,还能不随时携带就可以查阅。
准备:
截止日期:2016-01-30 :待定
时间:2016-01-10 ~ 2016-03
起始:2016-02-01 ~ 2016-03
先想到这些,就暂时记这么多吧,完成一件会标记一项。按照事情紧迫性一件件地去落实,
相信最终必然有收获!
学校的报告一个接]]>
根据我这两天搭建的经历,写下详细的帮助教程,总结自己的经验,也是为了避免后来者犯错。
注意:现在hexo都是3.0以上版本,使用方法与之前的2.0版本有所不同,这也是我被坑的地方,需要用新的方法.
1 | 前提工具:git nodejs npm ssh服务 |
我安装在免费的云中,这样换个地方,换台电脑也能有环境编辑上传文件,安装在本地需要额外的步骤。
国内免费的有代码市场,反正也就一个编辑上传的地方。当然也可以直接使用
云的服务建立博客,这种最简单。
1 | mkdir <your project> //博客项目文件夹 |
此时就可以新建博客了
1 | hexo new page '<blog title>' //会在/source/_posts/生成相应的文件文件夹 |
或者直接发布到_post:1
hexo new <blog name>
1 | hexo g && hexo s //生成博客,并开始启动服务 |
此时访问云分配给你的http://域名:4000/即可看到你的博客
Settings
中选择开启Github Pages
由于之前安装了hexo-deploy-git,因此只要配置git config即可
1 | git config --global user.name "your github name" |
修改本地站点下的/_config.xml,找到deploy:
1 | deploy: |
注意:这里不是网上所说的那种配置方式:1
2
3
4deploy:
type: github
repo: git@github.com:luoyibu/luoyibu.github.io.git
branch: master
当然,这种方式也可以,不过一次发布到2个及以上的托管服务会有问题,所以还是
使用新方法吧!
1 | 发布之前,最好清理本地环境: |
由于github在国外,国内访问不稳定,可以使用国内服务gitcafe,操作步骤也差不多。
这里提一下提前准备的东西。
git config
就是一样的,省去在~/.ssh下生成ssh密钥:
1 | ssh-keygen -t rsa -C "邮箱地址" |
copy id_rsa.pub中内容到github和gitcafe SSH
公钥管理中
1 | deploy: |
这里和网上教程介绍的也不一样,不需要brach: gitcafe-pages也能成功。
之后发布操作和github发布相同,就不再赘述。
github访问不稳定,gitcafe又快,为了国际化,能不能两个都发布呢?相信很多人和我
的想法一样,我找了很多资料,就连写makefile的都有,现在新版,同时发布已经很方便了。
网上大部分的是:1
2
3
4
5
6
7
8修改本地站点下的*/_config.xml*,修改*deploy*:
deploy:
type: git
message: [message]
repo:
github: <repository url>,master
gitcafe: <repository url>,gitcafe-pages
这种在新版中经过我测试是不行的,github 的master分支一直出错,只有gitcage能成功。
新版方法1
2
3
4
5
6deploy:
type: git
message: "update blog."
repo:
github: git@github.com:your-name/your-name.github.io.git
gitcafe: git@gitcafe.com:your-name/your-name.git
不需要设置brach,两个发布都会成功。
现在记录下来以帮助那些还在惨白主题中苦苦挣扎的程序员吧!
1 | theme_color: |
重新部署博客就可以看到新主题生效了,再也不用盯着惨白色看博客。
1 | 删除仓库里面 source/_posts/我的文章.md |
1 | hexo new page "<category name>" |
在生成的相应md中修改如下:1
2
3title: <文章标题>
date: 2016-01-09 10:56:27
type: "<category name>"
修改本地layour下的/themes/1
2
3
4menu:
main: /
archives: /archives
category: /<category name>
直接在新建的md文件中添加1
tags: <your tag name>
修改本地layour下的/themes/1
tags: /<your tag name>
修改1
2post.md ==> 对应hexo new 生成的md模板
page.md ==> 对应hexo new page生成的categories模板
可以在其中加上categories:
模板,这样就不用每次手动添加了。
例如:1
2
3
4title: {{ title }}
author: {{ your-name }} //添加的模板
date: {{ date }}
tags:
今天通过这种方法,我在github和gitcafe上的博客
都正常运行了,也是经经历一番排错和查找问题的经历。
其实有些东西自己摸索出来还是很有成就感的,印象也更深,即使是别人早已知道的。
希望给想建立自己博客的人一点帮助。后续遇到的一些问题都会在博客上更新,
欢迎关注我!
每当一种语言诞生,世界上的争吵就多了一种声音。经常可见这样的讨论:1
2
3
4"xxx就不错,yyy是垃圾,这都不支持!"
"xxx太复杂了,不适合如今的应用模式。"
"xxx是最好的语言。"
....
我大学期间也自学很多语言,主要是想做个东西出来,看别人推荐什么语言,有什么现成的库,
就开始了入坑之旅。
Java写得冗长,框架笨重又得配置xml,但是一旦项目规模上来了,
它却是协作下表现最不差的解决方案。因此现在使用Java最广泛。
毕竟水平一般不知道控制代码质量和内存使用的人太多。
C没有类型系统,我看过一些为C添加的对象系统,使用宏如同魔法
一样,但是这种法案离C写基础构件的初衷还是很远,不是经历过正规
训练的人很难习惯和掌握什么都需要自己创造的环境。另外C的字符串
处理太弱,由于历史原因不支持UTF-8,这就在今天大部分的字符串处理
逻辑里带来不便。
C++在C的基础上添加了很多语法,字符串处理同样比较差,多种范式,
怎么写都有办法达到目的,写起来最不容易控制复杂性,或者是在中途为
了追求优雅高效而忘记了自己究竟要干什么。项目不是写到一半被抛弃,
或者重构的时候干脆从头开始造轮子了。
还有重要的一点是,C++的Bug最难找,由内存处置不当导致的错误层出
不穷,如果使用了高级特性如模板、迭代器、算子之后一旦出错错误信息
如果天书一样在屏幕上滚过,而你的一天就这样过去了。
我见过很多C++的拥趸说C++是最强的语言,什么都能做还能做得很好,
这点确实如此,但是这么一件强大的武器需要非常熟练它的人才能发挥出它
的功效,而不是写过只有几百行代码就莫名喜欢的人所认为的那样,也不是
指针不释放,内存到处申请的人就能自称熟练使用C++。
python写出的程序直观,最适合初期快速实现自己的想法。但是多线程GIL
的原因,重构社么的也麻烦,因为写的时候太自由了,同样不能充当主力,
而是作为干杂活的最佳选择,写出1.0版最快。
javascript异军突起,google的V8引擎功不可没,现在进本上基本上所有智
能设备都可以支持javascript,这就有了广泛的市场。但是它的异步特性使得
我们长期受到一步一步操作的思路影响的大脑有点难以接受,常常困惑于为什
么没有取到数据,那一层层的函数包裹搞得人连函数边界在哪都不知道。这就
限制了一般程序员的使用,对象和继承出现的问题也比较奇怪。总的来说在web
前后端还是不错的,一致的语言带来的是连贯的思路,思路清晰又往往可以保
证项目完工,半路失败的项目太多了。
PHP也略微了解一些,在web领域也是非Java就PHP了,简单易于掌握的才是最
好的,不管是个人开发者还是团队来说。最近准备了解Laravel框架,思路和
express一样,也是路由、控制器、模型,避免了之前写php的随心所欲,就像写
网页的php里面有连接数据库。其他领域基本就没有phph用武之地了。
golang语言2007才出现,其特点是足够简单,还有很好的一点是静态编译,没有
依赖问题。我之前认为动态编译的程序,内存中只有一份,节省内存,程度加载的也快,
静态编译不好,慢不说程序占用空间还大。但是今天IT行业已经变了,到处都是云环境,
CPU、内存、硬盘太多了,反而生产环境变得复杂了,一不小心某个依赖变了,应用就有
可能出问题,静态编译却没有这个主要困扰开发者的问题。用下来感觉挺不错的。缺点是
没有泛型,写通用操作不方便,传个interface{}感觉怪怪的,它的特色goroutine我用的
还不是很多,国内使用的案例也比较少,可能没有公开或者别人还在观望。也只有以后再
补充自己使用的感受了。强制统一的规范比如大写方法可以导出,用习惯了自觉地在其他
语言里也是用这种规范,而规范减少了出bug的可能。自带的测试命令也使得我写单元测试
保证正确性,以前写代码就是弄一两个数据测一下就算完事,而这种单元测试经常能发现边
界情况的错误,还有性能测试、代码覆盖度测试都可以保证代码是工程级而不是玩票性质。
最近就在写golang的常用的数据结构和算法库,也算是轮子吧,可以关注utils
获取最新进展。
由于我接触的东西还不是很复杂,所以自己的这些看法难免会有一些错误的地方,之后随着阅
历的增加,感受自然会多起来。随着代码量的增长,我越来越觉得每种语言都有适合运用的场
景和劣势的地方,并不存在统一的万能解决方案,也不是以个人情绪来判断好坏。
我倾向的一种工作方式是,先用动态语言快速实现自己最重要的目标,之后可以将关键部分慢
慢升级换成Java这样的工业成熟方案,性能要求高的地方c/c++改写,让它们成为为应用提供服务的
组件。上层用户使用的地方javascript做初步操作,或者充当业务初步处理层。分布式的场景,
golang的通讯交换消息方式也有erlang先行者,性能非常出色。
每当一种语言诞生,世界上的争吵就多了一种声音。经]]>