golang nats[5] cluster集群

集群模式

nats的集群模式对客户端来说并不是透明的。
所以集群对发布者和订阅者都是有影响的。
发布者和订阅者都知道连接的是一个集群的服务,而不是一个单点服务,换句话说发布者订阅者都必须指明集群中各个节点的地址。
当然,发布者和订阅者可以只针对集群中的某节点发布消息和订阅消息,不过这并不是集群模式的目的。

目的

提高可用性和可伸缩性。

实现原理

可用性,多个节点,挂掉任意一个,不影响整个集群对外提供服务。
伸缩性,服务端支持随意增加节点。订阅者可以感知服务端节点的变动,但是发布者并不能自动感知。

3个node的集群

$ gnatsd -p 4222 -m 4333 -cluster nats://localhost:4248 -routes nats://localhost:5248,nats://localhost:6248 -DV  
$ gnatsd -p 5222 -m 5333 -cluster nats://localhost:5248 -routes nats://localhost:4248,nats://localhost:6248 -DV 
$ gnatsd -p 6222 -m 6333 -cluster nats://localhost:6248 -routes nats://localhost:4248,nats://localhost:5248 -DV 

-p 端口:服务端口,发布者,订阅者需要使用此端口。
-m 端口: 监控端口。
-cluster 地址:作为集群节点对其他节点提供服务的地址,其他节点需要连接的地址。(其他节点的-routes 可以填写此地址)
-routes 地址:此节点,路由到其他地址的列表(也就是其他节点的-cluster)
-DV Debug and trace

gnatsd -p 服务提供端口 -m 服务监控端口 -cluster 集群内node地址 -routes 集群内其他node地址列表 -DV

Server

package main

