前段時(shí)間在golang-China讀到這個(gè)貼:
創(chuàng)新互聯(lián)公司公司2013年成立,先為臺(tái)前等服務(wù)建站,臺(tái)前等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為臺(tái)前企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。
個(gè)人覺(jué)得golang十分適合進(jìn)行網(wǎng)游服務(wù)器端開(kāi)發(fā),寫下這篇文章總結(jié)一下。
從網(wǎng)游的角度看:
要成功的運(yùn)營(yíng)一款網(wǎng)游,很大程度上依賴于玩家自發(fā)形成的社區(qū)。只有玩家自發(fā)形成一個(gè)穩(wěn)定的生態(tài)系統(tǒng),游戲才能持續(xù)下去,避免鬼城的出現(xiàn)。而這就需要多次大量導(dǎo)入用戶,在同時(shí)在線用戶量達(dá)到某個(gè)臨界點(diǎn)的時(shí)候,才有可能完成。因此,多人同時(shí)在線十分有必要。
再來(lái)看網(wǎng)游的常見(jiàn)玩法,除了排行榜這類統(tǒng)計(jì)和數(shù)據(jù)匯總的功能外,基本沒(méi)有需要大量CPU時(shí)間的應(yīng)用。以前的項(xiàng)目里,即時(shí)戰(zhàn)斗產(chǎn)生的各種傷害計(jì)算對(duì)CPU的消耗也不大。玩家要完成一次操作,需要通過(guò)客戶端-服務(wù)器端-客戶端這樣一個(gè)來(lái)回,為了獲得高響應(yīng)速度,滿足玩家體驗(yàn),服務(wù)器端的處理也不能占用太多時(shí)間。所以,每次請(qǐng)求對(duì)應(yīng)的CPU占用是比較小的。
網(wǎng)游的IO主要分兩個(gè)方面,一個(gè)是網(wǎng)絡(luò)IO,一個(gè)是磁盤IO。網(wǎng)絡(luò)IO方面,可以分成美術(shù)資源的IO和游戲邏輯指令的IO,這里主要分析游戲邏輯的IO。游戲邏輯的IO跟CPU占用的情況相似,每次請(qǐng)求的字節(jié)數(shù)很小,但由于多人同時(shí)在線,因此并發(fā)數(shù)相當(dāng)高。另外,地圖信息的廣播也會(huì)帶來(lái)比較頻繁的網(wǎng)絡(luò)通信。磁盤IO方面,主要是游戲數(shù)據(jù)的保存。采用不同的數(shù)據(jù)庫(kù),會(huì)有比較大的區(qū)別。以前的項(xiàng)目里,就經(jīng)歷了從MySQL轉(zhuǎn)向MongoDB這種內(nèi)存數(shù)據(jù)庫(kù)的過(guò)程,磁盤IO不再是瓶頸??傮w來(lái)說(shuō),還是用內(nèi)存做一級(jí)緩沖,避免大量小數(shù)據(jù)塊讀寫的方案。
針對(duì)網(wǎng)游的這些特點(diǎn),golang的語(yǔ)言特性十分適合開(kāi)發(fā)游戲服務(wù)器端。
首先,go語(yǔ)言提供goroutine機(jī)制作為原生的并發(fā)機(jī)制。每個(gè)goroutine所需的內(nèi)存很少,實(shí)際應(yīng)用中可以啟動(dòng)大量的goroutine對(duì)并發(fā)連接進(jìn)行響應(yīng)。goroutine與gevent中的greenlet很相像,遇到IO阻塞的時(shí)候,調(diào)度器就會(huì)自動(dòng)切換到另一個(gè)goroutine執(zhí)行,保證CPU不會(huì)因?yàn)镮O而發(fā)生等待。而goroutine與gevent相比,沒(méi)有了python底層的GIL限制,就不需要利用多進(jìn)程來(lái)榨取多核機(jī)器的性能了。通過(guò)設(shè)置最大線程數(shù),可以控制go所啟動(dòng)的線程,每個(gè)線程執(zhí)行一個(gè)goroutine,讓CPU滿負(fù)載運(yùn)行。
同時(shí),go語(yǔ)言為goroutine提供了獨(dú)到的通信機(jī)制channel。channel發(fā)生讀寫的時(shí)候,也會(huì)掛起當(dāng)前操作channel的goroutine,是一種同步阻塞通信。這樣既達(dá)到了通信的目的,又實(shí)現(xiàn)同步,用CSP模型的觀點(diǎn)看,并發(fā)模型就是通過(guò)一組進(jìn)程和進(jìn)程間的事件觸發(fā)解決任務(wù)的。雖然說(shuō),主流的編程語(yǔ)言之間,只要是圖靈完備的,他們就都能實(shí)現(xiàn)相同的功能。但go語(yǔ)言提供的這種協(xié)程間通信機(jī)制,十分優(yōu)雅地揭示了協(xié)程通信的本質(zhì),避免了以往鎖的顯式使用帶給程序員的心理負(fù)擔(dān),確是一大優(yōu)勢(shì)。進(jìn)行網(wǎng)游開(kāi)發(fā)的程序員,可以將游戲邏輯按照單線程阻塞式的寫,不需要額外考慮線程調(diào)度的問(wèn)題,以及線程間數(shù)據(jù)依賴的問(wèn)題。因?yàn)?,線程間的channel通信,已經(jīng)表達(dá)了線程間的數(shù)據(jù)依賴關(guān)系了,而go的調(diào)度器會(huì)給予妥善的處理。
另外,go語(yǔ)言提供的gc機(jī)制,以及對(duì)指針的保護(hù)式使用,可以大大減輕程序員的開(kāi)發(fā)壓力,提高開(kāi)發(fā)效率。
展望未來(lái),我期待go語(yǔ)言社區(qū)能夠提供更多的goroutine間的隔離機(jī)制。個(gè)人十分推崇erlang社區(qū)的脆崩哲學(xué),推動(dòng)應(yīng)用發(fā)生預(yù)期外行為時(shí),盡早崩潰,再fork出新進(jìn)程處理新的請(qǐng)求。對(duì)于協(xié)程機(jī)制,需要由程序員保證執(zhí)行的函數(shù)不會(huì)發(fā)生死循環(huán),導(dǎo)致線程卡死。如果能夠定制goroutine所執(zhí)行函數(shù)的最大CPU執(zhí)行時(shí)間,及所能使用的最大內(nèi)存空間,對(duì)于提升系統(tǒng)的魯棒性,大有裨益。
go可以在安卓運(yùn)行的,github.com/golang/mobile,這是用golang做移動(dòng)端開(kāi)發(fā)的庫(kù),
go是靜態(tài)編譯語(yǔ)言,不是腳本語(yǔ)言
環(huán)境配置好復(fù)雜,我不得不嘮叨幾句。
需要下載golang1.4rc版,下載ndk,然后編譯。 然后用go get 下載gobind這個(gè)工具, 然后,將寫好的代碼用gobind轉(zhuǎn)化下,然后使用特殊的編譯命令,將代碼編譯成.so文件,將生成的相關(guān)文件,放到android studio的項(xiàng)目中。然后java代碼中,利用jni調(diào)用引用的代碼。
... 好,接著往下看吧。
環(huán)境準(zhǔn)備
一臺(tái)Linux 64的機(jī)器
一個(gè)帶有AndroidStudioIDE的開(kāi)發(fā)機(jī)器
因?yàn)榄h(huán)境配置實(shí)在復(fù)雜,所以我們引入的docker。
docker pull codeskyblue/docker-goandroid
docker run --rm -ti codeskyblue/docker-goandroid bash
cd example; echo "view example projects
docker起來(lái)之后,什么就都配置好了,NDK啦,java啦,GO的環(huán)境變量了,等等,并且還預(yù)裝了vim,gradle,tmux,git,syncthing,svn
開(kāi)始寫代碼
寫代碼之前,先約定下目錄結(jié)構(gòu)
go的代碼都放在src/golib下,編譯使用make.bash編譯腳本,看下這個(gè)文件樹(shù)
.
|-- app.iml
|-- build.gradle
|-- libs/armeabi-v7a # go編譯生成的so文件
| `-- libgojni.so
|-- main.go_tmpl # 一個(gè)模板文件,先不用管它
|-- make.bash # 編譯腳本,用來(lái)生成.so和Java代碼
`-- src
|-- golib
| |-- hi
| | |-- go_hi?0?2?0?2?0?2 # 自動(dòng)生成的代碼
| | | `-- go_hi.go
| | `-- hi.go # 需要編寫的代碼
| `-- main.go
`-- main
|-- AndroidManifest.xml
|-- java
| |-- go # 自動(dòng)生成的代碼
| | |-- Go.java
| | |-- Seq.java
| | `-- hi
| | `-- Hi.java
| `-- me/shengxiang/gohello # 主要的邏輯代碼
| `-- MainActivity.java
`-- res
我已經(jīng)寫了一個(gè)例子,先直接搞下來(lái)
編譯下,試試行不行(就算不行問(wèn)題應(yīng)該也不大,因?yàn)榇髥?wèn)題都被我消滅了)
cd GoHello/app
./make.bash
../gradlew build
一切順利的話在build/outputs/apk下應(yīng)該可以看到app-debug.apk這個(gè)文件。(劇透下,這個(gè)文件只有800多K)
編譯好的我放到qiniu上了,可以點(diǎn)擊下載看看
下面可以嘗試改改,我拋磚引玉說(shuō)下
打開(kāi)hi.go這個(gè)文件
hi.go的內(nèi)容,比較簡(jiǎn)單,我們寫Go代碼主要就是這部分
// Package hi provides a function for saying hello.
package hi
import "fmt"
func Hello(name string) {
fmt.Printf("Hello, %s!\n", name)
return "(Go)World"
}
文件末尾添加下面這行代碼
func Welcome(name string) string {
return fmt.Sprintf("Welcome %s to the go world", name)
}
使用./make.bash重新編譯下
打開(kāi)MainActivity.java 修改下OnClickListener事件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String message = Hi.Welcome("yourname");
Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
}
});
編譯運(yùn)行下,把生成的apk安裝到手機(jī)上試試。
原理解讀(有興趣的接著看)
首先說(shuō)下gobind這個(gè)工具。
go_hi/go_hi.go這個(gè)文件時(shí)通過(guò)gobind這個(gè)工具生成的,用來(lái)配合一個(gè)簡(jiǎn)單的程序,生成.so文件
// go_hi.go
package go_hi
import (
"golang.org/x/mobile/bind/seq"
"example/hi"
)
func proxy_Hello(out, in *seq.Buffer) {
param_name := in.ReadUTF16()
hi.Hello(param_name)
}
func init() {
seq.Register("hi", 1, proxy_Hello)
}
這個(gè)簡(jiǎn)單的程序內(nèi)容是這樣的
// main.go
package main
import (
"golang.org/x/mobile/app"
_ "golang.org/x/mobile/bind/java"
_ "example/hi/go_hi"
)
func main() {
app.Run(app.Callbacks{})
}
src/MyActivity.java文件內(nèi)容是這樣的
import ...
import go.Go; // 引入Go這個(gè)包
import go.hi.Hi; // gobind生成的代碼
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Go.init(getApplicationContext()); // 初始化兩個(gè)線程
Hi.Hello("world");
}
}
其中有一句Go.init(...)這里再看go.Go這個(gè)包是什么樣子的
public final class Go {
// init loads libgojni.so and starts the runtime.
public static void init(Context context) {
... 判斷該函數(shù)是否該執(zhí)行的代碼 -- 省略 --
System.loadLibrary("gojni"); // gojni需要這句
new Thread("GoMain") {
public void run() {
Go.run(); // run()是一個(gè)native方法
}
}.start();
Go.waitForRun(); // 這個(gè)也是一個(gè)native方法
// 這部分可以理解為,啟動(dòng)了一個(gè)后臺(tái)線程不斷的接收結(jié)果到緩存中。
new Thread("GoReceive") {
public void run() { Seq.receive(); }
}.start();
}
private static boolean running = false;
private static native void run();
private static native void waitForRun();
}
MyActivity.java中還有段代碼是 Hi.Hello("world");,打開(kāi)Hi.java路徑在src/go/hi/Hi.java,這個(gè)文件也是gobind生成的,是用來(lái)給java方便的調(diào)用.so文件
// Hi.java
// File is generated by gobind. Do not edit.
package go.hi;
import go.Seq;
public abstract class Hi {
private Hi() {} // uninstantiable
public static void Hello(String name) {
go.Seq _in = new go.Seq();
go.Seq _out = new go.Seq();
_in.writeUTF16(name);
Seq.send(DESCRIPTOR, CALL_Hello, _in, _out); // 下面接著說(shuō)
}
private static final int CALL_Hello = 1;
private static final String DESCRIPTOR = "hi";
}
Seq.send這部分實(shí)際上最終調(diào)用的是一段go代碼
func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) {
fn := seq.Registry[descriptor][code]
in := new(seq.Buffer)
if reqlen 0 {
in.Data = (*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen]
}
out := new(seq.Buffer)
fn(out, in)
seqToBuf(res, reslen, out)
}
轉(zhuǎn)載僅供參考,版權(quán)屬于原作者。祝你愉快,滿意請(qǐng)采納哦
Go語(yǔ)言將支持Android
概述
我們建議將Go語(yǔ)言引入Android平臺(tái),重點(diǎn)是用Go語(yǔ)言編寫游戲程序,API將在Android
NDK中定義。
背景
Android平臺(tái)被設(shè)定為一個(gè)多應(yīng)用操作系統(tǒng),一個(gè)相對(duì)于傳統(tǒng)UNIX系統(tǒng)來(lái)說(shuō)更依賴于網(wǎng)絡(luò)庫(kù)和服務(wù)的操作系統(tǒng),這意味著我們將為Go運(yùn)行時(shí)開(kāi)放更多的API。
將Golang帶入Android平臺(tái)是一件非常糟糕的事情,因?yàn)锳ndroid本身是Java構(gòu)建的并且擁有巨量的API,任何試圖將這些API用Go替代的結(jié)果將會(huì)導(dǎo)致一個(gè)非常糟糕的結(jié)果。無(wú)論是手工建立包裝類還是自動(dòng)建立都會(huì)導(dǎo)致程序運(yùn)行非常緩慢。
然而如果使用基于C的API,通過(guò)AndroidNDK來(lái)編寫一些游戲類程序可能會(huì)帶來(lái)意想不到的收獲。
提議
在Golang
1.4周期中,Android平臺(tái)的編譯將納入Go的源,并且包含Android支持的cgo(由Elias
Naur貢獻(xiàn))。Dalvik-loadable
.so外部連接文件的支持也會(huì)由Android
NDK提供。
在發(fā)布上我們?nèi)匀惶峁┙徊嫫脚_(tái)編譯,并且支持在Linux主機(jī)上adb工具在Android設(shè)備上測(cè)試程序。
我們將會(huì)提供一個(gè)名叫g(shù)o.mobile的子版本,包含:
1、通過(guò)Android
NDK對(duì)OpenGL,OpenSL和OpenMAX的支持。
2、一個(gè)Java-Go的工具,支持通過(guò)Java調(diào)用Go的Package,所以許多游戲的菜單UI可以通過(guò)標(biāo)準(zhǔn)SDK構(gòu)建。(有可能會(huì)支持通過(guò)其他語(yǔ)言調(diào)用Go,比如Objective-C。)
3、集成AndroidStudio
新特性將會(huì)在Go1.4穩(wěn)定版中提供。
Go 語(yǔ)言是一個(gè)試圖結(jié)合動(dòng)態(tài)類型和靜態(tài)類型,編譯效率和安全性以及語(yǔ)言的易用性等眾多特性與一體的一種嘗試。他的另一個(gè)目標(biāo)是支持現(xiàn)代的基于網(wǎng)絡(luò)的、基于多核的計(jì)算。最后,Go 還是非??斓?,他可以在單個(gè)計(jì)算機(jī)上僅僅花費(fèi)幾秒時(shí)間構(gòu)建一個(gè)龐大的應(yīng)用程序。使用GO語(yǔ)言開(kāi)發(fā)android語(yǔ)言步驟如下:
1. 下載安裝Go語(yǔ)言(版本1.5+)
Golang鏈接
回到頂部
2. 下載安裝 gomobile
下載
$ go get golang.org/x/mobile/cmd/gomobile
安裝(需要等待幾分鐘)
gomobile init
回到頂部
3. Golang開(kāi)發(fā)手機(jī)應(yīng)用有兩種方式
a. 原生應(yīng)用開(kāi)發(fā)
$ go get -d golang.org/x/mobile/example/basic
主要應(yīng)用領(lǐng)域:
應(yīng)用控制管理與配置
OpenGL ES 2 綁定
Asset 管理
Event 管理
試驗(yàn)中的包,含有OpenAL 綁定、音頻、字體、圖形以及運(yùn)動(dòng)傳感器
Android開(kāi)發(fā)
$ gomobile build -target=android golang.org/x/mobile/example/basic
此命令會(huì)生成名為basic的apk安裝包
$ gomobile install golang.org/x/mobile/example/basic
此命令將安裝apk包到已連接的android設(shè)備