1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-06-18 21:45:04 +08:00

optimize(qisgn): async operations

FYI: @1umine
This commit is contained in:
源文雨 2023-08-28 16:56:19 +08:00
parent 79a194fbb0
commit fd6ef4a2b8
4 changed files with 74 additions and 86 deletions

View File

@ -163,7 +163,6 @@ func LoginInteract() {
log.Fatalf("加载设备信息失败: %v", err) log.Fatalf("加载设备信息失败: %v", err)
} }
} }
initSignServersConfig()
signServer, err := getAvaliableSignServer() // 获取可用签名服务器 signServer, err := getAvaliableSignServer() // 获取可用签名服务器
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)

View File

@ -24,76 +24,59 @@ import (
"github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/config"
) )
type currentSignServer struct { type currentSignServer atomic.Pointer[config.SignServer]
server *config.SignServer
ok bool func (c *currentSignServer) get() *config.SignServer {
lock sync.RWMutex return (*atomic.Pointer[config.SignServer])(c).Load()
} }
func (c *currentSignServer) getServer() *config.SignServer { func (c *currentSignServer) set(server *config.SignServer) {
c.lock.RLock() (*atomic.Pointer[config.SignServer])(c).Store(server)
defer c.lock.RUnlock()
return c.server
}
func (c *currentSignServer) isOK() bool {
c.lock.RLock()
defer c.lock.RUnlock()
return c.ok
}
func (c *currentSignServer) setServer(server *config.SignServer, status bool) {
c.lock.Lock()
defer c.lock.Unlock()
if server != nil {
c.server = server
}
c.ok = status
} }
// 当前签名服务器 // 当前签名服务器
var curSignServer *currentSignServer var ss currentSignServer
var errorCount = uintptr(0)
var checkLock sync.Mutex
func initSignServersConfig() { // 失败计数
if len(base.SignServers) == 0 { type errconut atomic.Uintptr
log.Warn("no configured sign-server")
return func (ec *errconut) hasOver(count uintptr) bool {
} return (*atomic.Uintptr)(ec).Load() > count
if len(base.SignServers) > 5 {
base.SignServers = base.SignServers[:5]
log.Warn("签名服务器数量配置过多,可能导致不可用时检查时间过久,已取前 5 个")
}
curSignServer = &currentSignServer{
server: &base.SignServers[0],
ok: true,
}
} }
func (ec *errconut) inc() {
(*atomic.Uintptr)(ec).Add(1)
}
var errn errconut
// getAvaliableSignServer 获取可用的签名服务器,没有则返回空和相应错误 // getAvaliableSignServer 获取可用的签名服务器,没有则返回空和相应错误
func getAvaliableSignServer() (config.SignServer, error) { func getAvaliableSignServer() (*config.SignServer, error) {
if curSignServer.isOK() { cs := ss.get()
return *curSignServer.getServer(), nil if cs != nil {
return cs, nil
}
if len(base.SignServers) == 0 {
return nil, errors.New("no sign server configured")
} }
maxCount := base.Account.MaxCheckCount maxCount := base.Account.MaxCheckCount
if maxCount == 0 && atomic.LoadUintptr(&errorCount) >= 3 { if maxCount == 0 {
log.Warn("已连续 3 次获取不到可用签名服务器,将固定使用主签名服务器") if errn.hasOver(3) {
curSignServer.setServer(&base.SignServers[0], true) log.Warn("已连续 3 次获取不到可用签名服务器,将固定使用主签名服务器")
return *curSignServer.getServer(), nil ss.set(&base.SignServers[0])
} return ss.get(), nil
if maxCount > 0 && int(atomic.LoadUintptr(&errorCount)) >= maxCount { }
} else if errn.hasOver(uintptr(maxCount)) {
log.Fatalf("获取可用签名服务器失败次数超过 %v 次, 正在离线", maxCount) log.Fatalf("获取可用签名服务器失败次数超过 %v 次, 正在离线", maxCount)
} }
if checkLock.TryLock() { if len(cs.URL) > 0 {
defer checkLock.Unlock() log.Warnf("当前签名服务器 %v 不可用,正在查找可用服务器", cs.URL)
cs := curSignServer.getServer()
if len(cs.URL) > 1 {
log.Warnf("当前签名服务器 %v 不可用,正在查找可用服务器", cs.URL)
}
return syncCheckServer(base.SignServers)
} }
return config.SignServer{}, errors.New("checking sign-servers") cs = asyncCheckServer(base.SignServers)
if cs == nil {
return nil, errors.New("no usable sign server")
}
return cs, nil
} }
func isServerAvaliable(signServer string) bool { func isServerAvaliable(signServer string) bool {
@ -108,25 +91,31 @@ func isServerAvaliable(signServer string) bool {
return false return false
} }
// syncCheckServer 按同步顺序检查所有签名服务器直到找到可用的 // asyncCheckServer 按同步顺序检查所有签名服务器直到找到可用的
func syncCheckServer(servers []config.SignServer) (config.SignServer, error) { func asyncCheckServer(servers []config.SignServer) *config.SignServer {
for i, server := range servers { doRegister := sync.Once{}
log.Infof("检查签名服务器:%v (%v/%v)", server.URL, i+1, len(servers)) wg := sync.WaitGroup{}
if len(server.URL) < 4 { wg.Add(len(servers))
continue for i, s := range servers {
} go func(i int, server config.SignServer) {
if isServerAvaliable(server.URL) { defer wg.Done()
atomic.StoreUintptr(&errorCount, 0) log.Infof("检查签名服务器:%v (%v/%v)", server.URL, i+1, len(servers))
curSignServer.setServer(&server, true) if len(server.URL) < 4 {
log.Infof("使用签名服务器 url=%v, key=%v, auth=%v", server.URL, server.Key, server.Authorization) return
if base.Account.AutoRegister {
// 若配置了自动注册实例则在切换后注册实例否则不需要注册签名时由qsign自动注册
signRegister(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, server.Key)
} }
return server, nil if isServerAvaliable(server.URL) {
} doRegister.Do(func() {
ss.set(&server)
log.Infof("使用签名服务器 url=%v, key=%v, auth=%v", server.URL, server.Key, server.Authorization)
if base.Account.AutoRegister {
// 若配置了自动注册实例则在切换后注册实例否则不需要注册签名时由qsign自动注册
signRegister(base.Account.Uin, device.AndroidId, device.Guid, device.QImei36, server.Key)
}
})
}
}(i, s)
} }
return config.SignServer{}, errors.New("no avaliable sign-server") return ss.get()
} }
/* /*
@ -137,10 +126,10 @@ func syncCheckServer(servers []config.SignServer) (config.SignServer, error) {
*/ */
func requestSignServer(method string, url string, headers map[string]string, body io.Reader) (string, []byte, error) { func requestSignServer(method string, url string, headers map[string]string, body io.Reader) (string, []byte, error) {
signServer, e := getAvaliableSignServer() signServer, e := getAvaliableSignServer()
if e != nil && len(signServer.URL) <= 1 { // 没有可用的 if e != nil && len(signServer.URL) == 0 { // 没有可用的
log.Warnf("获取可用签名服务器出错:%v, 将使用主签名服务器进行签名", e) log.Warnf("获取可用签名服务器出错:%v, 将使用主签名服务器进行签名", e)
atomic.AddUintptr(&errorCount, 1) errn.inc()
signServer = base.SignServers[0] // 没有获取到时使用第一个 signServer = &base.SignServers[0] // 没有获取到时使用第一个
} }
if !strings.HasPrefix(url, signServer.URL) { if !strings.HasPrefix(url, signServer.URL) {
url = strings.TrimSuffix(signServer.URL, "/") + "/" + strings.TrimPrefix(url, "/") url = strings.TrimSuffix(signServer.URL, "/") + "/" + strings.TrimPrefix(url, "/")
@ -160,7 +149,7 @@ func requestSignServer(method string, url string, headers map[string]string, bod
}.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second) }.WithTimeout(time.Duration(base.SignServerTimeout) * time.Second)
resp, err := req.Bytes() resp, err := req.Bytes()
if err != nil { if err != nil {
curSignServer.setServer(nil, false) // 标记为不可用 ss.set(nil) // 标记为不可用
} }
return signServer.URL, resp, err return signServer.URL, resp, err
} }
@ -298,7 +287,7 @@ var lastToken = ""
func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) {
i := 0 i := 0
for { for {
cs := curSignServer.getServer() cs := ss.get()
sign, extra, token, err = signRequset(seq, uin, cmd, qua, buff) sign, extra, token, err = signRequset(seq, uin, cmd, qua, buff)
if err != nil { if err != nil {
log.Warnf("获取sso sign时出现错误: %v. server: %v", err, cs.URL) log.Warnf("获取sso sign时出现错误: %v. server: %v", err, cs.URL)
@ -343,7 +332,7 @@ func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []b
} }
rule := base.Account.RuleChangeSignServer rule := base.Account.RuleChangeSignServer
if (len(sign) == 0 && rule >= 1) || (len(token) == 0 && rule >= 2) { if (len(sign) == 0 && rule >= 1) || (len(token) == 0 && rule >= 2) {
curSignServer.setServer(nil, false) ss.set(nil)
} }
return sign, extra, token, err return sign, extra, token, err
} }
@ -358,7 +347,7 @@ func signServerDestroy(uin string) error {
} }
signServer, resp, err := requestSignServer( signServer, resp, err := requestSignServer(
http.MethodGet, http.MethodGet,
"destroy"+fmt.Sprintf("?uin=%v&key=%v", uin, curSignServer.getServer().Key), "destroy"+fmt.Sprintf("?uin=%v&key=%v", uin, ss.get().Key),
nil, nil, nil, nil,
) )
if err != nil || gjson.GetBytes(resp, "code").Int() != 0 { if err != nil || gjson.GetBytes(resp, "code").Int() != 0 {
@ -396,9 +385,9 @@ func signStartRefreshToken(interval int64) {
qqstr := strconv.FormatInt(base.Account.Uin, 10) qqstr := strconv.FormatInt(base.Account.Uin, 10)
defer t.Stop() defer t.Stop()
for range t.C { for range t.C {
cs, master := curSignServer.getServer(), base.SignServers[0] cs, master := ss.get(), base.SignServers[0]
if cs.URL != master.URL && isServerAvaliable(master.URL) { if cs.URL != master.URL && isServerAvaliable(master.URL) {
curSignServer.setServer(&master, true) ss.set(&master)
log.Infof("主签名服务器可用,已切换至主签名服务器 %v", cs.URL) log.Infof("主签名服务器可用,已切换至主签名服务器 %v", cs.URL)
} }
err := signRefreshToken(qqstr) err := signRefreshToken(qqstr)

View File

@ -93,7 +93,7 @@ func Init() {
SignServers = conf.Account.SignServers SignServers = conf.Account.SignServers
IsBelow110 = conf.Account.IsBelow110 IsBelow110 = conf.Account.IsBelow110
HTTPTimeout = conf.Message.HTTPTimeout HTTPTimeout = conf.Message.HTTPTimeout
SignServerTimeout = conf.Account.SignServerTimeout SignServerTimeout = int(conf.Account.SignServerTimeout)
} }
{ // others { // others
Proxy = conf.Message.ProxyRewrite Proxy = conf.Message.ProxyRewrite

View File

@ -37,8 +37,8 @@ type Account struct {
AllowTempSession bool `yaml:"allow-temp-session"` AllowTempSession bool `yaml:"allow-temp-session"`
SignServers []SignServer `yaml:"sign-servers"` SignServers []SignServer `yaml:"sign-servers"`
RuleChangeSignServer int `yaml:"rule-change-sign-server"` RuleChangeSignServer int `yaml:"rule-change-sign-server"`
MaxCheckCount int `yaml:"max-check-count"` MaxCheckCount uint `yaml:"max-check-count"`
SignServerTimeout int `yaml:"sign-server-timeout"` SignServerTimeout uint `yaml:"sign-server-timeout"`
IsBelow110 bool `yaml:"is-below-110"` IsBelow110 bool `yaml:"is-below-110"`
AutoRegister bool `yaml:"auto-register"` AutoRegister bool `yaml:"auto-register"`
AutoRefreshToken bool `yaml:"auto-refresh-token"` AutoRefreshToken bool `yaml:"auto-refresh-token"`