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. SpringMVC 返回实体对象时屏蔽某些属性

    SpringMVC 可以直接已JSON的结果返回实体对象,可是返回时是所有属性与属性值都会一并返回, 怎样才能屏蔽某些属性?方法很简单,只要在实体对象类中要屏蔽的属性值上加 @JsonIgnore 注 ...

  2. Spring scope 配置

    Scope 描述的是 Spring 容器如何新建Bean的实例,Spring的Scope有以下几种,通过@Scope来注解实现: 1. Singleton: 一个Spring容器中只有一个Bean的实 ...

  3. 【Java/Android性能优 6】Android 图片SD卡缓存 使用简单 支持预取 支持多种缓存算法 支持不同网络类型 支持序列化

    本文转自:http://www.trinea.cn/android/android-imagesdcardcache/ 本文主要介绍一个支持图片自动预取.支持多种缓存算法.支持数据保存和恢复的图片Sd ...

  4. Spring Boot 的配置文件application.properties

    Spring Boot 中的application.properties 是一个全局的配置文件,放在src/main/resources 目录下或者类路径的/config下. 作为全局配置文件的app ...

  5. javascript设计模式之中介者模式

    /* * 小游戏演示中介者模式 * Home 按键 1 * Guest 按键 0 * 半分钟内看谁按下的次数多 * * 参与的对象: * 玩家 * 计分板 * 中介者 * * 中介者模式使对象之间松耦 ...

  6. 微信公众平台网页开发实战--3.利用JSSDK在网页中获取地理位置(HTML5+jQuery)

    复制一份JSSDK环境,创建一份index.html文件,结构如图7.1所示. 图7.1  7.1节文件结构 在location.js中,封装“getLocation”接口,如下: 01 wxJSSD ...

  7. 小div在大div里面 垂直居中

    方法1: .parent { width:800px; height:500px; border:2px solid #000; position:relative; } .child { width ...

  8. 【MATLAB】画平行于坐标轴的曲线

    hold on; ylim=get(gca,'Ylim'); % 获取当前图形的纵轴的范围 plot([4,4],ylim,'m--'); % 绘制x=4的直线 hold off;

  9. linux 命令——22 find (转)

    find一些常用参数的一些常用实例和一些具体用法和注意事项. 1.使用name选项: 文 件名选项是find命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用.  可以使用某种文件名模式来匹 ...

  10. IOS 旋转+缩放(手势识别)

    @interface NJViewController ()<UIGestureRecognizerDelegate> @property (weak, nonatomic) IBOutl ...