golang的zk客户端

最近打算写个简单的配置中心,考虑到实现便捷性,语言选择了go,由于其中计划用到zk,就调研了下golang的zk客户端,并实现了个简单的分布式server。最终找到了两个,地址如下:

由于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:

  1. 服务启动时,创建zk连接,并在go_servers节点下创建一个新节点,节点名为"ip:port",完成服务注册
  2. 服务结束时,由于连接断开,创建的节点会被删除,这样client就不会连到该节点

client:

  1. 先从zk获取go_servers节点下所有子节点,这样就拿到了所有注册的server
  2. 从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的更多相关文章

  1. ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁

    作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...

  2. 编写一个简单的Web Server

    编写一个简单的Web Server其实是轻而易举的.如果我们只是想托管一些HTML页面,我们可以这么实现: 在VS2013中创建一个C# 控制台程序 编写一个字符串扩展方法类,主要用于在URL中截取文 ...

  3. 一个简单的mock server

    在前后端分离的项目中, 前端无需等后端接口提供了才调试, 后端无需等第三方接口提供了才调试, 基于“契约”,可以通过mock server实现调试, 下面是一个简单的mock server,通过pyt ...

  4. 用Python编写一个简单的Http Server

    用Python编写一个简单的Http Server Python内置了支持HTTP协议的模块,我们可以用来开发单机版功能较少的Web服务器.Python支持该功能的实现模块是BaseFTTPServe ...

  5. 写了一个简单的CGI Server

    之前看过一些开源程序的源码,也略微知道些Apache的CGI处理程序架构,于是用了一周时间,用C写了一个简单的CGI Server,代码算上头文件,一共1200行左右,难度中等偏上,小伙伴可以仔细看看 ...

  6. 超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务

    来自:JavaGuide Github 地址:https://github.com/Snailclimb/springboot-integration-examples 目录: 使用 SpringBo ...

  7. 创建一个简单的 MDM server(1)

    前提:已获得 APNS 证书 ,已完毕 MDM 配置描写叙述文件的制作.请參考< MDM 证书申请流程 >一文和<配置MDM Provisioning Profile>. 环境 ...

  8. 如何写一个简单的Web Server(一)

      在本篇博文中我将介绍如何写一个Web Server.博文中大部分资料我是参考的这篇文章(http://www.linuxhowtos.org/C_C++/socket.htm),英文不错的同学可以 ...

  9. 使用redis设计一个简单的分布式锁

    最近看了有关redis的一些东西,了解了redis的一下命令,就记录一下: redis中的setnx命令: 关于redis的操作命令,我们一般会使用set,get等一系列操作,数据结构也有很多,这里我 ...

随机推荐

  1. 项目开发bug记录

    项目开发中遇到了一个问题,类中出现未知属性 ‘ $jacocoData ’,准确的来说,实际上在集成测试阶段,系统自动运行测试用例时,抛出来的异常提示信息,但是在开发阶段是不存在的.这个问题是以前没有 ...

  2. smarty基本用法

    简介: 1.smarty语法:它是php的一种模板引擎   它的设计特点是:业务逻辑与显示逻辑分离 Smarty的标签都是使用定界符{ }括起来注释:{* 我是Smarty的注释内容 *} <u ...

  3. mysql登陆远程数据库

    1.登陆mysql 2.e mysql; 3.比如用户名密码为root/root. 你想root使用root从任何主机连接到mysql服务器的话. @’ ’后面加ip地址一般般为localhost或者 ...

  4. 零基础逆向工程28_Win32_02_事件_消息_消息处理函数

    1 第一个图形界面程序 步骤1:创建Windows应用程序 选择空项目 步骤2:在新建项窗口中选C++代码文件 创建一个新的cpp文件 步骤3:在新的cpp文件中添加:#include <Win ...

  5. Linux命令行环境与桌面环境护切换

    1.前言 在大部分情况下,我们在使用Linux时习惯使用命令行环境,但是有时候也还是会使用到安装桌面环境,所以在这里介绍一下如何给没有安装桌面环境的系统安装桌面环境.以Centos 6.5 为例演示一 ...

  6. NIO(一)缓冲区

    I/O的基本概念 同步和异步的概念: 所谓的同步就是在发出一个请求的时候,如果没有得到结果,就不返回.即调用者主动等待返回结果. 所谓的异步:调用之后直接返回结果,一般通过回调函数来处理这个应用. 阻 ...

  7. CentOS下内核TCP参数优化配置详解

    主动关闭的一方在发送最后一个ACK后就会进入TIME_WAIT状态,并停留2MSL(Max Segment LifeTime)时间,这个是TCP/IP必不可少的. TCP/IP的设计者如此设计,主要原 ...

  8. linux 命令——4 mkdir (转)

    linux mkdir 命令用来创建指定的名称的目录,要求创建目录的用户在当前目录中具有写权限,并且指定的目录名不能是当前目录中已有的目录. 1.命令格式: mkdir [选项] 目录... 2.命令 ...

  9. fetch用法说明

    语法说明 fetch(url, options).then(function(response) { // handle HTTP response }, function(error) { // h ...

  10. SecureCRT连接Linux

    一.服务端 1.在linux上安装openssh-server服务,并确认打开了22监听端口 1)安装openssh-server:apt-get install openssh-server 2)查 ...