首页 > *nix应用编程, *nix技术 > Go rpcx etcd试用

Go rpcx etcd试用

2020年5月1日 发表评论 阅读评论 227 次浏览

客户端与服务端要进行通信,至少得有一方知道另外一方的地址才行(一般是客户端知道服务端的监听地址),比如在这篇文章http://lenky.info/?p=2840中的101basic示例里,就是在客户端和服务端的代码里直接硬编码(hardcode)了服务器的监听地址(localhost:8972),从而客户端才能请求到服务端的服务。

直观来看,硬编码方式有很大缺点,比如如果服务地址发生变化则需要修改源码进行重新编译或启动。因此,一种改进的办法是服务端将服务地址放到配置文件,而客户端从配置文件中获取到对应的服务地址。基于这种思想,就有了服务注册与服务发现,即服务端(服务提供方)将提供服务的地址注册到服务注册中心,而客户端(服务请求方)则从服务注册中心获取服务地址,这种动态的管理方式无疑更加灵活且更加适应现在的云计算环境。

服务注册中心可以由很多软件来充当这个角色,在极其简单的环境下,文件系统(用配置文件存储服务地址)其实就可以,当然,考虑到高可用与一致性,可以有更好的选择,比如etcd等。

仍然是老套路,下面试用下rpcx结合etcd来做的服务注册与发现示例。

一,测试环境
二,安装Go环境
三,安装rpcx
四,下载测试代码
1,直接安装测试工程
$ go get -u github.com/rpcx-ecosystem/rpcx-examples3/…

上面四个部分请参考:http://lenky.info/?p=2840

五,测试
1,拷贝测试代码到$GOPATH目录,比如我这里测试etcdv3
$ cp -fr ~/gopath/pkg/mod/github.com/rpcx-ecosystem/rpcx-examples3@v0.0.0-20200423061325-9ca396e92ecd/registry/etcdv3 $GOPATH
$ cd $GOPATH
$ chmod 775 etcdv3/
$ cd etcdv3/

2,安装并启动etcd
$ sudo apt install etcd-server
$ sudo netstat -natp | grep etcd
tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 9804/etcd
tcp 0 0 127.0.0.1:2380 0.0.0.0:* LISTEN 9804/etcd

$ sudo apt install etcd-client
$ etcdctl member list
8e9e05c52164694d: name=lenky-virtual-machine peerURLs=http://localhost:2380 clientURLs=http://localhost:2379 isLeader=true
可以将etcd以集群方式进行部署,在这里,只有一个节点。

3,启动服务器
$ go mod init etcdv3
$ go run server.go

4,启动客户端
另开一个终端,执行:
$ cd $GOPATH
$ cd etcdv3/
$ cd client/
$ go run client.go
2020/04/26 11:57:20 10 * 20 = 200
^Csignal: interrupt

5,回到服务器的终端,可以看到如下输出:
$ go run server.go
2020/04/26 11:57:18 server.go:172: INFO : server pid:10422
call: 10 * 20 = 200
2020/04/26 11:57:25 server.go:355: INFO : client has closed this connection: 127.0.0.1:59952

六,看一下源代码

server.go源码如下:

package main

import (
	"flag"
	"log"
	"time"

	example "github.com/rpcx-ecosystem/rpcx-examples3"
	"github.com/smallnest/rpcx/server"
	"github.com/smallnest/rpcx/serverplugin"
)

var (
	addr     = flag.String("addr", "localhost:8972", "server address")
	etcdAddr = flag.String("etcdAddr", "localhost:2379", "etcd address")
	basePath = flag.String("base", "/rpcx_test", "prefix path")
)

func main() {
	flag.Parse()

	s := server.NewServer()
	addRegistryPlugin(s)

	s.RegisterName("Arith", new(example.Arith), "")
	err := s.Serve("tcp", *addr)
	if err != nil {
		panic(err)
	}
}

func addRegistryPlugin(s *server.Server) {

	r := &serverplugin.EtcdV3RegisterPlugin{
		ServiceAddress: "tcp@" + *addr,
		EtcdServers:    []string{*etcdAddr},
		BasePath:       *basePath,
		UpdateInterval: time.Minute,
	}
	err := r.Start()
	if err != nil {
		log.Fatal(err)
	}
	s.Plugins.Add(r)
}

