通过erlang:memory()查看节点内存占用总览,需要通过静态和动态两个维度对内存进行考核:

  • 静态: 各类内存占用比例,是否有某种类的内存占用了节点总内存的绝大部分
  • 动态: 各类内存增长特性,如增长速度,或是否长期增长而不回收(atom除外)

找出有疑似内存泄露的种类后,再进行下一步分析

atom

atom不会被GC,这意味着我们应该对atom内存增长更加重视而不是忽略。在编写代码时,尽量避免动态生成atom,因为一旦你的输入源不可靠或受到攻击(特别针对网络消息),atom内存增长可能导致节点crash。可以考虑将atom生成函数替换为更安全的版本:

阅读全文 »

Unicode基础

编码方式

定义字符集中每个字符的codepoint(数字编码)

  • ASCII: 不用多说,编码空间为7位(0-127)
  • ISO 8859-1: 又称Latin-1,以ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个字母及符号。编码空间为8位(0-255)
  • UCS-2: 16位编码空间 又称基本多文种平面或零平面
  • UCS-4: 32位编码空间 在UCS-2基本上,加入辅助平面(目前有16个辅助平面,至少需要21位)
  • 注1: UCS(Universal Character Set, 通用字符集)
  • 注2: 以上四种编码都是向前兼容的,通常我们所说的Unicode编码指UCS-2和UCS-4,目前广泛运用的是UCS-2
阅读全文 »

上篇,结合Erlang,对Docker的实际应用进一步理解。并探索将Docker应用到Erlang集群的方案。

简单Docker交互

下面是个简单的echo server:

-module(server_echo).
-export([start/0]).

start() ->
     io:format("SERVER Trying to bind to port 2345\n"),
     {ok, Listen} = gen_tcp:listen(2345, [ binary
                                         , {packet, 0}
                                         , {reuseaddr, true}
                                         , {active, true}
                                         ]),
     io:format("SERVER Listening on port 2345\n"),
     accept(Listen).

 accept(Listen) ->
     {ok, Socket} = gen_tcp:accept(Listen),
     WorkerPid = spawn(fun() -> echo(Socket) end),
     gen_tcp:controlling_process(Socket, WorkerPid),
     accept(Listen).

 echo(Socket) ->
     receive
         {tcp, Socket, Bin} ->
             io:format("SERVER Received: ~p\n", [Bin]),
             gen_tcp:send(Socket, Bin),
             echo(Socket);
         {tcp_closed, Socket} ->
             io:format("SERVER: The client closed the connection\n")
     end.

简单起见,我们直接用telnet命令对echo server进行测试。现在,考虑如何在Docker容器中运行echo server。

阅读全文 »

一. 理解 Docker

Docker是一种轻量级的虚拟化方案,虚拟化本身可以从两个角度来理解:

  • 隔离性:可传统的虚拟机类似,资源隔离(进程,网络,文件系统等)可用于更好地利用物理机。Docker本身虚拟化的开销非常小,这也是它相对于传统虚拟机最大的优势
  • 一致性:同样一份虚拟机镜像,可以部署在不同的平台和物理机上,并且内部的环境,文件,配置是一致的,这在当前多样化的平台,日益复杂的配置/部署流程,以及团队和团队间的协作中,有着重要的意义。想象一下,当你用Docker提交代码时,你做的事情跟以前是完全不同的。在以前我们只是把代码提交上去,而在Docker中我们把整台计算机(虚拟机)提交上去。为什么Docker这么火,就是因为它帮助开发者很简单的就让自己的开发环境跟生产环境一致。环境的标准化,意味着目录、路径、配置文件、储存用户名密码的方式、访问权限、域名等种种细节的一致和差异处理的标准化。
阅读全文 »

问题描述

有十几个玩家报告被回档,几小时到一两天不等

问题背景

在我们的集群架构中,集群有若干GS节点,每个GS节点可部署N个GS服务器,整个集群所有的玩家进程注册于cluster,我们通过为每个服开一个player_mgr来维护单服玩家状态,player_mgr维护{player_id, agent_pid, player_pid}三元组,用户处理多点登录,单服逻辑,离线玩家LRU等。cluster本身只提供服务注册/注销,如果做服务替换(如agent),确保服务的唯一性(如player)应该由外部逻辑来确保,cluster并不知晓内部各种服务的特性。player进程启动/终止时,会向player_mgr和cluster分别注册/注销自己。

阅读全文 »

一. 简介

goa是基于微服务的go语言框架,能够有效帮助开发人员快速开发基于微服务的系统。它通过DSL和代码生成器来生成样板代码和辅助套件(如文档,客户端模块,客户端工具等)。这些生成数据均基于服务的设计描述,goa遵循单一数据源(Single Source of Truth, SSOT)原则,任何对设计的改变,都将自动反映到系统各处,

goa可以分为三个部分:

  • goa的设计语言是内置DSL,用于描述微服务的设计
  • goa代码生成器,用于根据DSL描述生成代码模块,辅助工具,和文档等
  • goa利用生成代码和用户代码来实现一个服务,并提供一个完全可插拨的框架
阅读全文 »

一. 依赖管理

1. 包依赖和源码依赖

Rebar3支持两种依赖:

{deps,[
  %% 包依赖
  rebar,
  {rebar,"1.0.0"},
  {rebar, {pkg, rebar_fork}}, % rebar app under a different pkg name
  {rebar, "1.0.0", {pkg, rebar_fork}},
  %% 源码依赖
  {rebar, {git, "git://github.com/erlang/rebar3.git"}},
  {rebar, {git, "http://github.com/erlang/rebar3.git"}},
  {rebar, {git, "https://github.com/erlang/rebar3.git"}},
  {rebar, {git, "git@github.com:erlang/rebar3.git"}},
  {rebar, {hg, "https://othersite.com/erlang/rebar3"}},
  {rebar, {git, "git://github.com/erlang/rebar3.git", {ref, "aef728"}}},
  {rebar, {git, "git://github.com/erlang/rebar3.git", {branch, "master"}}},
  {rebar, {git, "git://github.com/erlang/rebar3.git", {tag, "3.0.0"}}}
  ]}
阅读全文 »

不定参数&多返回值

不定参数只能是最后一个参数,它实际上是数组切片参数的语法糖:

// 语法糖 相当于 func myfunc(args []interface{})
func myfunc(args ...interface{}){
    for _, arg := range args {
    fmt.Println(arg)
}

// 参数会被打包为 []{arg1,arg2,arg3}
myfunc(arg1,arg2,arg3)
// 要完成可变参数的完美传递 需要用...将Slice打散
func myfunc2(args ...interface{})
    // 此时args已经是Slice 如果不打散将作为一个参数 不能完美传递
    myfunc(args)
    // 编译器在此处有优化 最终会直接将args传入 不会打散再打包 参考: http://www.jianshu.com/p/94710d8ab691
    myfunc(args...) 
end

多返回值为函数提供了更大的便利性,无需传引用或者专门构造返回值结构体,并且在错误处理方面也更简便,在前面的示例代码中已经初尝甜头。

阅读全文 »

探索Go类型扩展,类和继承,以及接口的用法和实现。

面向对象

1. 类型扩展

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

// 定义了一个新类型:Integer,与int不能直接比较/赋值
type Integer int

func (a *Integer) Add(b Integer) Integer{
return *a + b
}

2. 类和继承

在Go中,传统意义上的类相当于是对struct的类型扩展:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type Rect struct{
x, y float64
w, l float64
}

func (r Rect) Area() float64{
return r.l * r.w
}

func main(){
c := Rect{1,1,4,4}
fmt.Println(c.Area())
}
0%