redisgo-ngrok实现内网穿透的管理

Redis

场景和需求: 在使用ngrok的时候,我们需要对ngrok的所有连接进行管理,可以理解为对连接上来的用户进行管理。ngrok的原始目的是实现内网穿透,可以从外网访问内网的机器(如树莓派),那么问题来了,如果有100台树莓派,我们如何知道?所以需要在ngrokd服务器上记录下每一台树莓派的ip地址和端口。

目的

在ngrokd服务端记录所有的连接。

方案

记录所有的方式有很多种,可以写入到文件当中、也可以写入到数据库中(如sqlite、mysql,MongoDB,Redis等)。有这么多的方式可以实现我们的目的,当然需要根据你的业务场景选择一种较为合适的。这里我们选用redis,因为redis 是一个NoSql,not only sql,redis不仅仅是一个数据库,redis还是一个发布/订阅的系统。发布/订阅系统可以方便日后与企业级的平台进行交互融合。有了发布/订阅系统,就可以类似微信一样,可以实时的知道有谁连接上了ngrokd服务器。

Redis使用

Redis教程资源在网上一大堆的资料。在本项目中只会用到常用的几个而已。可以通过本项目举一反三了解redis的特性。

修改步骤:

  • 增加ivgredis.go
  • 修改src/ngrok/msg/conn.go
  • 修改src/ngrok/server/tunnel.go
  1. ivgredis.go文件如下
package ivgredis
//package main

import (
	"time"

	log "github.com/alecthomas/log4go"
	"github.com/gomodule/redigo/redis"
)

var redisClient *redis.Pool

func GetConnFromPool() redis.Conn {
    
    
	if redisClient != nil {
    
    
		goto end
	}
	redisClient = &redis.Pool{
    
    
		//连接方法
		Dial: func() (redis.Conn, error) {
    
    
			c, err := redis.Dial("tcp", "127.0.0.1:6379")
			if err != nil {
    
    
				return nil, err
			}
			c.Do("SELECT", 0)
			return c, nil
		},
		//DialContext:     nil,
		//TestOnBorrow:    nil,
		//最大的空闲连接数,表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态。
		MaxIdle: 1,
		//最大的激活连接数,表示同时最多有N个连接
		MaxActive: 10,
		//最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭
		IdleTimeout: 180 * time.Second,
		//Wait:            false,
		//MaxConnLifetime: 0,
	}
end:
	c := redisClient.Get()
	// defer c.Close()
	return c
}

func IsSetMemberExist(key string, member string) bool {
    
    
	c1 := GetConnFromPool()
	exists, err := redis.Bool(c1.Do("SISMEMBER", key, member))
	defer c1.Close()
	if err != nil {
    
    
		log.Debug("%v", err)
		return false
	} else {
    
    
		return exists
	}
}

func AddSetMember(key string, member string) (num int64, err error) {
    
    
	// var str string = {"Type":"NewTunnel","Payload":{"ReqId":"7ead4dedaecaf989","Url":"tcp://ng.ubuntu.local:8022","Protocol":"tcp","Error":""}}
	c1 := GetConnFromPool()
	num, err = redis.Int64(c1.Do("SADD", key, member))
	defer c1.Close()
	return num, err
}

func GetSetMembers(key string) (results1 []string, err error) {
    
    
	c1 := GetConnFromPool()
	results1, err = redis.Strings(c1.Do("SMEMBERS", key))
	defer c1.Close()
	return results1, err
}

// SREM KEY MEMBER1..MEMBERN
func DelSetMember(key string, member string) (results1 int64, err error) {
    
    
	log.Debug("del %s", member)
	c1 := GetConnFromPool()
	results1, err = redis.Int64(c1.Do("SREM", key, member))
	log.Debug(results1)
	defer c1.Close()
	return results1, err
}
func PublishConnNgrokClien(channel string, message string) (num int, err error) {
    
    
	c1 := GetConnFromPool()
	defer c1.Close()
	return redis.Int(c1.Do("PUBLISH", channel, message))
}

/*
func main() {

	var ip1 string
	ip1 = "ivg-12-34-56.vkingman.cn"

	exists := isSetMemberExist("ngrok_client", ip1)
	log.Debug(exists)

	val1, err := addSetMember("ngrok_client", ip1)
	if err != nil {
		log.Debug("%v", err)
	} else {
		log.Debug("add %d members", val1)
	}

	results1, err := getSetMembers("ngrok_client")
	if err != nil {
		log.Debug("set err")
		fmt.Println(err)
	} else {
		log.Debug("SMEMBERS ok")
		log.Debug(results1)
	}

	results2, err := delSetMember("ngrok_client", ip1)
	if err != nil {
		log.Debug("%v", err)
	} else {
		log.Debug("del %d member", results2)
	}

}
*/

  1. 修改src/ngrok/msg/conn.go的WriteMsg函数

    func WriteMsg(c conn.Conn, msg interface{
          
          }) (err error) {
          
          
    	var (
    		ret int
    	)
    	buffer, err := Pack(msg)
    	if err != nil {
          
          
    		return
    	}
    
    	c.Debug("Writing message: %s", string(buffer))
    	err = binary.Write(c, binary.LittleEndian, int64(len(buffer)))
    
    	if err != nil {
          
          
    		return
    	}
    
    	if _, err = c.Write(buffer); err != nil {
          
          
    		return
    	}
    
    	ret = strings.Index(string(buffer), "NewTunnel")
    	if ret > 0 {
          
          
    		log.Error("liubaolong----------------> %s", string(buffer))
    		bExist := ivgredis.IsSetMemberExist("ngrok_client", string(buffer))
    		if !bExist {
          
          
    			/* add */
    			t := &IvgNewTunnel{
          
          }
    			err = json.Unmarshal(buffer, &t)
    			log.Debug(" %v", t.Payload.Url)
    
    			ivgredis.AddSetMember("ngrok_client", t.Payload.Url)
    			_, err = ivgredis.PublishConnNgrokClien("ngrok_client.connect", t.Payload.Url)
    			if err != nil {
          
          
    				log.Debug("%v", err)
    			}
    		}
    	}
    
    	return nil
    }
    
  2. 修改src/ngrok/server/tunnel.go的Shutdown函数

func (t *Tunnel) Shutdown() {
    
    
	t.Info("liubaolong--190---> Shutting down, %s", t.url)
	ret, _ := ivgredis.DelSetMember("ngrok_client", t.url)
	if ret > 0 {
    
    
		log.Debug("del %s", t.url)
		_, err := ivgredis.PublishConnNgrokClien("ngrok_client.disconnet", t.url)
		if err != nil {
    
    
			log.Debug("%v", err)
		}
	}

	// mark that we're shutting down
	atomic.StoreInt32(&t.closing, 1)

	// if we have a public listener (this is a raw TCP tunnel), shut it down
	if t.listener != nil {
    
    
		t.listener.Close()
	}

	// remove ourselves from the tunnel registry
	tunnelRegistry.Del(t.url)

	// let the control connection know we're shutting down
	// currently, only the control connection shuts down tunnels,
	// so it doesn't need to know about it
	// t.ctl.stoptunnel <- t

	metrics.CloseTunnel(t)
}

go import 需要注意

首字母小写时,为包内使用,包外无法引用到。

猜你喜欢

转载自blog.csdn.net/V__KING__/article/details/109441363