client.go源码如下:

package main

import (
	"context"
	"flag"
	"log"
	"time"

	example "github.com/rpcx-ecosystem/rpcx-examples3"
	"github.com/smallnest/rpcx/client"
)

var (
	etcdAddr = flag.String("etcdAddr", "localhost:2379", "etcd address")
	basePath = flag.String("base", "/rpcx_test", "prefix path")
)

func main() {
	flag.Parse()

	d := client.NewEtcdV3Discovery(*basePath, "Arith", []string{*etcdAddr}, nil)
	xclient := client.NewXClient("Arith", client.Failover, client.RoundRobin, d, client.DefaultOption)
	defer xclient.Close()

	args := &example.Args{
		A: 10,
		B: 20,
	}

	for {
		reply := &example.Reply{}
		err := xclient.Call(context.Background(), "Mul", args, reply)
		if err != nil {
			log.Printf("failed to call: %v\n", err)
			time.Sleep(5 * time.Second)
			continue
		}

		log.Printf("%d * %d = %d", args.A, args.B, reply.C)

		time.Sleep(5 * time.Second)
	}

}

可以看到在客户端的源码client.go里,并没有看到服务端的硬编码地址,而是通过从服务注册中心etcd里去获取,可以对比101basic的client.go源码,主要区别是:

101basic是通过点对点直接去访问服务:
addr = flag.String(“addr”, “localhost:8972″, “server address”)
d := client.NewPeer2PeerDiscovery(“tcp@”+*addr, “”)

而etcdv3是通过etcd的地址去间接获取真实服务地址:
etcdAddr = flag.String(“etcdAddr”, “localhost:2379″, “etcd address”)
d := client.NewEtcdV3Discovery(*basePath, “Arith”, []string{*etcdAddr}, nil)

七,服务集群
服务注册中心etcd可以以集群方式进行部署,极大的提高可用性,那么具体的服务是否可以以集群方式运行呢?初步来看是可以的,如果不用服务注册中心,那么可以直接硬编码,具体见rpcx-examples3项目下的registry\multiple示例。
如果采用服务注册中心,那么也可以,继续沿用本文前面的示例。

1,在已有终端启动server.go

2,新开个终端,先将server.go拷贝为server2.go,并且修改服务端口后,执行启动服务
$ cp server.go server2.go
$ chmod 755 server2.go
$ vi server2.go
$ cat server2.go | grep 8973
addr = flag.String(“addr”, “localhost:8973″, “server address”)
$ go run server2.go

此次,上面的server.go和server2.go都是提供的相同服务,但是是在不同端口(server.go在8972,而server2.go在8973),类似于形成本地集群。

3,再开个终端,执行客户端程序
$ go run client.go

可以看到客户端程序正常获得请求结果,并且请求被均匀的分发到server.go和server2.go,如果ctrl+c结束掉其中的一个服务端,比如server.go,客户端仍然能够正常获得结果,达到了集群的高可用效果。将server.go重新启动后,又能提供服务了。
client.go默认选择的是RoundRobin轮询算法,因此请求被均匀的分发到server.go和server2.go,可以修改下client.go,比如选择ConsistentHash算法,那么请求每次都会被发到同一个服务。假设此时分发到server.go,如果ctrl+c结束掉server.go后,client仍将尝试几次,并且打印出错信息,几次尝试后,请求被分发到server2.go继续得到服务。如果再启动server.go,客户端的请求又会被重新分发到server.go。

Over~

参考:
1,https://blog.csdn.net/qq_36308324/article/details/80762702
2,https://www.jianshu.com/p/3bd041807974
3,https://www.jianshu.com/p/4828f90674b3
4,https://smallnest.gitbooks.io/go-rpc-programming-guide/content/part2/registry.html

转载请保留地址:http://www.lenky.info/archives/2020/05/2844http://lenky.info/?p=2844


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

分类: *nix应用编程, *nix技术 标签: , ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.