golang 通过socks5代理连接mysql(gorm)

golang 通过socks5代理连接mysql(gorm),第1张

前言

在前一段时间遇见过这样一件事,我们的部分服务要部署在两个机房,而且两个机房并不互通,但是两个服务要通过mysql数据库实现配合工作,从无到有想到了两种实现方式。
这里注明一点的是mysql是与主服务部署一起的,而子服务是单独在另一个机房,因为一些特殊原因,两个机房并不能直接互相访问,但是两个机房部署了proxy代理和socks5代理。
在其间想到了两个解决方法,一是子服务的所有sql *** 作均放在主服务侧,然后子服务的curd *** 作通过GET和POST请求主服务,然后主服务实现具体的curd,但是这种方式对以后的服务变更并不太友好,可能只是一个小的修改就需要同时改变二者,第二种方式是子服务通过socks5直接连接mysql服务,这样只需要考虑延迟就可以直接无感使用,而且数据量并非过大时,这是最好的处理手段。只需要将最后的处理结果导入mysql就可以了。

proxy代理方式

这种方式仅是在进行http请求时增加了代理,其本质与普通http请求并无不同。
下面是实例代码

package main

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

/*
	1. 普通请求
*/
func RequestGeneral() {
	webUrl := "https://www.qiniu.com/"
	resp, err := http.Get(webUrl)
	if err != nil {
		fmt.Println(err)
		return
	}
	time.Sleep(time.Second * 3)

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(body))
}
/*
	1. 代理请求
	2. 跳过https不安全验证
*/
func RequestNoHttps(webUrl, proxyUrl string) {
	// webUrl := "https://www.qiniu.com/"
	// proxyUrl := "http://xxx.xxx.xx.xx:xxx" //proxy代理的 ip:port
	proxy, _ := url.Parse(proxyUrl)
	tr := &http.Transport{
		Proxy:           http.ProxyURL(proxy),
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}

	client := &http.Client{
		Transport: tr,
		Timeout:   time.Second * 5, //超时时间
	}

	resp, err := client.Get(webUrl)
	if err != nil {
		fmt.Println("出错了", err)
		return
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(body))
}

func main() {
	webUrl := "https://www.qiniu.com/"
	proxyUrl := "http://xxx.xxx.xx.xx:xxx" //proxy代理的 ip:port

	RequestNoHttps(webUrl, proxyUrl)
}

不难发现只是在创建http请求时,在http.Transport内增加了proxy字段,并将对应的代理地址赋值进去,就可以实现http请求的代理。
但是这种方式在我们的服务内是并不是最优解,所以并未使用。

socks5代理

这种方式是我们最后使用的方案。

package dbx

import (
	"context"
	"fmt"
	proxy_mysql "github.com/go-sql-driver/mysql"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/proxy"
	"log"
	"net"
	"time"

	gorm_mysql "gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type Config struct {
	DbUser   string `yaml:"dbUser"`
	DbPass   string `yaml:"dbPass"`
	DbUrl    string `yaml:"dbUrl"`
	DbPort   string `yaml:"dbPort"`
	DbSchema string `yaml:"dbSchema"`
	Dbsocks  string `yaml:"dbSocks"`
}

// 使用自定义socks5代理拨号
func InitDBProxyUrl(con Config) string {
	logrus.Info("InitDBProxyUrl get Dbsocks:", conf.Dbsocks)
	dialer, err := proxy.SOCKS5("tcp", conf.Dbsocks, nil, proxy.Direct)
	checkErrFatal(err)

	// 此处自定义一个拨号(代理)
	proxy_mysql.RegisterDialContext("fixieDial", func(ctx context.Context, addr string) (net.Conn, error) {
		return dialer.Dial("tcp", addr)
	})

	dbUrl = fmt.Sprintf("%s:%s@fixieDial(%s:%s)/%s?parseTime=true&loc=Local&charset=utf8mb4",
		conf.dbUser,
		conf.dbPass,
		conf.dbUrl,
		conf.dbPort,
		conf.dbSchema,
	)
	return dbUrl
}

// 普通tcp拨号
func InitDBUrl(con Config) string {
	dbUrl = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true&loc=Local&charset=utf8mb4",
		conf.dbUser,
		conf.dbPass,
		conf.dbUrl,
		conf.dbPort,
		conf.dbSchema,
	)
	return dbUrl
}

var db *gorm.DB

func GetDb() *gorm.DB {
	return db
}

// 使用socks5代理模式
func OpenProxyDB() {
	dbUrl := InitDBProxyUrl() // 使用自定义socks5代理

	var err error
	db, err = gorm.Open(gorm_mysql.Open(dbUrl), &gorm.Config{})
	checkErrFatal(err)

	db.Debug()

	sqlDB, err := db.DB()
	checkErrFatal(err)
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(10)
	sqlDB.SetConnMaxLifetime(time.Hour)
}

func checkErrFatal(err error) {
	if err != nil {
		log.Fatalln(err)
	}
}

这里先是使用了"golang.org/x/net/proxy"模块,根据传入的socks5代理的IP和端口返回自定义代理拨号方法,然后利用"github.com/go-sql-driver/mysql"将得到的自定义方法注册进系统,然后使用"gorm.io/driver/mysql"驱动链接mysql,这里使用了两个mysql驱动,一个是为了注册socks5代理拨号,一个是为了使用gorm连接mysql。

虽然"github.com/go-sql-driver/mysql"本身也是可以直接连接mysql的,但是"gorm.io/driver/mysql"是为了使用gorm。

至于二者为什么是可以混合使用的,这不清楚,但是应该是上下文的关系,或者说是将自定义拨号方法临时注册进系统内部了。

因为如果不进行注册是无法这样使用的,而且使用tcpdump指令,也是可以检测到本机ip端口和代理ip端口之间的数据通信过程。

23:09:46.825460 IP xx.xx.xx.xx.xxxx > yy.yy.yy.yy.yyyy0: Flags [R.], seq 0, ack 3037942227, win 0, length 0
23:09:46.833522 IP yy.yy.yy.yy.yyyy2 > xx.xx.xx.xx.xxxx: Flags [S], seq 1928785681, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
23:09:46.833524 IP yy.yy.yy.yy.yyyy1 > xx.xx.xx.xx.xxxx: Flags [S], seq 155678182, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
23:09:46.833579 IP yy.yy.yy.yy.yyyy4 > xx.xx.xx.xx.xxxx: Flags [S], seq 1472247145, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
23:09:46.833581 IP xx.xx.xx.xx.xxxx > yy.yy.yy.yy.yyyy2: Flags [R.], seq 0, ack 1928785682, win 0, length 0
23:09:46.833634 IP xx.xx.xx.xx.xxxx > yy.yy.yy.yy.yyyy4: Flags [R.], seq 0, ack 1472247146, win 0, length 0
23:09:46.833751 IP yy.yy.yy.yy.yyyy5 > xx.xx.xx.xx.xxxx: Flags [S], seq 2981254641, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
23:09:46.833968 IP yy.yy.yy.yy.yyyy6 > xx.xx.xx.xx.xxxx: Flags [S], seq 1755001897, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0

这里为了安全就不暴露ip和具体端口了,但是依然可以看出二者之间的数据交换过程。
有需要的可以去建立个socks5代理环境测试下。

如下命令可以设置系统的代理为socks5

export http_proxy=socks5://xx.xx.xx.xx:xxxx

如下命令可以设置系统的代理为proxy

export http_proxy=http://yy.yy.yy.yy:yyyy

然后搞个curl或者什么来测试下,能找到这个文章说明这里应该对你不是难事,而且有测试条件。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/995569.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存