??xml version="1.0" encoding="utf-8" standalone="yes"?> Golang的热更新采用什么机? 使用go1.8提供的plugin包机制实?/p> plugin包本w设计的目的是热更新? plugin包其实只是支持将代码分别~译为多个动态库,动态加载后q行 q不能完全支持类似C/C++的动态库方式处理代码 带状态的q程热更新的基本概念及范围是什? 数据部分(model)不更? 只更新逻辑部分(函数) 表格和配|更新算热更C? ? 但不是本文描q范?/p> 热更新能在windows上用么? 不支?/p> 我能原来一个exe的代码编译ؓso提供lplugin使用? 可以, 但是必须仍然保留main包作为插件入? q在main包内d提供lplugin调用入口. 如果动态库中没有main? ~译出的so能用? 不能, 包必d含main, 否则输出的是.a的文? plugin包加载会报错 动态库? 非main包的的代码修改能做热更新? 不能!(崩溃了吧, 我提了一个issue: https://github.com/golang/go/issues/20554) 如果实做了修改, 底层会报? plugin was built with a different version of package 解决Ҏ: 修改plugin包底层实现ƈ重新~译 打开runtime/plugin.go, 注释以下代码 for _, pkghash := range md.pkghashes { if pkghash.linktimehash != *pkghash.runtimehash { return "", nil, pkghash.modulename } } 执行/usr/local/go/run.bash 重编?试 代码中哪些可以被更新? Ҏ可以被更C? 闭包? 只能更新拥有静态地址的结?例如: 包别函?cM于静态函? 例如: svc_0.so里有一个Foo函数, svc_1.so修改了Foo函数实现, 热更新可实现 闭包=函数+捕获变量, 实际上是一个动态结? 没有静态地址, 无法被更?/p> 各种包别变? l构体变? l构体方? 局部变量均不能被热更新, 但是变量g会被影响 新增l构可以被运?/p> 使用l构体方法调用了包别函? 包别函数能被更C? 可以, 虽然Ҏ不能被更? 但方法被调用的包U别函数的地址是固定的, 所以可以被热更?/p> init包初始化函数能用? 能被热更C? 官方q样描述: 插gW一ơ被打开? 其关联的, 没有成ؓE序的一部分的所有的包的init函数被调用. 插g的main函数不会被调? 插g只会被初始化一? 不能被关?/p> 因此, 需要手动将init函数Ҏ自己的函? l一在so的main包里调用 如何~译获得plugin包支持的动态库 -buildmode=plugin是重要参?/p> --ldflags里的-pluginpath的作用是: 每次~译的内部识别\径都是不同的, 避免重复加蝲的警?/p> 怽技术大规模出现在魔兽世界WLK版本, 现在应用已经q泛应用在各UMMORPG游戏? 下面对相位技术的做法q行单归Ux?/p> 早期副本的出? 避免抢怪问? 所? 副本其实本n是一U相位技? 只不q实现时, 我们一般会小队和怪物直接预分配在独立的一个副本实例中(所以副本原文也是实例的意? 怽技术还没有正式命名? 同一个场? 玩家q到不同的分U看到的玩家不一? 也是属于怽的一U? 当然, 如果是组队玩? 服务器默认会分配所有队伍玩家在同一个线(位面) 副本怽和分U相位其实都是静态相? 一旦进? 中途不会有切换或者合查看的q程. 真相位可以在一个场景中,动态切换相? 怽内和怽外所以不?/p> 我们常见的真怽表现? 怽中的角色+玩家+队伍成员 在护送Q务时, q会在上面所见角色中叠加世界中的所有角? 也就是说, 你和队伍成员可以看到的角? 其他人看不到, 其他Z看不C和你的队伍成?/p> Z清晰的简单的描述, 我ؓ怽创徏如下概念与名?/p> 表现为除玩家外的角色(怪物,交互物体与相位可见场? q是最常见的一U相位内角色 持有变量 取PhasingID?为PhasingTargetID 生成规则 当玩家开启相位后, 在玩家相位内生成的角?/span>为私有客? 此时, ?PhasingTargetID讄为相位生成者的实例ID 删除规则 如果玩家退出相? U有客体会存在一D|间或按照需求删?/p> 一般指提前攄在场景中, 世界内不可见, 但是能被同相位玩家可?且同怽玩家都可以互相可?比如: 只要接了同一个Q务的玩家, 都可以看到的NPC 持有变量 取PhasingID?为PublicPhasingID 生成规则 通过场景~辑? 攄角色? 讄其可被观察到的Q务ID 角色被加载后, Q务ID讄到StaticPhasingID 删除规则 场景删除, 角色删除 包含玩家与同队伍玩家 开启相位后, 可见U有客体+公有客体 队长视ؓ怽M, 单h? 自己为队?/p> 队伍其他成员׃n队长的私有相位客?/p> 队伍其他成员Ҏ自己的PublicPhasingID匚w目标对象的PublicPhasingID时可互相可见 持有变量 怽开启时, 取PhasingID? 色实例ID 怽关闭? 取PhasingID? ? PublicPhasingID 当两个角色的PhasingID相等? M与私有客体互相可?/p> 当两个角色的PublicPhasingID相等? M与公有客体互相可?/p> 可以通过开兌|? 是否在可见的怽客体基础? 叠加世界角色(护送Q? 我们对Golang的结构体变量赋? 以及单参数函数调用进行反和native操作的测?pre> === RUN TestNativeAssign 各位看官必须吐槽用for来遍历获取数? 但冷静下来分? q样做无可厚? 开发效率:Windows下可以通过VisualStudioq行开发,其他q_可以使用MonoDevelopQ非常方?/p> q行效率QJIT的性能优化比较CQ能适应90%性能环境 部v便捷性:可以通过交叉~译生成其他q_的可执行文gQ通过monoq行可执行文?/p> 调试便捷性:VisualStudio和MonoDevelop调试均很方便Q?q可q程调试 上手度:对Cp语a熟悉的几天就可上?/p> 热更斎ͼ可以通过DLL方式q行 WebҎQ可做,代码比较啰嗦 崩溃处理Q可通过try catch捕获错误 |络库编写难度:一般,需注意gc问题 W三方网l库及框架数量:一?/p> 开发效率:?/p> q行效率Qƈ发上非常有优势,对CPU利用率比较高Q原生运行无虚拟?/p> 部v便捷性:一ơ编译到处运行,无Q何运行库依赖 调试便捷性:实际操作中,单线E挂接调试器可行Q?但变量显CZ正确Q开发期基本采用日志方式q行查错 上手度:语言单,Ҏ少Q?新手1周能贡献代码 热更斎ͼ无法q行热更斎ͼ语言无法~译为DLLQ也不支持DLL加蝲Qlinuxq_?so加蝲忽略不计Q?/p> WebҎQ非常方便, 代码_ 崩溃处理Q崩溃后以命令行方式打印出栈Q程序内可以捕获M崩溃错误ql运?/p> |络库编写难度:单,比C socket更简?/p> W三方网l库及框架数量:偏少 开发效率:Z动态语a的开发初ơ写比较快,后期l护和重构会耗费一定的旉在查错上 q行效率Q基于lua jit的运行效率还是能接受?/p> 部v便捷性:方便Q?只有底层修改需要重新编译, 大部分时间只用更新lua文g 调试便捷性:不是很方便,Z日志方式q行查错 上手度:lua语言Ҏ有部分和Cp语a有一定差异,ZActor模型的思想学习Q适应需要耗费一定的旉 热更斎ͼcM于ErlangQ可_到函数的热更新 WebҎQ有一些http支持Q通过C慢慢q行完善 崩溃处理Qlua天生可以捕获错误 |络库编写难度:自带Q无需~写 W三方网l库及框架数量:通过C慢慢完善 开发效率:~译慢,文g多,通用库少 q行效率Qnative速度标杆 部v便捷性:~写各类的make门槛较高 调试便捷性:可通过VisualStudioq行Windowsq_调试 上手度:2~3q经验的熟手仍然会写出崩溃和泄露代码 热更斎ͼ可通过DLLq行 WebҎQ代码啰嗦,W三方库?/p> 崩溃处理QWindows下可使用SEH捕获D异常,其他q_只能通过崩溃后进行coredump分析Q容错非常差 |络库编写难度:Zasio~写较ؓ单,但M看来隑ֺ不低 W三方网l库及框架数量:较多 以下是得?/p> 从发文时的项目对q些语言使用率来_JavaQErlangQC++~写的服务器较多QGolangQJavaScriptQC#是第二梯队,Skynet׃上手不是很容易,所以仅有两位数的团队在使用Q但M表现q是比较? 对于老团队, C++的服务器工具铑֒框架已经相对成熟Q?完全没必要更换新语言Q?只是在对接sdk感觉困难Ӟ可以试Golangq些对web有优势的语言q行混合语言开? 对于新团队,开发效率,上手度和部v效率是优先选择的,C#QGolangQJavaScriptq些新兴语言会让你事半功? 对于大规模无需选服的服务器Q?Skynet的actor模型Ҏ展会比较Ҏ 对于大公司,好项目,上线后需要通过热更新进行bug修补的,C#QC++QErlang会是首? 但ȝ一点, q是Ҏ团队熟悉度来选择语言QN然的使用新语a的风险也是很大的
]]>
]]>基本概念
代码及结?/h1>
When a plugin is first opened, the init functions of all packages not already part of the program are called. The main function is not run. A plugin is only initialized once, and cannot be closed
~译
SVCNAME=$1 SVCVER=$2 TIMESTAMP=`date '+%Y%m%d_%H%M%S'` go build -v -buildmode=plugin --ldflags="-pluginpath=${SVCNAME}_${TIMESTAMP}" -o ${SVCNAME}_${SVCVER}.so ${SVCNAME}
]]>表现分类
副本怽
分线怽
真相?/h3>
怽客体
U有客体
公共客体
怽M
可见规则
U束
]]>
package main
import (
"reflect"
"testing"
)
type data struct {
Hp int
}
const AssignTimes = 100000000
func TestNativeAssign(t *testing.T) {
v := data{Hp: 2}
for i := 0; i < AssignTimes; i++ {
v.Hp = 3
}
}
func TestReflectAssign(t *testing.T) {
v := data{Hp: 2}
vv := reflect.ValueOf(&v).Elem()
f := vv.FieldByName("Hp")
for i := 0; i < AssignTimes; i++ {
f.SetInt(3)
}
}
func TestReflectFindFieldAndAssign(t *testing.T) {
v := data{Hp: 2}
vv := reflect.ValueOf(&v).Elem()
for i := 0; i < AssignTimes; i++ {
vv.FieldByName("Hp").SetInt(3)
}
}
func foo(v int) {
}
const CallTimes = 100000000
func TestNativeCall(t *testing.T) {
for i := 0; i < CallTimes; i++ {
foo(i)
}
}
func TestReflectCall(t *testing.T) {
v := reflect.ValueOf(foo)
for i := 0; i < CallTimes; i++ {
v.Call([]reflect.Value{reflect.ValueOf(2)})
}
}
性能试数据
?PASS: TestNativeAssign (0.03s)
=== RUN TestReflectAssign
?PASS: TestReflectAssign (0.41s)
=== RUN TestReflectFindFieldAndAssign
?PASS: TestReflectFindFieldAndAssign (9.86s)
=== RUN TestNativeCall
?PASS: TestNativeCall (0.03s)
=== RUN TestReflectCall
?PASS: TestReflectCall (21.46s)
试评测
// FieldByName returns the struct field with the given name
// and a boolean to indicate if the field was found.
func (t *structType) FieldByName(name string) (f StructField, present bool) {
// Quick check for top-level name, or struct without anonymous fields.
hasAnon := false
if name != "" {
for i := range t.fields {
tf := &t.fields[i]
if tf.name == nil {
hasAnon = true
continue
}
if *tf.name == name {
return t.Field(i), true
}
}
}
if !hasAnon {
return
}
return t.FieldByNameFunc(func(s string) bool { return s == name })
}
试想如果reflect包在我们使用ValueOf时用map~冲好一个结构体所有字D늚讉K数据? 肯定讉K指定字段速度会很?br>但是, 以空间换速度的需求其实最多满了1%的需?
同样的例子是囑ŞAPI里访问Shader变量的方? L默认使用字符串获? 速度很慢. 当你惛_速访问时, h前按需~存字段
那么, Golang使用的也是这L思\. 虽然暴力了一? 但是能够让程序跑? 性能优化的东西放在之后来? ~冲下就可以解决
因此, 我们在^时用反时, 量偏向于反变量缓冲存在下的变量赋值或者获?br>而调用的需求尽量减? 如果有goroutine存在的情况下, 则不必太多担?
]]>C#
Golang
Skynet(lua+C)
C++
func server() {
pipe := cellnet.NewEventPipe()
evq := socket.NewAcceptor(pipe).Start("127.0.0.1:7234")
socket.RegisterSessionMessage(evq, coredef.TestEchoACK{}, func(content interface{}, ses cellnet.Session) {
msg := content.(*coredef.TestEchoACK)
log.Println("server recv:", msg.String())
ses.Send(&coredef.TestEchoACK{
Content: proto.String(msg.String()),
})
})
pipe.Start()
}
func client() {
pipe := cellnet.NewEventPipe()
evq := socket.NewConnector(pipe).Start("127.0.0.1:7234")
socket.RegisterSessionMessage(evq, coredef.TestEchoACK{}, func(content interface{}, ses cellnet.Session) {
msg := content.(*coredef.TestEchoACK)
log.Println("client recv:", msg.String())
})
socket.RegisterSessionMessage(evq, coredef.SessionConnected{}, func(content interface{}, ses cellnet.Session) {
ses.Send(&coredef.TestEchoACK{
Content: proto.String("hello"),
})
})
pipe.Start()
}
目地址: https://github.com/davyxu/cellnet
首先我们来看?/p>
假设A要把内容传输lB
1. B生成RSA的公钥和密钥, q是成对出现? 密钥由B保存, 把公钥告诉A
2. A用B的公钥加密内? q把密文内容传输lB
3. B用密钥解?/p>
证明某个内容是你发的, 而不是被别h冒名替, 例如git的push中就带有q个功能
假设A有内? B要验证内容确实由A发出
1. A生成公钥和密?/p>
2. A内容做一个hash, 把hash码用自己的密钥加密ƈ把这D密文发lB
3. B用A的公钥对密文q行验证, 卛_认密文是否由A发出
可以看出, 两种用法都是典型的非对称用法
但PP助手却干了g奇的事?
在PP SDK官方文档? 我们扑ֈ了PHP语言的验证方? Ҏ里用了q样一个API
从官Ҏ档看得出q个使用openssl的算法库
cM? q有Java, C++, Python语言的处理方?/p>
其中, C++也是用的openssl, Python则是需要预~译C库,在Ubuntu下需要手工patch M2Crypto的_ssl.c文g.
先不说这些非正规的编?patchҎ会造成多大的问? 单就q个用公钥解密就很蛋?/p>
从之前的RSA法中了? 只有对公钥进行验证的Ҏ, 也就是只能得到是q是不是的结? 但PP的SDK则要求必ȝ公钥解密?/p>
解出的数据ؓ一Djson, 以对比是否有订单改.
那么q种做法q效于, 用最单的异或+一个公钥进行订单加? 然后同样用这个公钥进行解?/p>
只不q用RSA感觉很高U?/p>
q种做法一旦公钥在PP助手服务器或者玩家的开发服务器, 甚至源代码泄? 那么马上有很大的伪造订单的危险
我把q个做法发给朋友? 他们? 其实PP助手的开发者只用了RSA, 跟传输不是明文就好了, 至于什么信息安? 都是?
在我们的游戏目中用的golang服务器开发方式如?/p>
1.多线E逻辑
2.同步d. 也就是说, 每个Z个线E?goroutine), ioU程=逻辑U程
q种方式的优?
1. 同步d方式与h的思维方式cd
2. 逻辑处理性能有一定提?/p>
在大规模使用q种模式~写逻辑? 我们发现了这U模式只?个缺? ~写者需要处理多U程关系
但这本n实直接致命? 回想C++时代, 多线E处理时, 调试重现的困䏀?脑补景象太惨不敢直视
在C++时代, 我曾l编写过一套asio的C++服务器框? 采用io多线E? 逻辑单线E? 依赖着C++高性能的优? 让开发便L单且无需兛_U程问题.
那么Cgolang时代, Z么不能试下单U程异步多进E方式来~写逻辑?
与多U程同步dҎ? 我们发现, 两者优~点互补. 那这回C领域选型问题? 对于游戏服务器需要的上手? 开发便? 压力降低(非MMO)q些特点来说, 单线E异步多q程再合适不q了
那么, 我们在用golang~写单线E异步多q程服务器应该注意哪些点?
1. socket处理完全装, 只通过channel抛出到逻辑U程排队处理
2. 数据? rpc及其他io处理, 一律改为异步回调模? 不用同步接?/p>
3. 玩家存盘提交数据可以考虑复制q提交到存盘U程方式, 提高性能.
4. 采用多进E架? 比如讄兌E? 把io压力分散到进E中
5. 逻辑~写? 不允怋用go开U程及channel, 有需要提高性能部分需要单独编?/p>
cellnet在开发时本来考虑使用actor模型来进一步简化多U程逻辑的麻? l历了一D|间的原型开发后, 发现了一些问? 列D如下:
1. golang的强cd不适合actor模型q种l常需要动态生成各cL息的模型, 但skynet(C+lua)/erlang有天生优势
2. actor模型本n不是万能? 不能解决所有需? 特别是游?/p>
3. actor模型理解到应用有一定的隑ֺ. 本nq需要搭建框? 上手复杂
M, 看过一些erlang及skynet的用? 没有应用的很U正且成熟的成功actor模型案例, 从传lsocket服务器框架跨到actor模型会扯到蛋, 因此, 后期cellnet会考虑回归到成熟的socket服务器框? 把架构做到简单上? 高扩展上.
游戏客户端采用Cocos2dx-Lua的纯Lua~写逻辑, 服务器采用Golang作ؓ开发语a
游戏cdcM于COC,因此无需选服. 需要用大服务器架构进行处?
采用Mongodb做持久存? redis做跨服通信数据交换
使用UCloud的云技? 省去了烦人的q维工作
客户端和服务器通讯使用HTTP短连? Zjson的数据封包协?
服务器间大量使用Golang自带的json+rpcq行通信
服务器类型大致分为逻辑服务?战斗服务? 中心服务?
逻辑服务器负责日帔R辑及公共逻辑处理(好友, 公会)
1个逻辑服务器对应一个区, n个区均用Ucloud云Mongodbq行数据存储
战斗服务器是一个集? 集群会返回一个负载最低的服务器返回?
战斗服务器通过cgo技术与客户端C++/lua的战斗逻辑q行逻辑复用, 在此技术上q行
战斗逻辑的校?
客户端登陆前, 在中心服务器q里获得可登陆的逻辑服务器地址, 同时做一个负载均?
短连?
׃操作pȝ的技术趋于稳? 同时, 手游的弱交互型导致的游戏架构于? 因此|络负蝲不再是游戏服务器技术的瓉. 从经验看? 游戏服务器技? 更重要的是还是看数据库的选型及处理方?
虽然Mongodb的性能上不如内存数据库. 但是从存储安全性上要比个h搭徏的内存数据库? 安全
外加上云技术的引用, 性能的瓶颈和q维的技术复杂度q刃而解
Redis用于跨服数据交互那是再好不过的数据中介了, 保证速度和稳定? l对不是造轮子能比拟?
短连接在手游上处理v来比长连接简单一? 无需做断UKq? 服务器的底层也是由Golang的框架库保证质量? 因此负蝲毫无问题. 服务器对内及对外均用jsonq行数据交换, 化了协议处理. 也方便了调试
json rpc的性能损耗对于整个逻辑的处理来说均可以忽略不计
我们的项目一直用MySQL作ؓ数据? 无论是从C++的服务器, q是到Golang服务? 当年搞服务器? 看大部分人都是用SQL(MySQL/SQLServer), 而Mongo感觉像邪教一? 再加上服务器q是Linux比较正统, 所以果断选了MySQL.
刚开始感?游戏服务器的数据存储其实应该是蛮圣的过E? 那么多的数据, 需要按照MySQL一样分? 分字D存? Z查询, q要乖乖的学一下SQL的语?/p>
p么折腾了几年. 在云DB的蒙蔽下, 一直认为MySQL是做游戏服务器存储的专业技? 分布式和存储压力一定交l云DB来做. 直到真正试了下NoSQL在游戏服务器开发里的思\.
用了Golang, 才发现同步写逻辑是多么的优雅.
用了NoSQLpd的数据库, 才意识到: 游戏服务器的数据存储和游戏服务器的存盘两个概念差异其实蛮大的.
MySQL? 背包其实跟角色完全没有关p? 只是通过1个角色id映射q去, Zؓ的割裂了数据的关联? q硬生生的整Z概念叫结构化查询让你?/p>
NoSQL? 只是把数据库当成是存储点, 每个角色的数据是完整的一? 里面怎么存随你便. 每个角色通过id来查? 其他都没有了
于是? 游戏开发变得异常简? MySQL角色q门查询4~5ơ才能搞定要的数?而NoSQL一口气全查出来, 存盘也无需增量, 直接存盘可以了
所以现在觉? NoSQL的思\对于游戏服务器存储来说简直是完美?
转蝲h? 战魂筑http://www.shnenglu.com/sunicdavy
NoSQL下实现方案很? 游戏常用的就q么3? mongo, redis, memcached
下面说下优缺?/p>
盘映射内存数据?/p>
value为documentcd, ZBSON的value序列?/p>
应用场景:
适合多写读, 例如日志和备?/p>
转蝲h? 战魂筑http://www.shnenglu.com/sunicdavy
内存数据?/p>
单核
value限制512M
多种valuecd, 游戏用途用私有的序列化协?例如protobuf)
支持落地(bgsave)
用户: 新浪, 淘宝, Flickr, Github
应用场景: 适合d都很? 数据处理复杂{?/p>
转蝲h? 战魂筑http://www.shnenglu.com/sunicdavy
内存数据?/p>
多核
value限制1M
不支持落?持久?
用户: LiveJournal、hatena、Facebook、Vox
应用场景: 动态系l中的缓? 适合多读写
转蝲h? 战魂筑http://www.shnenglu.com/sunicdavy
memcached 适合|页~冲, 游戏里很有使用. 目前只有腾讯云支持云memcached
redis非常适合游戏的内存数据库, 但是落地{略会比较复? 需要具体分? 可以参考后面的链接看下云风怎么处理q个问题
mongo数据库在早期q是非常不错的NoSQL的数据库. 工具比较方便, 可视? 但是随着q年来游戏的q发度越来越? 所以ؓ了一ơ到? 很多是选择了redis
下图参考自知乎问题. 链接在后面有提示, 若R权请联系删除
转蝲h? 战魂筑http://www.shnenglu.com/sunicdavy
http://blog.codingnow.com/2014/03/mmzb_redis.html
转蝲h? 战魂筑http://www.shnenglu.com/sunicdavy
Memcache,Redis,MongoDBQ数据缓存系l)ҎҎ与分?/p>
http://blog.csdn.net/suifeng3051/article/details/23739295
http://www.zhihu.com/question/31417262
回想了下, goroutine的调度规? 1.4之前, 在碰到syscall? goroutine会被调度q处? 1.4? 只要有函数调用时, 均会q行一ơ调? 密度比以前增加了, 更加接近真线E的处理.
Ҏq个原理, 问题应该出现在服务器底层没有l系l提供调度机会的点上. 我们的服务器通过一个bool型的chanq行d, 让服务器l持dq行消息处理不退? 但是最qؓ了在windows下提供命令行支持, 增加了一些代? 如下
1: func WaitForExit() {
2:
3: if len(peerMap) == 0 {
4: log.Println("no peer running, exit!")
5: return
6: }
7:
8: // 命o行功能只在windows下启?/span>
9: if runtime.GOOS == "windows" {
10: reader := bufio.NewReader(os.Stdin)
11:
12: var running bool = true
13:
14: go func() {
15: select {
16: case <-exitChan:
17: running = false
18: }
19: }()
20:
21: for running {
22: data, _, _ := reader.ReadLine()
23: command := string(data)
24:
25: dispatchConsoleCommand(command)
26: }
27: } else {
28: // Linux环境
29: <-exitChan
30: }
31:
32: }
我暂时屏蔽了新加的这套功? l持<-exitChan, 问题马上解决
l合前面的猜? 我估计在reader.ReadLine()函数? 没有l底层提供调度的Z, D其他goroutine无法q行, 造成服务器卡?/p>
技术讨论群: 309800774 Ƣ迎golang爱好者加? U技术研?/p>
ZC/C++游戏服务器框架M设计的还是不错的, 兄弟们M使用效果都是好评. 因ؓ在技术上喜欢"h", 所以在很多设计? 都是力求? 高效(开发效?.
Zd的异步DB查询pȝ, 带多重异步的同步
代码CZ:
1:
2: void BatchQueryPlayerInfo( uint32 ClientID, const std::string& AccountName, int64 CharID )
3: {
4: GDBExecutor->Commit
5: (
6: dynamic_cast<DBDataTask*>( (new DBQueryCharInfo( ClientID, CharID ) )
7: ->LinkAtomTask( new DBQueryQuest( ClientID, CharID ) )
8: ->LinkAtomTask( new DBQuerySkill( ClientID, CharID ) )
9: ->LinkAtomTask( new DBQueryHero( ClientID, CharID ) )
10: ->LinkAtomTask( new DBQueryAccountInfo( ClientID, AccountName ) )
11: ->LinkAtomTask( new DBQueryEquip( ClientID, CharID ) )
12: ->LinkAtomTask( new DBQueryObject( ClientID ,CharID ) )
13: ->LinkAtomTask( new DBQueryLevel(ClientID, CharID))
14: ->LinkAtomTask( new DBQueryChapter(ClientID, CharID))
15: ->LinkAtomTask( new DBQueryActivity( ClientID, CharID ))
16: )
17: );
18:
19: }
q段主要处理玩家在登陆时, 需要从DB查询大量的不同分cȝ数据. Z保证效率, 我让每个Taskq行执行, 然后通过一个机? 让所有Q务完成后, 回调W一个Q务的一个函? q样无需手动实现很多_合代码, 避免了反复调试和错误
Zprotobuf反射机制的语句自动合?/strong>
1: DBUpdateCharInfo::DBUpdateCharInfo( int64 CharID, const std::string& Buffer )
2: {
3: char buffer[256];
4:
5: sprintf( buffer, "update tb_char set $FIELD$ where charid = %lld;", CharID );
6:
7: ExecuteCommand( buffer, Buffer, dbopr::FET_Equation );
8: }
q段是一个典型的DBd, 构造函数提供了CharID和一个由l构体序列化好的buffer, $FIELD$字段, 是通过反射ҎBuffer内容, 自动填充字段
q段例子? $FIELD$可以填充?hp=100, mp=100之类? 自动填充避免了因为添加字D늚到处d代码, q需要调? Ҏ搞错
配置pȝ概念
Z同一个配|系l? 分层实现不同的需? 更简单的? 解决?个实际问题是:
自己改了配置文g中的ip, 上传svn? 覆盖了别人的配置, 很多人的解决Ҏ都是, 本地配置不提? 但同旉题又来了:
当配|中有别人新加的pȝ配置, 怎么保证每个人都能更新到?
上线? 服务器交付运l? 他们会对配置有一定程度的修改, q个时候怎么合ƈE序配置和运l配|?
其实对于冲突的需? 只要对系l进行分层就可以解决问题,我的处理方式:
配置分ؓ:
全局配置: 所有服务的M配置
单服务配|? 本服务的配置, 涉及|络及逻辑
本地配置: q个配置每个Z? 不上传SVN
命o行配|? 格式和前面的一? q块可以通过q维q行配置
Ml构其实是OO的派生概? 下层可以覆盖, 修改上层的配|?/p>
服务器互联及识别框架
基本功能: Z一些简单的配置可以将多台服务? 同种cȝ不同服务器互相连接v? 断线自动重连.
服务器连接后, 所有服务器可知晓ƈ可自动按需q接
逻辑端也很方便的q行q播或者单独发送等
也就是说, 每个服务器的q接和接受端都是带识别名U或id?
后面觉得q套东西实在是做的复? 多整ZC心服务器来做. 但好Ҏ架稳定下来了, 也就好了.
Zlua的服务器web后台框架
思想是很不错? C++ 配合lua本nl对是个p|
问题出在web处理,本n都是一个同步阻塞过E? 而这个后台框架是异步方式来做, 所以特别别?/p>
不过比v以前的本地GMpȝ, q块的设计是伟大的进?/p>
现在正在设计Zgolang的服务器框架, 基本框架已经完工, {待~写逻辑后的实战试
以上的很多思想在golang的服务器框架都有改进, 特别是golang本n做web也是优秀? 外加martiniq种牛X框架, 更是水到渠成
如果你对服务器框架设计有特别的认? 或者想撞思想, 可以加博客群 309800774或者我的qq: 20998333讨论
但默认因为找不到git而报错?解决Ҏ如下Q?/p>
?a title="http://git-scm.com/downloads" >http://git-scm.com/downloads下蝲对应q_git
在LiteIDE的查?>~辑环境变量中, 修改PATHQ?加入git路径。例?/p>
PATH=c:\mingw64\bin;%GOROOT%\bin;c:\Program Files (x86)\Git\bin;%PATH%
再按下Get键, W三方包׃自动更新?/p>
按照作者的安装Ҏ在天朝行不通的, 原因你懂?/p>
因此q入q个链接, 点击双的Download ZIP下蝲快照?/p>
下蝲好后攑ֈ你的GOPATH指定路径, 整理路径如下
github.com\aarzilli\golua\
其下的目录如?/p>
example\
lua\
LICENSE
README.md
TODO
然后准备lua5.1的开发包
lua-5.1.4.tar.gz
q有2个第三方依赖?/p>
readline-6.2.tar.gz
ncurses-5.9.tar.gz
直接configure ?gt; make install 装好
go env认你的GOPATH已经指向你的开发目?/p>
golua默认使用cgoq行~译, 可能会报? 修改lua.go的cgo定义如下
#cgo linux,!llua,!luaa LDFLAGS: -llua -lm –ldl
q入$GOPATH\src\github.com\aarzilli\golua\lua
执行go install
完成
目前Go~译器是C写的Q是时候换成Go啦?
“gc"Go工具链来自Plan 9~译器的工具链。组装器、C~译器和链接器基本没变。Go的编译器(cmd/gc,cmd/5g,cmd/6g,cmd/8g)是配合工具链写的新的CE序?
目起始Ӟ用C而不是Go写编译器有很多好处。突出的比如Q首先,那时候Goq不存在Q没法儿写编译器。而且实际上,q存在Q也会经常有明显的不兼容的变化。用C不用Go可以避免初始和持l开发导致的问题。然而如今Go 1已经E_Q所以这些持l的问题减少了很多?
持箋开发的问题已经消除Qؓ了让Go实现的编译器比C更有吸引力,另一些工E问题出玎ͼ
写正的Go代码比写正确的C代码更容易?/p>
调试错误的Go代码比调试错误的C代码更容易?/p>
使用Go~译器需要对Go有一定理解。而用C~译器还需要一定理解C?/p>
Go使ƈ发执行比C更方ѝ?/p>
Go有更好的标准支持模块化,自动重写Q单元测试和性能分析?/p>
Go比C更有?fun)?/p>
Z以上理由Q我们相信是时候用Go写Go~译器啦?
我们打算用自动化译工具来用Go重写现在C的编译器。这个翻译需要一些阶D,从Go 1.3开始持l到未来的发行版?
W一阶段。开发和调试一个自动化译工具。这可以在日常开发时同步q行。而且Qh们还可以在这个阶DؓC~译器l改q。这个工具工作量很大Q不q我们有信心完成q个Ҏ使命的工兗有许多C的观忉|法儿直接转换成GoQmacros(?Qunions(联合Q共用体,)Qbit fields(位域)可能最先考虑。比较幸q(不是巧合Q,q些功能功能用的,都会被翻译掉。指针运和数组也需要一些{换工作,管~译器里很少。编译器里主要是tree(?和linked list(链表)。翻译工具会保留注释和C代码的结构,所以翻译后的代码和当前的编译器代码一样可阅读?
W二阶段。用译工具转换C代码到GoQƈ删除C源码。这时我们已l开始翻译,但是Goq是q行在C~译器上。非怹观的Q这可能发生在Go 1.3。不q更可能是Go 1.4?
W三阶段。用一些工P可能来自gofix和the Go oracleQ拆分编译器到包Q清理和文档化代码,d适当的单元测试。这是编译器会是地道的GoE序。目前打在Go 1.4实现?
W四a阶段。用标准的分析和测试工具优化编译器的CPU和内存用。可能要引入q行。如果真q样Q?a >Race Detector(Go的ƈ行竞争检工?)会有很大帮助。这目标在Go 1.4Q可能部分会延后?.5。基本的优化分析会在W三阶段完成?
W四b阶段。(和四a几段同时q行Q当~译器依照明昄界限分割成包之后Q需要明引入一个中介码Q在l构无关的无序树(Node_s)和结构相关的有序链表(Prog_s)之间。这个中介码应该不依赖整体架构,但是包含准确的执行顺序信息,可以用于有顺序但是结构无关的操作的优化,比如清理多余的nil和出界。这些过E基?a >SSAQ静态单赋|Q你可以从Alan Donovan?go.tools/ssa 包中了解更多?
W五阶段。替换go/parser和go/types到最斎ͼ全新Q的版本。Robert Griesemer参考现在的l验Q讨Z设计新的parser和types的可能。如果联pM们到~译器后端,怿对设计新的API有很大帮助?
自展QBootstrappingQ?/strong>用Go语言实现的Go的编译器Q从一开始就要考虑如何自展。我们考虑的规则就是Go1.3~译器必ȝGo1.2~译QGo1.4的编译器必须由Go1.4~译Q以此类推? q时Q我们就有了一个清晰的程来生成当前的E序Q编译Go1.2的工具链Q由C~写Q,然后使用它编译Go1.3的工具链Q以此类推。这里需要一个脚本来做这个事情,来保证只会消耗CPU的时间而非某个人的旉。这L自展Q每个机器只会做一ơ,Go1.x的工具链会在本C留,q在执行all.bash来编译Go1.(x+1)工具铄时候被再次使用? 昄Q随着旉的推U这U自举方式是不充分的。在后面的多个版本被发布之前Qؓ~译器写一个后端来生成C代码也许是一个更有意义的事情。这些C代码不要求效率或可读性,只要正确卛_。这些C代码会被签入,像我们{օ由yacc生成的y.tab.c文g一栗这P自展q程׃变成Q先用gcc~译C代码生成一个自展编译器Q然后用这个自展编译器来编译真正的~译器。类g另一个自展过E,q个自展~译器将会在本地保留Qƈ在每ơ执行all.bash的时候重复用(不用重新~译Q? 替代选择q有一些比较明昄替代ҎQ需要我们说明一下ؓ什么放弃了q些选择? 从一开始写一个编译器。现在的~译器有一个非帔R要的特征Q他们能够正常工作(或者其臛_能够满所有用L要求Q。尽Go语言比较单,但是~译器中有很多细微的l节优化和改写,直接丢弃10或数q的在这上面的努力是比较愚蠢的? 对编译器q行人工译。我们已l以人工的方式翻译了一部分C/C++代码到Go语言了。这个过E是枯燥而且易错的,且这些错误非常的l微及难以发现。相反,使用机械译会Ş成一些比较一致的错误Q而这些错误是易于发现的;而且不会因ؓ枯燥的过E开差。Go~译器的代码明显的比我们译的代码多很多Q超q?0,000行C代码Q机械翻译会使这个过E容易一些。就像Dick Sites?974q说的一P“相比写E序Q我宁愿写一个程序来帮我写程序。?使用机械来翻译编译器也方便于在准备好切换之前Q我们可以l开发完善现有的CE序? 只翻译后端ƈ链接到go/parser和go/types.从前端传l后端的数据l构所包含的信息中Qgo/parser和go/types所能提供的除了API没其他的东西了。如果用这些库来替代前端,需要写代码来{换go/parser和go/types所能提供数据结构到后端Q这是一个非常宽泛且易出错的工作。我们相信用这些库是有意义的,但更明智的是Q等到将~译器代码调整的更像GoE序Q分成确定边界的、包含说明文档和单元试子包之后再用? 攑ּ现有的编译器Q用gccgoQ或者go/parser + go/types + LLVM, …)。现有的~译器是Go语言昑־比较灉|的一个重要组成部分。如果尝试用基于大量代码的GCC或LLVM来开发GoE序Q感觉会有碍到Go语言的灵zL。另外,GCC是大量C代码Q现在有部分C++Q、LLVM是大量C++代码的程序。以上列丄、用于解释不使用现有~译框架代码的几个原因,也都适用于更多的cM的代码库? 临近l束Q这个计划还留下了由C写成的Plan9的工具链的一部分。在长期发展中,q是所有的C从代码树排除掉比较好。本章节推测了一下这件事会如何发生Q但不保证其指定会发生或者按照这U套路发生? q行时包(runtime)?runtime包的大部分都是用C写成Q基于一些同L原因QGo~译器也是用C实现。但是,runtime包远比编译器的代码量要小Q且它现在已l是用Go和C混合~写。将C代码转换为Go代码Ӟ一ơ{化一部分貌似也是可行的。其中,主要部分有:调度器(schedulerQ,垃圾回收Qthe garbage collectorQ,散列映射表(hash mapQ的实现Q和channel的实现。(q里Go和C代码混合的很融洽Q是因ؓq里使用?c而不是gcc来编译的C代码。) C~译器?Plan 9的C~译器本w就是用C写成Q如果我们要从Go包实现里面移除所有的C代码Q那么我们将U除q些~译工具Q“go tool 6c”等{,另外Q?c的文件也不被支持出现的Go包的目录里面。我们应该提前声明这L计划Q以便用C的第三方包有旉ȝ除这cC代码的用。(CgoQ由于用了gcc来替?cQ所以它仍然可以作ؓ一个途径来在Go包中使用C实现部分功能。)在Go1的兼Ҏ文档中没有包含工具链修改的描述Q也是说去掉C~译器是被允许的? 汇编器?Plan 9的汇~器也是用C实现的,但这个汇~器只不q是一pd解析树组成的单解析器Q这使得不论手动q是自动它译成Go语言都比较简单? q接器?Plan 9的连接器也是由C写成。最q的一些工作,已经大部分的连接器工作攑ֈ的编译器中,而且Q也已经有个计划剩余的部分重写成一个新的、更单的GoE序。{Ud~译器的部分q接器代码,现在需要随着~译器的原有代码一赯行翻译? ZLibmach的工? nm, pack, addr2line, 和objdump?Nm现在已经使用Go语言重写。Pack和addr2line可以M一天被重写。Objdump现在依赖于libmach的反汇编器,但这些{换ؓGo也是比较单的Q不论是使用机械q是人工译。所以基于这几点Qlibmach本n来也可以被U除? 来源: http://www.oschina.net/translate/go-1-3-compiler-overhaul C语言的长期?/h5>
]]>
搜烦到gocode的开发页?a >https://github.com/nsf/gocode l果发现nsfq家伙居然也是luaBridge的作?
下蝲最新的gocode代码, 解压? ~译:
windows下命令行
go build gocode.go autocompletecontext.go autocompletefile.go client.go config.go cursorcontext.go decl.go declcache.go formatters.go os_windows.go package.go ripper.go rpc.go scope.go server.go utils.go
linux? 只需要将os_windows.go换ؓos_posix.go卛_
~译完成? 可执行文ggocode覆盖到liteIDE下的同名文g, 杀掉gocodeq程后重启liteIDE卛_
error: missing binary operator before token "("
Ҏ老外的描q? boost中的BOOST_PP_ITERATION_FLAGS?.49版本后发生了一些变?
在git扑ֈ一个patch, 链接在此
以下单描q?/p>
修改call_function.hpp, call_member.hpp及wrapper_base.hpp
L#elif BOOST_PP_ITERATION_FLAGS() == 1
更换?/p>
#else
#if BOOST_PP_ITERATION_FLAGS() == 1
然后在源码底部加一?endif卛_
在编写本文前, W者用过诸如libunwind{库q行错误时堆栈打? 但是其本w由于需要引用第三方? 使用q是E微ȝ.
l过Google? 居然扑ֈ一?a >好文, 光过捕获SIGSEGV信号, q迫使程序进入gdb调试阶段, 利用gdb强大的调试功能可以进行各U错误跟t? 此法已与Windows下程序崩溃后弹出VC调试几乎接近.
我在此文基础? 扩展了其通用性及便利?/p>
1. 使用gdb?-ex参数, 在挂接程序后, 执行bt指o打出E序堆栈
2. 信息重定向到自定义的文?在多q程都需要进行后台输出时带来更大的灵zL? 同时也解决了gdb只能在前台调试的问题
代码如下
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> void dump(int signo) { char buf[1024]; char cmd[1024]; FILE *fh; snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid()); if(!(fh = fopen(buf, "r"))) exit(0); if(!fgets(buf, sizeof(buf), fh)) exit(0); fclose(fh); if(buf[strlen(buf) - 1] == '/n') buf[strlen(buf) - 1] = '/0'; snprintf(cmd, sizeof(cmd), "gdb %s %d -ex=bt > ./a.txt", buf, getpid()); system(cmd); exit(0); }
在服务器开启时,d signal(SIGSEGV, &dump ); q行信号处理挂接卛_
引用: http://blog.csdn.net/kakaka2011/article/details/6597857 作? kakaka2011
”After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter. An application will typically leave that data in the streambuf for a subsequent async_read_until operation to examine.?/p>
意思是, streambuf中ƈ不一定是到分隔符前的所有数? 多余的数据可能一样会在streambuf? 也就是说, q需要自己再ơ处理一遍数?..
动手? async_read_until看似是一个废? 底层已经费了很多CPU在逐字W与分隔W的匚w? 抛上来的数据居然q是半成?
代码如下, 试通过, 但是实在很费解ؓ啥非要再做一?.
boost::asio::streambuf* SB = SBP.get(); // 讉K~冲 const char* Buffs = boost::asio::buffer_cast<const char*>( SB->data() ); uint32 DataSize = 0; for ( uint32 i = 0; i < SB->size(); ++i ) { const char DChar = Buffs[i]; // q里需要自己判断字W串内容, read_until的文档里q么说的 if ( DChar == '\0' ) { DataSize = i; break; } } if ( DataSize > 0 ) { // 取成字符?/span> std::string FullText( Buffs, DataSize ); // 消费 SB->consume( DataSize ); mWorkService->post( boost::bind(&AsioSession::NotifyReadString, shared_from_this(), FullText ) ); }
另外, Z保证输入性安? 可以在streambuf构造时加一个最大一个读取量, 过此量会返回报? 避免了缓冲区被撑爆的危险
最q遇到DB服务器中报出一个MySQL的错?Commands out of sync; you can't run this command now,2014
查阅很多代码, 解决Ҏ都是使用C接口的方? 模仿其解x?在MySQL++中找C比较好的解决Ҏ:
ҎA: 清空每次未用的记录
for (int i = 1; DataQuery.more_results(); ++i) { DataQuery.store_next(); }
其中 DataQuerycd为mysqlpp::Query
ҎB: 对于存储q程?使用了多个select语句q回同样的列l果, 需要用以下语?/p>
static void print_multiple_results(Query& query)
{
// 执行查询q输出结果表
StoreQueryResult res = query.store();
print_result(res, 0);
for (int i = 1; query.more_results(); ++i) {
res = query.store_next();
print_result(res, i);
}
}
参考文?http://hi.baidu.com/freeknight/item/ea9fd88e7d291f854514cf43
传统的服务器/客户端版本发布流E都需要经历以下流E?
1. 获取代码
2. ~译代码
3. 配|?二进制文? 资源打包
4. 挂接q程服务器磁盘拷贝打包文?/font>
5. q程操作解压打包文g
6. 修改讄,指向最新版?/font>
7. 重启服务?/font>
此流E繁?重复且无? 同时, ׃|络带宽,|速等U束, 每次若用完整包发布,传输h非常吃力
本文讨论的外|服务器׃安全性要?止rootd,只能用普通帐L录或传输? 提权为rootl箋q行以上操作, 因此rsync的用受C重限?/font>
即便使用Windows下的同步软g, 也几乎不可能.
HG作ؓ一个优U,y的跨q_代码理软g的特?正好能解决以上问? 主要Ҏ?
1. 安装? 可以使用代码直接安装
2. 利用本地映射版本可以Ҏ版本做差异比?/font>
3. 增量包传? 100%同步, 本地文g删除? q程文g也会同步删除
4. 传输压羃
5. 增量包可以打包ؓpatchq行ȝ更新
6. 可以恢复CQ意版? 提交版本有据可查
以下部vpȝ以CentOS为基, 其他pȝcM
本文来自战魂筑的博?a href="http://www.shnenglu.com/sunicdavy">http://www.shnenglu.com/sunicdavy 转蝲h明来?/font>
yum install python-devel
wget http://mercurial.selenic.com/release/mercurial-2.1.tar.gz
tar zxvf ./mercurial-2.1.tar.gz
make all
make install
hg debuginstall
扑ֈ你需要同步的目录,q入目录
执行
hg init
vi .hg/hgrc
d以下内容,让这个仓库支持外部push
[ui]
username=服务器提交后看到的用户名
[web]
vi /etc/sysconfig/iptables
dHG服务?000端口
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 8000 -j ACCEPT
开启仓库同步服?/font>
hg serve
本地机器同样扑ֈ文g?创徏仓库
hg init
以后每次需要同步时,使用命o,或者乌龟HG的界面工h取服务器数据卛_
hg pull http://服务器地址:8000
版本提交Ҏ与HG日常使用cM, q里不再阐述
对于某些服务器深处防火墙或者安全登录后?不能直接开?000端口的情?/p>
可以使用hg导出一个patch, 传输到远E服务器, 使用hg import PATCH 卛_
[root@localhost bin]# ldd wkcenter
./wkcenter: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.11' not found (required by ./wkcenter)
./wkcenter: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.14' not found (required by ./wkcenter)
./wkcenter: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./wkcenter)
./wkcenter: /lib/libc.so.6: version `GLIBC_2.9' not found (required by ./wkcenter)
./wkcenter: /lib/libc.so.6: version `GLIBC_2.7' not found (required by ./wkcenter)
./wkcenter: /lib/libc.so.6: version `GLIBC_2.8' not found (required by ./wkcenter)
./wkcenter: /lib/libc.so.6: version `GLIBC_2.11' not found (required by ./wkcenter)
linux-gate.so.1 => (0xffffe000)
liblog4cpp.so.4 => not found
libprotobuf.so.7 => not found
libboost_filesystem.so.1.48.0 => not found
libboost_system.so.1.48.0 => not found
libboost_thread.so.1.48.0 => not found
libboost_program_options.so.1.48.0 => not found
libunwind-x86.so.7 => not found
libluabind.so.0.9.0 => not found
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x008ae000)
libm.so.6 => /lib/libm.so.6 (0x0044b000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00476000)
libc.so.6 => /lib/libc.so.6 (0x002c1000)
libpthread.so.0 => /lib/libpthread.so.0 (0x0041d000)
librt.so.1 => /lib/librt.so.1 (0x00440000)
/lib/ld-linux.so.2 (0x002a2000)
上面U字部分表示glibc及glibcxx库依赖不正确. 本h使用的Linux~译版本为Mint 11(ZUbuntu), 一般Ubuntu发行版的glibc配备非常? 但是上文中的发布的Linux版本为CentOS 5.8
使用/lib/libc.so.6 查看libc版本?.5, q远低于开发环境的2.11
GNU C Library stable release version 2.5, by Roland McGrath et al.
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.1.2 20080704 (Red Hat 4.1.2-51).
Compiled on a Linux 2.6.9 system on 2012-02-21.
Available extensions:
The C stubs add-on version 2.1.2.
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
GNU libio by Per Bothner
NIS(YP)/NIS+ NSS modules 0.19 by Thorsten Kukuk
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
RT using linux kernel aio
Thread-local storage support included.
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.
׃Linux操作pȝ的特有elf加蝲序. (可以参?a >此文). 虽然可以很大E度上解决Windows早期版本的dll hell问题, 但是l部|带来了很大隑ֺ
一般常见的解决Ҏ? 扑ֈ一个与目标Linux版本及glibc版本一致的Linux, 代码及依赖包放在之上编? 完成后再发布.q种Ҏ与Linux下常见Y件安装方法类? 但是对于商用服务器部|步骤来说未免繁? 安全性低.
q有一U方?使用静态链? 所有可执行文g文g依赖的静态库, pȝ?全部静态链接到可执行文件中,可以一ơ性解册个问?/p>
步骤:
1. 在gcc链接命o行中d-static -static-libgcc -static-libstdc++
2. 第三方依赖库打开静态链接开? 原来链?so的库,全改为链?a
3. gcc寚w接库序很敏? 链接库顺序需要按照从前至后ؓ: 目产生的静态库 > W三方库静态库 > pȝ静态库
4. 链接? 若有未解决的symbol, 可以试在最后添?lpthread?lrt解决
在发布版本Linux上运行可能遇到的问题:
terminate called after throwing an instance of 'std::runtime_error'
what(): locale::facet::_S_create_c_locale name not valid
解决Ҏ: 执行之前q行export LC_ALL="C"
ServicePath=/usr/local/bin ServiceList=( "wkcenterd --toc /home/davy/dev/kaze/Config/CenterService.toc --logfile /tmp/centerd.log" "wkagentd --toc /home/davy/dev/kaze/Config/AgentService.toc --logfile /tmp/agentd.log" ) StartAll() { for((i = 0;i<${#ServiceList[*]};i=i+1)) do echo "start:" $ServicePath/${ServiceList[i]} $ServicePath/${ServiceList[i]} > /dev/null & done } StopAll() { for((i = 0;i<${#ServiceList[*]};i=i+1)) do echo "stop:" $ServicePath/${ServiceList[i]} svcname=`echo ${ServiceList[i]} | awk '{print $1}'` killall $svcname > /dev/null done } RestartAll() { StopAll StartAll } InstallService() { svcname=`basename $0` chmod +x $svcname cp $svcname /etc/init.d ln /etc/init.d/$svcname /etc/rc3.d/S03$svcname ln /etc/init.d/$svcname /etc/rc0.d/K03$svcname chkconfig --add $svcname chkconfig $svcname on chkconfig --list | grep $svcname } UninstallService() { svcname=`basename $0` chkconfig --del $svcname rm -f /etc/init.d/$svcname rm -f /etc/rc3.d/S03$svcname rm -f /etc/rc3.d/K03$svcname } case "$1" in start) StartAll ;; stop) StopAll ;; restart) RestartAll ;; install) InstallService ;; uninstall) UninstallService ;; *) echo "Usage: service $EXEC {install|start|stop|restart|uninst}" exit 1 esac exit $?
q里是一个Shell可以依赖列表中的文件拷贝到指定目录
deplist=$( ldd $1 | awk '{if (match($3,"/")){ print $3}}' )
cp $deplist $2
代码解释: ldd导出列表, q个列表打印出来很丑
linux-gate.so.1 => (0x00ed2000)
liblog4cpp.so.4 => /usr/local/lib/liblog4cpp.so.4 (0x00657000)
libprotobuf.so.7 => /usr/local/lib/libprotobuf.so.7 (0x00360000)
libboost_filesystem.so.1.48.0 => /usr/local/lib/libboost_filesystem.so.1.48.0 (0x00a9a000)
libboost_program_options.so.1.48.0 => /usr/local/lib/libboost_program_options.so.1.48.0 (0x00110000)
libboost_system.so.1.48.0 => /usr/local/lib/libboost_system.so.1.48.0 (0x00a85000)
libboost_thread.so.1.48.0 => /usr/local/lib/libboost_thread.so.1.48.0 (0x00179000)
libunwind-x86.so.7 => /usr/lib/libunwind-x86.so.7 (0x00821000)
libluabindd.so.0.9.0 => /usr/local/lib/libluabindd.so.0.9.0 (0x00bb3000)
libmysqlpp.so.3 => /usr/local/lib/libmysqlpp.so.3 (0x00de5000)
libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0x001a9000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0x00782000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0x00aea000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00447000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0x00abd000)
librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0x00193000)
libnsl.so.1 => /lib/i386-linux-gnu/libnsl.so.1 (0x00294000)
libunwind.so.7 => /usr/lib/libunwind.so.7 (0x002ab000)
libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0x00e8a000)
libmysqlclient_r.so.16 => /usr/lib/libmysqlclient_r.so.16 (0x0083b000)
/lib/ld-linux.so.2 (0x00608000)
libz.so.1 => /lib/i386-linux-gnu/libz.so.1 (0x002c0000)
我们发现W一行的so没有对应的库地址, 因此我们使用awk的脚本功?判断W三个参?也就?>之后的\径必d?
之后第一行的输出重定向到变量? 再用cp指o从列表拷贝到指定目录
参考链?a >http://yubosun.akcms.com/tech/linux-daemon-program.htm
1: #include <unistd.h>
2: #include <signal.h>
3: #include <sys/types.h>
4: #include <sys/stat.h>
5: #include <stdio.h>
6: #include <stdlib.h>
7:
8: #ifndef NOFILE
9: #define NOFILE 3
10: #endif
11:
12: void init_daemon()
13: {
14: int pid;
15: int i;
16: if(pid = fork()) exit(0); //父进E,退?
17:
18: else if(pid < 0) exit(1); //forkp|
19:
20: /* 子进El执?*/
21: setsid(); //创徏新的会话l,子进E成为组长,q与控制l端分离
22:
23: /* 防止子进E(l长Q获取控制终?*/
24: if(pid = fork()) exit(0); //父进E,退?
25:
26: else if(pid < 0) exit(1); //fork错误Q退?
27:
28: /* W二子进El执?, W二子进E不再是会会话组l长*/
29:
30: //for(i = 0; i < NOFILE; i++) /* 关闭打开的文件描q符*/
31: //{
32: //close(i);
33: //}
34: chdir("/tmp"); /* 切换工作目录 */
35: umask(0); /* 重设文g创徏掩码 */
36: return;
37: }
38:
39: int main(int argc, char* argv[])
40: {
41: FILE *fp;
42:
43: signal(SIGCHLD, SIG_IGN); /* 忽略子进E结束信P防止出现僵尸q程 */
44:
45: init_daemon();
46:
47: while(1)
48: {
49: sleep(1);
50:
51: // 注意, 日志写到q个目录
52: if((fp = fopen("/var/tmp/test.log", "a")) != NULL)
53: {
54: fprintf(fp, "%s\n", "test message");
55: fclose(fp);
56: }
57: }
58:
59: return 0;
60: }
参考链?a >http://blog.sina.com.cn/s/blog_57421ff80100c7nn.html
U色字是需要填写的部分, 文g头部分可以选填
1: #!/bin/bash
2:
3: # chkconfig: 3 3 1
4:
5: # description: web kill center
6:
7: EXEC_PATH=/usr/local/bin
8:
9: EXEC=CenterServiced
10:
11: PID_FILE=/var/run/CenterServiced.pid
12:
13: DAEMON=/usr/local/bin/CenterServiced
14:
15: if ! [ -x $EXEC_PATH/$EXEC ] ; then
16:
17: echo "ERROR: $EXEC_PATH/$EXEC not found"
18:
19: exit 1
20:
21: fi
22:
23: stop()
24:
25: {
26:
27: echo "Stoping $EXEC ..."
28:
29: killall $DAEMON >/dev/null
30:
31: echo "Shutting down $EXEC: [ OK ]"
32:
33: }
34:
35: start()
36:
37: {
38:
39: echo "Starting $EXEC ..."
40:
41: $DAEMON > /dev/null &
42:
43: echo "Starting $EXEC: [ OK ]"
44:
45: }
46:
47: restart()
48:
49: {
50:
51: stop
52:
53: start
54:
55: }
56:
57: case "$1" in
58:
59: start)
60:
61: start
62:
63: ;;
64:
65: stop)
66:
67: stop
68:
69: ;;
70:
71: restart)
72:
73: restart
74:
75: ;;
76:
77: status)
78:
79: status -p $PID_FILE $DAEMON
80:
81: ;;
82:
83: *)
84:
85: echo "Usage: service $EXEC {start|stop|restart|status}"
86:
87: exit 1
88:
89: esac
90:
91: exit $?
92:
参考链?a >http://hi.baidu.com/guanxiansun/blog/item/b4c7dcf55f6011e47709d724.html
服务文件拷贝到/etc/init.d?L扩展? 文g名即是服务名
chmod +x ./wkcenter
如果不设|启? 那么service中将无法扑ֈ该服务及操作
创徏启动链接
ln /etc/init.d/wkcenter /etc/rc3.d/S03wkcenter
创徏关闭链接
ln /etc/init.d/wkcenter /etc/rc0.d/K03wkcenter
chkconfig --add wkcenter
查看服务是否存在
chkconfig ?list | grep wkcenter
查看服务状?
chkconfig wkcenter on
注意, 认wkcenter?,3,4,5中Q意或者部分开? 必须为绿? 灰字代表服务无法开机启动或者其他问?
临时开启命令测?
service wkcenter start
参考链? http://blog.526net.com/?p=1706
1. 服务切记不可攑֜用户home目录, 最好放?usr/local/bin目录, 日志写到var? 否则服务试正常,但是无法自动启动
2. Linux? 父进E启动的E序的生命期跟随父进E? 父进E可以是l端, 父进E一旦终? 子进E都必须l束. 因此守护q程需要脱ȝq程,避免被父q程生命期控?
在原来原基础?代码整?q加强安全? q按照WindowsAPI设计, d输出~冲长度探测功能
当OutUTFString为NULL? 可以q行输出的UTF8字符串长度探?/p>
1: uint32 UniCharToUTF8(wchar_t UniChar, char *OutUTFString)
2: {
3:
4: uint32 UTF8CharLength = 0;
5:
6: if (UniChar < 0x80)
7: {
8: if ( OutUTFString )
9: OutUTFString[UTF8CharLength++] = (char)UniChar;
10: else
11: UTF8CharLength++;
12: }
13: else if(UniChar < 0x800)
14: {
15: if ( OutUTFString )
16: {
17: OutUTFString[UTF8CharLength++] = 0xc0 | ( UniChar >> 6 );
18: OutUTFString[UTF8CharLength++] = 0x80 | ( UniChar & 0x3f );
19: }
20: else
21: {
22: UTF8CharLength += 2;
23: }
24: }
25: else if(UniChar < 0x10000 )
26: {
27: if ( OutUTFString )
28: {
29: OutUTFString[UTF8CharLength++] = 0xe0 | ( UniChar >> 12 );
30: OutUTFString[UTF8CharLength++] = 0x80 | ( (UniChar >> 6) & 0x3f );
31: OutUTFString[UTF8CharLength++] = 0x80 | ( UniChar & 0x3f );
32: }
33: else
34: {
35: UTF8CharLength += 3;
36: }
37: }
38: else if( UniChar < 0x200000 )
39: {
40: if ( OutUTFString )
41: {
42: OutUTFString[UTF8CharLength++] = 0xf0 | ( (int)UniChar >> 18 );
43: OutUTFString[UTF8CharLength++] = 0x80 | ( (UniChar >> 12) & 0x3f );
44: OutUTFString[UTF8CharLength++] = 0x80 | ( (UniChar >> 6) & 0x3f );
45: OutUTFString[UTF8CharLength++] = 0x80 | ( UniChar & 0x3f );
46: }
47: else
48: {
49: UTF8CharLength += 4;
50: }
51:
52: }
53:
54: return UTF8CharLength;
55: }
当OutUnicodeString为NULL? 可以q行输出的Unicode字符串长度探?/p>
1: uint32 UTF8StrToUnicode( const char* UTF8String, uint32 UTF8StringLength, wchar_t* OutUnicodeString, uint32 UnicodeStringBufferSize )
2: {
3: uint32 UTF8Index = 0;
4: uint32 UniIndex = 0;
5:
6: while ( UTF8Index < UTF8StringLength )
7: {
8: unsigned char UTF8Char = UTF8String[UTF8Index];
9:
10: if ( UnicodeStringBufferSize != 0 && UniIndex >= UnicodeStringBufferSize )
11: break;
12:
13: if ((UTF8Char & 0x80) == 0)
14: {
15: const uint32 cUTF8CharRequire = 1;
16:
17: // UTF8字码不
18: if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
19: break;
20:
21: if ( OutUnicodeString )
22: {
23: wchar_t& WideChar = OutUnicodeString[UniIndex];
24:
25: WideChar = UTF8Char;
26: }
27:
28: UTF8Index++;
29:
30: }
31: else if((UTF8Char & 0xE0) == 0xC0) ///< 110x-xxxx 10xx-xxxx
32: {
33: const uint32 cUTF8CharRequire = 2;
34:
35: // UTF8字码不
36: if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
37: break;
38:
39: if ( OutUnicodeString )
40: {
41: wchar_t& WideChar = OutUnicodeString[UniIndex];
42: WideChar = (UTF8String[UTF8Index + 0] & 0x3F) << 6;
43: WideChar |= (UTF8String[UTF8Index + 1] & 0x3F);
44: }
45:
46: UTF8Index += cUTF8CharRequire;
47: }
48: else if((UTF8Char & 0xF0) == 0xE0) ///< 1110-xxxx 10xx-xxxx 10xx-xxxx
49: {
50: const uint32 cUTF8CharRequire = 3;
51:
52: // UTF8字码不
53: if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
54: break;
55:
56: if ( OutUnicodeString )
57: {
58: wchar_t& WideChar = OutUnicodeString[UniIndex];
59:
60: WideChar = (UTF8String[UTF8Index + 0] & 0x1F) << 12;
61: WideChar |= (UTF8String[UTF8Index + 1] & 0x3F) << 6;
62: WideChar |= (UTF8String[UTF8Index + 2] & 0x3F);
63: }
64:
65:
66: UTF8Index += cUTF8CharRequire;
67: }
68: else if((UTF8Char & 0xF8) == 0xF0) ///< 1111-0xxx 10xx-xxxx 10xx-xxxx 10xx-xxxx
69: {
70: const uint32 cUTF8CharRequire = 4;
71:
72: // UTF8字码不
73: if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
74: break;
75:
76: if ( OutUnicodeString )
77: {
78: wchar_t& WideChar = OutUnicodeString[UniIndex];
79:
80: WideChar = (UTF8String[UTF8Index + 0] & 0x0F) << 18;
81: WideChar = (UTF8String[UTF8Index + 1] & 0x3F) << 12;
82: WideChar |= (UTF8String[UTF8Index + 2] & 0x3F) << 6;
83: WideChar |= (UTF8String[UTF8Index + 3] & 0x3F);
84: }
85:
86: UTF8Index += cUTF8CharRequire;
87: }
88: else ///< 1111-10xx 10xx-xxxx 10xx-xxxx 10xx-xxxx 10xx-xxxx
89: {
90: const uint32 cUTF8CharRequire = 5;
91:
92: // UTF8字码不
93: if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
94: break;
95:
96: if ( OutUnicodeString )
97: {
98: wchar_t& WideChar = OutUnicodeString[UniIndex];
99:
100: WideChar = (UTF8String[UTF8Index + 0] & 0x07) << 24;
101: WideChar = (UTF8String[UTF8Index + 1] & 0x3F) << 18;
102: WideChar = (UTF8String[UTF8Index + 2] & 0x3F) << 12;
103: WideChar |= (UTF8String[UTF8Index + 3] & 0x3F) << 6;
104: WideChar |= (UTF8String[UTF8Index + 4] & 0x3F);
105: }
106:
107: UTF8Index += cUTF8CharRequire;
108: }
109:
110:
111: UniIndex++;
112: }
113:
114: return UniIndex;
115: }
疗效: 用了此代码啊, 再也不用被iconv折磨?/p>
1: #ifdef _WIN32
2: #include <hash_map>
3: #define HASHMAP_PREFIX stdext
4: #else
5: #include <ext/hash_map>
6: #define HASHMAP_PREFIX __gnu_cxx
7: #endif
对于初始桶大设|?Linux下用hash_map构造函数可以设|? Windows下则没有对应的设|函?
查阅Windows下hash_map的源?q在hash_map()默认构造函数旁Ҏ加一个测试用初始桶设|函?/p>
1: hash_map( size_type _Buckets )
2: : _Mybase(key_compare(), allocator_type())
3: {
4: _Init( _Buckets );
5: }
接下来用相同的试代码
1: const uint32 Buckets = 1000;
2: HASHMAP_PREFIX::hash_map<uint32,uint32> MyHash( Buckets );
3:
4: TimeRuler Ruler;
5: for ( uint32 i = 0; i <1000000;i++)
6: {
7: MyHash[i] = i;
8: }
9:
10: printf("%d\n", Ruler.GetCostTime() );
q里的TimeRuler是用boost timer的时间戳装
Release下测试结?
OS \ Buckets | 8 ( default ) | 1000 |
Win7 | 430ms | 560ms |
Mint( VMware ) | 127ms | 127ms |
Windows的测试结果说? 不给出桶初始化函数是正确? 默认理比自p|更高效.
Linuxq_感觉很诡? 不清楚是不是虚拟机造成的结果不准确
查阅bjam的参数说明及luabind的jamroot文gW?40行有如下文字
install stage
: luabind
: <location>$(stage-locate)
<install-no-version-symlinks>on
<install-dependencies>on
<install-type>LIB
;
说明参数应该是这L: bjam install [stage]
而且文章中给出的是bjam install 因此默认试版也是正确? 而且估计作者只~译了调试版没有处理release版了
正确的luabind~译法应该是:
export BOOST_ROOT=/home/davy/dev/boost_1_48_0
export LUA_PATH=/usr/local/
/home/davy/dev/boost_1_48_0/bjam stage --toolset=gcc --with-date_time --with-fpic --with-filesystem link=static debug release
/home/davy/dev/boost_1_48_0/bjam install debug
/home/davy/dev/boost_1_48_0/bjam install release
我这里必L明bjam是因为boost的bjam版本高于默认安装的版? 因此使用高版本编?
以下虚拟机内系l叫Guest, q行VMWare的系l叫Host
VMWare的网l方式设为Bridge模式. 注意Host-Only模式只能与Hostq接,局域网的机器及互联|机器无法访?/p>
在Guest的网l设|中,IP讄ZHost在一个局域网|段的IP, 讄DNS
关闭Guestpȝ
接下来将Host的可以上|的q接d׃n,q连接到VMNet1
q入Guestpȝ, 试上网