用go和zk实现一个简单的分布式server
golang的zk客户端
最近打算写个简单的配置中心,考虑到实现便捷性,语言选择了go,由于其中计划用到zk,就调研了下golang的zk客户端,并实现了个简单的分布式server。最终找到了两个,地址如下:
- gozk:https://wiki.ubuntu.com/gozk
- go-zookeeper:https://github.com/samuel/go-zookeeper
由于gozk的文档不如后者,且代码没在gihub上,所以就直接选择了后者。go-zookeeper文档还是比较全面的:文档
基本操作测试
这里默认大家已经了解zk的用处和基本用法了,如果还不了解可以参看:官方文档或中文文档
下边我们先来写个简单的例子来测试下基本的操作:
package main
/**
客户端doc地址:github.com/samuel/go-zookeeper/zk
**/
import (
"fmt"
zk "github.com/samuel/go-zookeeper/zk"
"time"
)
/**
* 获取一个zk连接
* @return {[type]}
*/
func getConnect(zkList []string) (conn *zk.Conn) {
conn, _, err := zk.Connect(zkList, 10*time.Second)
if err != nil {
fmt.Println(err)
}
return
}
/**
* 测试连接
* @return
*/
func test1() {
zkList := []string{"localhost:2183"}
conn := getConnect(zkList)
defer conn.Close()
conn.Create("/go_servers", nil, 0, zk.WorldACL(zk.PermAll))
time.Sleep(20 * time.Second)
}
/**
* 测试临时节点
* @return {[type]}
*/
func test2() {
zkList := []string{"localhost:2183"}
conn := getConnect(zkList)
defer conn.Close()
conn.Create("/testadaadsasdsaw", nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
time.Sleep(20 * time.Second)
}
/**
* 获取所有节点
*/
func test3() {
zkList := []string{"localhost:2183"}
conn := getConnect(zkList)
defer conn.Close()
children, _, err := conn.Children("/go_servers")
if err != nil {
fmt.Println(err)
}
fmt.Printf("%v \n", children)
}
func main() {
test3()
}
上边的代码里边我们测试了golang对zk的连接、创建节点、或取节点操作,在下边的分布式server中将用到这几个操作。
简单的分布式server
目前分布式系统已经很流行了,一些开源框架也被广泛应用,如dubbo、Motan等。对于一个分布式服务,最基本的一项功能就是服务的注册和发现,而利用zk的EPHEMERAL节点则可以很方便的实现该功能。EPHEMERAL节点正如其名,是临时性的,其生命周期是和客户端会话绑定的,当会话连接断开时,节点也会被删除。下边我们就来实现一个简单的分布式server:
server:
- 服务启动时,创建zk连接,并在go_servers节点下创建一个新节点,节点名为"ip:port",完成服务注册
- 服务结束时,由于连接断开,创建的节点会被删除,这样client就不会连到该节点
client:
- 先从zk获取go_servers节点下所有子节点,这样就拿到了所有注册的server
- 从server列表中选中一个节点(这里只是随机选取,实际服务一般会提供多种策略),创建连接进行通信
这里为了演示,我们每次client连接server,获取server发送的时间后就断开。主要代码如下:
zkutil:用来处理zk操作
func GetConnect() (conn *zk.Conn, err error) {
conn, _, err = zk.Connect(hosts, timeOut*time.Second)
if err != nil {
fmt.Println(err)
}
return
}
func RegistServer(conn *zk.Conn, host string) (err error) {
_, err = conn.Create("/go_servers/"+host, nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
return
}
func GetServerList(conn *zk.Conn) (list []string, err error) {
list, _, err = conn.Children("/go_servers")
return
}
server: server端操作
func main() {
go starServer("127.0.0.1:8897")
go starServer("127.0.0.1:8898")
go starServer("127.0.0.1:8899")
a := make(chan bool, 1)
<-a
}
func starServer(port string) {
tcpAddr, err := net.ResolveTCPAddr("tcp4", port)
fmt.Println(tcpAddr)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
//注册zk节点q
conn, err := example.GetConnect()
if err != nil {
fmt.Printf(" connect zk error: %s ", err)
}
defer conn.Close()
err = example.RegistServer(conn, port)
if err != nil {
fmt.Printf(" regist node error: %s ", err)
}
for {
conn, err := listener.Accept()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s", err)
continue
}
go handleCient(conn, port)
}
fmt.Println("aaaaaa")
}
func handleCient(conn net.Conn, port string) {
defer conn.Close()
daytime := time.Now().String()
conn.Write([]byte(port + ": " + daytime))
}
client: 客户端操作
func main() {
for i := 0; i < 100; i++ {
startClient()
time.Sleep(1 * time.Second)
}
}
func startClient() {
// service := "127.0.0.1:8899"
//获取地址
serverHost, err := getServerHost()
if err != nil {
fmt.Printf("get server host fail: %s \n", err)
return
}
fmt.Println("connect host: " + serverHost)
tcpAddr, err := net.ResolveTCPAddr("tcp4", serverHost)
checkError(err)
conn, err := net.DialTCP("tcp", nil, tcpAddr)
checkError(err)
defer conn.Close()
_, err = conn.Write([]byte("timestamp"))
checkError(err)
result, err := ioutil.ReadAll(conn)
checkError(err)
fmt.Println(string(result))
return
}
func getServerHost() (host string, err error) {
conn, err := example.GetConnect()
if err != nil {
fmt.Printf(" connect zk error: %s \n ", err)
return
}
defer conn.Close()
serverList, err := example.GetServerList(conn)
if err != nil {
fmt.Printf(" get server list error: %s \n", err)
return
}
count := len(serverList)
if count == 0 {
err = errors.New("server list is empty \n")
return
}
//随机选中一个返回
r := rand.New(rand.NewSource(time.Now().UnixNano()))
host = serverList[r.Intn(3)]
return
}
我们先启动server,可以看到有三个节点注册到zk:
再启动client,可以看到每次client都会随机连接到一个节点进行通信:
至此,我们的分布式server就实现了,够简单吧。当然,实际项目中这样的实现是远远不够的,有兴趣的可以研究下一些开源的框架。
相关代码:github
用go和zk实现一个简单的分布式server的更多相关文章
- ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁
作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...
- 编写一个简单的Web Server
编写一个简单的Web Server其实是轻而易举的.如果我们只是想托管一些HTML页面,我们可以这么实现: 在VS2013中创建一个C# 控制台程序 编写一个字符串扩展方法类,主要用于在URL中截取文 ...
- 一个简单的mock server
在前后端分离的项目中, 前端无需等后端接口提供了才调试, 后端无需等第三方接口提供了才调试, 基于“契约”,可以通过mock server实现调试, 下面是一个简单的mock server,通过pyt ...
- 用Python编写一个简单的Http Server
用Python编写一个简单的Http Server Python内置了支持HTTP协议的模块,我们可以用来开发单机版功能较少的Web服务器.Python支持该功能的实现模块是BaseFTTPServe ...
- 写了一个简单的CGI Server
之前看过一些开源程序的源码,也略微知道些Apache的CGI处理程序架构,于是用了一周时间,用C写了一个简单的CGI Server,代码算上头文件,一共1200行左右,难度中等偏上,小伙伴可以仔细看看 ...
- 超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务
来自:JavaGuide Github 地址:https://github.com/Snailclimb/springboot-integration-examples 目录: 使用 SpringBo ...
- 创建一个简单的 MDM server(1)
前提:已获得 APNS 证书 ,已完毕 MDM 配置描写叙述文件的制作.请參考< MDM 证书申请流程 >一文和<配置MDM Provisioning Profile>. 环境 ...
- 如何写一个简单的Web Server(一)
在本篇博文中我将介绍如何写一个Web Server.博文中大部分资料我是参考的这篇文章(http://www.linuxhowtos.org/C_C++/socket.htm),英文不错的同学可以 ...
- 使用redis设计一个简单的分布式锁
最近看了有关redis的一些东西,了解了redis的一下命令,就记录一下: redis中的setnx命令: 关于redis的操作命令,我们一般会使用set,get等一系列操作,数据结构也有很多,这里我 ...
随机推荐
- 【踩坑】mysql LIMIT 与 ORDER BY位置问题
今天在重做iblog项目后台时,当测试功能,发现后台报错,位置在 MyBatis 向 MySQL 取数据时的语法问题,如下: SELECT * FROM article WHERE authorID ...
- 前端WEB编辑器-------webstrom
欲先善其事,必先利其器,如题.看到网上一篇介绍webstrom的文章,觉得功能确实强大,也知道为什么阿里巴巴的前端传到github上的文件为啥都有一个 .idea 文件,(传说淘宝内部推荐写js用we ...
- 数据库(DBUtils)
DBUtils和连接池 今日内容介绍 u DBUtils u 连接池 第1章 DBUtils 如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC开发,本案例我们讲采用apache c ...
- scanner/portscan/syn
msf > use auxiliary/scanner/portscan/syn msf auxiliary(syn) > set RHOSTS 172.16.21.170 /*设定rh ...
- koa2实现文件上传服务
使用方法 方法一: 使用中间介 koa-body 方法二: 自己写个借口去接收数据流并保存 方法三: 使用 koa-body 接受文件,自己写个接口做文件保存或处理等操作 这里简单记录方法三 app. ...
- HDU5269 字典树
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5269 ,BestCoder Round #44的B题,关于字典树的应用. 比赛的时候没想出做法,现在补 ...
- [VC]VC实现开机自动运行程序
有时候,我们需要在计算机启动的时候就启动某些程序,不要人干预.这里,提供一种让程序开机自动运行的方法.见下面代码: BOOL CXXX::SetAutoRun(CString strPath) { C ...
- Codeforces Round #324 (Div. 2) A B C D E
A,水题不多说. #include<bits/stdc++.h> using namespace std; //#define LOCAL int main() { #ifdef LOCA ...
- 【BZOJ1057】[ZJOI2007] 棋盘制作(单调栈的运用)
点此看题面 大致题意: 给你一个\(N*M\)的\(01\)矩阵,要求你分别求出最大的\(01\)相间的正方形和矩形(矩形也可以是正方形),并输出其面积. 题解 这题第一眼看去没什么思路,仔细想想,能 ...
- 用virtualenv构建一个新的python环境,这个新的环境在这个创建的文件夹下
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143271210830032 ...