├── README.md ├── bear.go └── sdk └── bear.php /README.md: -------------------------------------------------------------------------------- 1 | bear-分布式ID生成服务 2 | ============ 3 | 4 | sdk 目前只有 php 版本: 5 | https://github.com/nixuehan/bear/tree/master/sdk 6 | 7 | 8 | 按照这个格式生成全局唯一id: 毫秒40bit + 机房2bit + 机器6bit + 业务8bit + 序列号7bit 9 | 10 | 下载符合自己机器的版本,运行即可 11 | 12 | 13 | $ ./bear 14 | 15 | 16 | ###支持的参数: 17 | 18 | -h string 19 | Bound IP. default:localhost (default "localhost") 20 | 21 | -p string 22 | port. default:8384 (default "8384") 23 | 24 | -r int 25 | server room. default:1 (default 1) 26 | 27 | -s int 28 | server id. default:1 (default 1) 29 | 30 | 31 | 32 | ###其他不懂 看源代码吧 -------------------------------------------------------------------------------- /bear.go: -------------------------------------------------------------------------------- 1 | //author 逆雪寒 2 | //version 0.6.1 3 | //分布式id生成服务 4 | package main 5 | 6 | import( 7 | "net" 8 | "fmt" 9 | "log" 10 | "flag" 11 | "io" 12 | "time" 13 | "bytes" 14 | "sync" 15 | "encoding/binary" 16 | "math/rand" 17 | "runtime" 18 | ) 19 | 20 | var host = flag.String("h","localhost","Bound IP. default:localhost") 21 | var port = flag.String("p","8384","port. default:8384") 22 | var room = flag.Int("r",1,"server room. default:1") 23 | var serverId = flag.Int("s",1,"server id. default:1") 24 | 25 | type Fish struct { 26 | millis int64 27 | increase int64 28 | } 29 | var Pool map[uint8]Fish 30 | 31 | type CommandCode uint8 32 | type Status uint8 33 | 34 | const ( 35 | REQ_MAGIC = 0x83 36 | RES_MAGIC = 0x84 37 | B_LEN = 4 38 | ) 39 | const ( 40 | GET = CommandCode(0x01) 41 | ) 42 | const ( 43 | SUCCESS = Status(0x33) 44 | FAIL = Status(0x44) 45 | ) 46 | 47 | var handlers = map[CommandCode]func(Request) Response{ 48 | GET : GetIdHandler, 49 | } 50 | 51 | func init() { 52 | Pool = make(map[uint8]Fish) 53 | } 54 | 55 | func must(e error) { 56 | if e != nil { 57 | panic(e) 58 | } 59 | } 60 | 61 | func random() int{ 62 | x := rand.Intn(9) 63 | if x == 0 { 64 | x = 1 65 | } 66 | return x 67 | } 68 | 69 | func ERROR(w io.WriteCloser){ 70 | defer func(){must(w.Close())}() 71 | 72 | var body = make([]byte,2) 73 | body[0] = RES_MAGIC 74 | body[1] = byte(FAIL) 75 | w.Write(body) 76 | } 77 | 78 | type Response struct { 79 | Body []byte 80 | } 81 | 82 | func(this *Response)HeaderBodyFill(status Status,data interface{}) []byte{ 83 | var body = make([]byte,2) 84 | body[0] = RES_MAGIC 85 | body[1] = byte(SUCCESS) 86 | 87 | buf := bytes.NewBuffer([]byte{}) 88 | binary.Write(buf, binary.BigEndian,data) 89 | body = append(body,buf.Bytes()...) 90 | return body 91 | } 92 | 93 | func(this *Response)Write(w io.WriteCloser) (int,error){ 94 | defer func(){must(w.Close())}() 95 | return w.Write(this.Body) 96 | } 97 | 98 | func GetIdHandler(req Request) Response{ 99 | now := time.Now() 100 | nanos := now.UnixNano() 101 | millis := nanos / 1000000 102 | 103 | workId := req.WordId 104 | //毫秒41 + 机房2 + 机器5 + 业务8+ 序列号7 105 | id := (millis << 22) + (int64(*room) << 20) + (int64(*serverId) << 15) + (int64(workId) << 7) 106 | 107 | var uniqueID int64 108 | 109 | req.Dog.Lock() 110 | if v,ok := Pool[workId]; ok { 111 | if v.millis == millis { 112 | v.increase += 1 113 | }else{ 114 | v.millis = millis 115 | v.increase = int64(random()) 116 | } 117 | uniqueID = id + v.increase 118 | }else{ 119 | Pool[workId] = Fish{millis,1} 120 | uniqueID = id + 1 121 | } 122 | req.Dog.Unlock() 123 | 124 | res := Response{} 125 | res.Body = res.HeaderBodyFill(SUCCESS,uniqueID) 126 | 127 | return res 128 | } 129 | 130 | func flying(reqChan chan Request) { 131 | for{ 132 | req := <- reqChan 133 | if kfc,ok := handlers[req.CommandCode]; ok { 134 | res := kfc(req) 135 | res.Write(req.Conn) 136 | }else{ 137 | log.Printf("commandCode error:%v",req.CommandCode) 138 | ERROR(req.Conn) 139 | } 140 | } 141 | } 142 | 143 | type Request struct { 144 | CommandCode CommandCode 145 | WordId uint8 146 | Conn net.Conn 147 | Dog *sync.Mutex 148 | } 149 | 150 | func IO(conn net.Conn,msgBytes []byte) (Request,error){ 151 | if msgBytes[0] != REQ_MAGIC { 152 | ERROR(conn) 153 | return Request{},fmt.Errorf("bad 0x%x",msgBytes[0]) 154 | } 155 | 156 | req := Request{} 157 | req.CommandCode = CommandCode(msgBytes[1]) 158 | req.WordId = uint8(msgBytes[2]) //业务id 159 | return req,nil 160 | } 161 | 162 | func handler(r io.Reader,conn net.Conn,reqChan chan Request) error{ 163 | buf := make([]byte,B_LEN) 164 | if _,e := io.ReadFull(r,buf);e != nil { 165 | return e 166 | } 167 | 168 | lock := &sync.Mutex{} 169 | 170 | if req,e := IO(conn,buf);e == nil { 171 | req.Conn = conn 172 | req.Dog = lock 173 | reqChan <- req 174 | }else{ 175 | log.Printf("Format error:%s",e) 176 | } 177 | return io.EOF 178 | } 179 | 180 | func waitForYou(ls net.Listener) { 181 | var reqChan = make(chan Request) 182 | 183 | go flying(reqChan) 184 | 185 | for { 186 | conn,e := ls.Accept() 187 | if e == nil { 188 | go handler(conn,conn,reqChan) 189 | }else{ 190 | log.Printf("Error accepting from %s",ls) 191 | } 192 | } 193 | } 194 | 195 | func main() { 196 | defer func() { 197 | if err := recover();err != nil{ 198 | log.Printf("Fatal error:%v",err) 199 | } 200 | }() 201 | 202 | runtime.GOMAXPROCS(runtime.NumCPU()) 203 | 204 | flag.Parse() 205 | log.SetFlags(log.LstdFlags) 206 | ls,e := net.Listen("tcp",*host + ":" + *port) 207 | if e != nil { 208 | log.Fatalf("Error bound: %s",e) 209 | } 210 | waitForYou(ls) 211 | } -------------------------------------------------------------------------------- /sdk/bear.php: -------------------------------------------------------------------------------- 1 | host = $host; 18 | $this->port = $port; 19 | $this->timeout = $time; 20 | } 21 | 22 | public function __clone(){ 23 | trigger_error('Clone is not allow!',E_USER_ERROR); 24 | } 25 | 26 | private function ordering($command,$workID) { 27 | $fp=fsockopen($this->host,$this->port,$errno,$err,$this->timeout); 28 | $data = ''; 29 | if($fp){ 30 | fwrite($fp,pack("N",(hexdec("0x83") << 24) + (hexdec($command) << 16) + ($workID << 8))); 31 | stream_set_blocking($fp,true); 32 | stream_set_timeout($fp,$this->timeout); 33 | $info=stream_get_meta_data($fp); 34 | while((!feof($fp)) && (!$info['timed_out'])){ 35 | $data.=fgets($fp,11); 36 | } 37 | } 38 | return $data; 39 | } 40 | 41 | private function getBody($dataBytes) { 42 | $size = strlen($dataBytes) - 2; 43 | $data = unpack("Cmagic/Cstatus/C$size",$dataBytes); 44 | if($data['magic'] != self::RES_MAGIC || $data['status'] != self::SUCCESS) { 45 | return false; 46 | } 47 | $data = array_slice($data,2); 48 | return $data; 49 | } 50 | 51 | public function factory($host = 'localhost',$port = 8384,$time = 1) { 52 | if(is_null(self::$instance)) { 53 | self::$instance = new self($host,$port,$time); 54 | } 55 | return self::$instance; 56 | } 57 | 58 | /** 59 | * 获取全局id. 60 | * @param int8 $workID 业务id 范围 1 - 255 比如 1:用户 2:动态 又或者 1:微博系统 2:招聘系统 61 | * @return bool or int64 62 | */ 63 | public function ID($workID = 1) { 64 | if($workID < 1 || $workID > 255) { 65 | return false; 66 | } 67 | 68 | $dataBytes = $this->ordering(self::GET_ID_COMMAMD,$workID); 69 | $bodyBytes = $this->getBody($dataBytes); 70 | 71 | if($bodyBytes === false) { 72 | return false; 73 | } 74 | 75 | for($id=0,$i = 56,$j=0;$i >= 0;$i = $i - 8,$j++) { 76 | $id += $bodyBytes[$j] << $i; 77 | } 78 | return $id; 79 | } 80 | 81 | /** 82 | * 获取业务id. 83 | * @param int64 $id 84 | * @return bool or int8 85 | */ 86 | public function workID($id) { 87 | $workID = ($id >> 7) & 0xFF; 88 | return $workID; 89 | } 90 | 91 | /** 92 | * 获取ID生成时间戳 93 | * @param int64 $id 94 | * @return int 95 | */ 96 | public function time($id) { 97 | $millis = (($id >> 22) & 0x1FFFFFFFFFF) / 1000; 98 | return (int)$millis; 99 | } 100 | } 101 | 102 | // //example 103 | // $bear = Bear::factory("127.0.0.1"); 104 | // $id = $bear->ID(233); 105 | // $a = $bear->time($id); 106 | 107 | 108 | --------------------------------------------------------------------------------