用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等一系列操作,数据结构也有很多,这里我 ...
随机推荐
- Java8 如何正确使用 Optional
原文出处:https://blog.kaaass.net/archives/764 Optional是Java8提供的为了解决null安全问题的一个API.善用Optional可以使我们代码中很多繁琐 ...
- fish 常用主题推荐
在安装fish的前提下 omf install zish omf theme zish zish
- 跨平台移动开发phonegap/cordova 3.3全系列教程-目录
目录(更新完成后会附上源码供参考) 第一章 android平台开发 phonegap/cordova简介 1.开发环境搭建 2.helloworld 3.启动画面 4.结合asp.net/jqmboi ...
- 【来龙去脉系列】RSA算法原理
如果你问我,哪一种算法最重要? 我可能会回答"公钥加密算法". 因为它是计算机通信安全的基石,保证了加密数据不会被破解.你可以想象一下,信用卡交易被破解的后果. 进入正题之前,我先 ...
- Asp.net MVC 服务端验证多语言错误
服务端验证用户提交信息时为了实现多语言使用了资源文件,如: using System.ComponentModel.DataAnnotations; public class UserModel { ...
- LeetCode Valid Palindrome 有效回文(字符串)
class Solution { public: bool isPalindrome(string s) { if(s=="") return true; ) return tru ...
- Java分代的思考
我们都知道,java的gc是基于java的分代前提,不管是CMS还是G1,都是基于分代思想:老年代和新生代 思考一:为什么可以分代? 1.java中对象的生命周期是不一样的,在gc中对应年龄的概念. ...
- IOS给图片增加水印(图片、文字)
在网上发现很多人使用 CGContextDrawImage(context,CGRectMake(0,0,self.width,self.height),[image CGImage]); //原图 ...
- 一、Web 如何工作的
平常我们在浏览器中输入一个网址,随即看到一个页面,这个过程是怎样实现的呢?下面用一幅图来说明: 整个流程如下: 1.域名解析 浏览器会解析域名对应的IP地址 PS:DNS服务器的知识 2.建立TCP ...
- python_31_集合
# 集合是一个无序的,不重复的数据组合,它的主要作用如下: # 去重,把一个列表变成集合,就自动去重了 # 关系测试,测试两组数据之前的交集.差集.并集等关系 s = set([3, 5, 9, 10 ...