import (
    "github.com/nats-io/go-nats"
    "log"
    "flag" "fmt" "time" ) const ( //url = "nats://192.168.3.125:4222" //url = nats.DefaultURL url = "nats://localhost:4222,nats://localhost:6222" //url = "nats://localhost:4222,nats://localhost:5222,nats://localhost:6222" ) var ( nc *nats.Conn err error ) func init() { if nc, err = nats.Connect(url, nats.DontRandomize(), nats.MaxReconnects(5), nats.ReconnectWait(2*time.Second), nats.DisconnectHandler(func(nc *nats.Conn) { fmt.Printf("Got disconnected!\n") }), nats.ReconnectHandler(func(_ *nats.Conn) { fmt.Printf("Got reconnected to %v!\n", nc.ConnectedUrl()) }), nats.ClosedHandler(func(nc *nats.Conn) { fmt.Printf("Connection closed. Reason: %q\n", nc.LastError()) }), nats.DiscoveredServersHandler(func(conn *nats.Conn) { fmt.Printf("Got Discover Server %v!\n", nc.ConnectedUrl()) }), nats.ErrorHandler(func(conn *nats.Conn, subscription *nats.Subscription, e error) { fmt.Printf("Got Error Server %v!\n",e) })); checkErr(err) { // } } func main() { var ( servername = flag.String("servername", "y", "name for server") queueGroup = flag.String("group", "", "group name for Subscribe") subj = flag.String("subj", "abc", "subject name") ) flag.Parse() log.Println(*servername, *queueGroup, *subj) startService(*subj, *servername+" worker1", *queueGroup) //startService(*subj, *servername+" worker2", *queueGroup) //startService(*subj, *servername+" worker3", *queueGroup) select {} } //receive message func startService(subj, name, queue string) { go async(nc, subj, name, queue) } func async(nc *nats.Conn, subj, name, queue string) { _, e := nc.QueueSubscribe(subj, queue, func(msg *nats.Msg) { log.Println(name, "Received a message From Async : ", string(msg.Data)) }) checkErr(e) } func checkErr(err error) bool { if err != nil { log.Println("error:", err) return false } return true } 

Client

package main

import (
    "github.com/nats-io/go-nats" "log" "strconv" "github.com/pborman/uuid" "flag" "time" "fmt" ) const ( //url = "nats://192.168.3.125:4222" //url = "nats://localhost:4222" //url = "nats://localhost:4222,nats://localhost:6222" url = "nats://localhost:4222,nats://localhost:5222,nats://localhost:6222" //url = "nats://localhost:5222" ) var ( nc *nats.Conn err error ) func init() { if nc, err = nats.Connect(url, nats.DontRandomize(), nats.MaxReconnects(10), nats.ReconnectWait(2*time.Second), nats.DisconnectHandler(func(nc *nats.Conn) { fmt.Printf("Got disconnected!\n") }), nats.ReconnectHandler(func(_ *nats.Conn) { fmt.Printf("Got reconnected to %v!\n", nc.ConnectedUrl()) }), nats.ClosedHandler(func(nc *nats.Conn) { fmt.Printf("Connection closed. Reason: %q\n", nc.LastError()) })); checkErr(err) { // } nc.SetDiscoveredServersHandler(func(conn *nats.Conn) { }) } func main() { var ( subj = flag.String("subj", "abc", "subject name") ) flag.Parse() log.Println(*subj) startClient(*subj) time.Sleep(time.Second) } //send message to server func startClient(subj string) { for i := 0; i < 1; i++ { id := uuid.New() log.Println(id) nc.Publish(subj, []byte(id+" Golang "+strconv.Itoa(i))) //nc.Publish(subj, []byte(id+" Rain "+strconv.Itoa(i))) //nc.Publish(subj, []byte(id+" Fog "+strconv.Itoa(i))) //nc.Publish(subj, []byte(id+" Cloudy "+strconv.Itoa(i))) } } func checkErr(err error) bool { if err != nil { log.Println(err) return false } return true } 

注意

  • 发布者和订阅者都需要指明3个节点的ur地址
    nats://localhost:4222,nats://localhost:5222,nats://localhost:6222
  • 如果3个node都不可用,发布者会发送消息失败。
  • 如果3个node至少有一个可用,订阅者就会收到消息。
  • 如果3个node全都不可用,订阅者会自动断开连接。
  • 增加一个node nats://localhost:7222,订阅者可以自动连接。
  • 增加node后,3个node全都不可用,订阅者不会断开连接,可以接受从新node发布的消息。
  • 3个node恢复后,订阅者可以接受3个node的消息。

后续

发布者和订阅者

  • 原始集群中node都不可用
  • 主动查询可用node
  • 接受可用node通知
  • 向可用node发送消息,订阅可用node的消息
  • 以上内容需要配合服务发现中间件或者自己实现

配置文件启动

$ gnatsd -c nodea.cfg
$ gnatsd -c nodeb.cfg
$ gnatsd -c nodec.cfg 

nodea.cfg

listen: localhost:4222 # host/port to listen for client connections

http: localhost:4333 # HTTP monitoring port # Authorization for client connections #authorization { #user: yasenagat # ./util/mkpasswd -p T0pS3cr3t #password: $2a$11$W2zko751KUvVy59mUTWmpOdWjpEm5qhcCZRd05GjI/sSOT.xtiHyG #ytc #token: $2a$11$ZuYXelbdaRQnOcADEx40yOtinCvEi9c3X64K2Kyx7wLJq7ECPUnA2 #timeout: 1 #} # Cluster definition cluster { listen: localhost:4248 # host/port for inbound route connections # Authorization for route connections #authorization { #user: user2 # ./util/mkpasswd -p T0pS3cr3tT00! #password: $2a$11$xH8dkGrty1cBNtZjhPeWJewu/YPbSU.rXJWmS6SFilOBXzmZoMk9m #yctc #token: $2a$11$d/RrRseSiPOd/fxurspFquSirrjseRFRFGHdRbte7D8wj2laCLcVS #timeout: 0.5 #} # Routes are actively solicited and connected to from this server. # Other servers can connect to us if they supply the correct credentials # in their routes definitions from above. routes = [ nats-route://127.0.0.1:5248 nats-route://127.0.0.1:6248 ] } # logging options debug: false trace: true logtime: false log_file: "nodea.log" # pid file pid_file: "nodea.pid" # Some system overides # max_connections max_connections: 100 # max_subscriptions (per connection) max_subscriptions: 1000 # maximum protocol control line max_control_line: 512 # maximum payload max_payload: 65536 # Duration the server can block on a socket write to a client. Exceeding the # deadline will designate a client as a slow consumer. write_deadline: "2s" 

nodeb.cfg

listen: localhost:5222 # host/port to listen for client connections

http: localhost:5333 # HTTP monitoring port

# Authorization for client connections
authorization {
  #user:     yasenagat
  # ./util/mkpasswd -p T0pS3cr3t #password: $2a$11$W2zko751KUvVy59mUTWmpOdWjpEm5qhcCZRd05GjI/sSOT.xtiHyG #ytb token: $2a$11$ToARKoxzTSTXxKCljOFe4eDmiPQ/EcaB0M7V8mGE1tfgOv97.iECe timeout: 1 } # Cluster definition cluster { listen: localhost:5248 # host/port for inbound route connections # Authorization for route connections authorization { #user: user1 # ./util/mkpasswd -p T0pS3cr3tT00! #password: pass1 #yctb token: $2a$11$EriHSUV8WO7PWUXTxOCY5uP7MhAswLE2tqQQPuz6kaoF89KhO8CcW timeout: 0.5 } # Routes are actively solicited and connected to from this server. # Other servers can connect to us if they supply the correct credentials # in their routes definitions from above. routes = [ nats-route://127.0.0.1:4248 nats-route://127.0.0.1:6248 ] } # logging options debug: false trace: true logtime: false log_file: "nodeb.log" # pid file pid_file: "nodeb.pid" # Some system overides # max_connections max_connections: 100 # max_subscriptions (per connection) max_subscriptions: 1000 # maximum protocol control line max_control_line: 512 # maximum payload max_payload: 65536 # Duration the server can block on a socket write to a client. Exceeding the # deadline will designate a client as a slow consumer. write_deadline: "2s" 

nodec.cfg

listen: localhost:6222 # host/port to listen for client connections

http: localhost:6333 # HTTP monitoring port # Authorization for client connections #authorization { #user: yasenagat # ./util/mkpasswd -p T0pS3cr3t #password: $2a$11$W2zko751KUvVy59mUTWmpOdWjpEm5qhcCZRd05GjI/sSOT.xtiHyG #ytc #token: $2a$11$HZy0M3lcxxzJRsFhtAoiX.jCuqKLyztcYYZPWRtlR.APhs/4mFYGC #timeout: 1 #} # Cluster definition cluster { listen: localhost:6248 # host/port for inbound route connections # Authorization for route connections #authorization { #user: user2 # ./util/mkpasswd -p T0pS3cr3tT00! #password: $2a$11$xH8dkGrty1cBNtZjhPeWJewu/YPbSU.rXJWmS6SFilOBXzmZoMk9m #yctc #token: $2a$11$srwaIbFHGwIt37t3GrPynOHSpZ2LHTtw1QXWuznXGOaknEwulP4o6 #timeout: 0.5 #} # Routes are actively solicited and connected to from this server. # Other servers can connect to us if they supply the correct credentials # in their routes definitions from above. routes = [ nats-route://127.0.0.1:5248 nats-route://127.0.0.1:4248 ] } # logging options debug: false trace: true logtime: false log_file: "nodec.log" # pid file pid_file: "nodec.pid" # Some system overides # max_connections max_connections: 100 # max_subscriptions (per connection) max_subscriptions: 1000 # maximum protocol control line max_control_line: 512 # maximum payload max_payload: 65536 # Duration the server can block on a socket write to a client. Exceeding the # deadline will designate a client as a slow consumer. write_deadline: "2s"


作者:luckyase
链接:https://www.jianshu.com/p/0a54ffd7430f
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转载于:https://www.cnblogs.com/gao88/p/10007754.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang 中使用 Redis 集群,可以使用官方提供的 Redis 客户端库 Redigo。Redigo 提供了连接池、管道、事务等功能,非常适合高并发场景下的 Redis 操作。 以下是一个使用 Redigo 连接 Redis 集群的示例: ```go package main import ( "fmt" "time" "github.com/gomodule/redigo/redis" ) func main() { cluster := &redis.Cluster{ MaxRedirects: 8, StartupNodes: []string{ "redis://node1:6379", "redis://node2:6379", "redis://node3:6379", }, DialTimeout: 10 * time.Second, ReadTimeout: 100 * time.Millisecond, WriteTimeout: 100 * time.Millisecond, } conn, err := cluster.Get() if err != nil { panic(err) } defer conn.Close() _, err = conn.Do("SET", "key", "value") if err != nil { panic(err) } value, err := redis.String(conn.Do("GET", "key")) if err != nil { panic(err) } fmt.Println(value) } ``` 在以上示例中,我们使用了 `redis.Cluster` 结构体来连接 Redis 集群,`MaxRedirects` 指定了最大重定向次数,`StartupNodes` 指定了集群中的节点地址,`DialTimeout`、`ReadTimeout`、`WriteTimeout` 分别指定了建立连接、读取数据、写入数据的超时时间。 使用 `cluster.Get()` 方法可以获取一个连接,使用完后需要调用 `conn.Close()` 方法释放连接。在获取到连接后,我们可以使用 `conn.Do()` 方法来执行 Redis 命令,也可以使用 `redis.String()`、`redis.Int()` 等方法来获取命令执行结果。 需要注意的是,在 Redis 集群中使用 `SET`、`GET` 等单个键操作命令时,Redigo 会自动进行分片操作,将请求发送到正确的节点。但是在使用一些不支持分片的命令时,需要手动指定节点。例如使用 `MGET` 命令时,需要使用 `redis.MultiConn.Do()` 方法并手动指定节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值