Răsfoiți Sursa

新增模拟登陆grpc服务

maxiaoshan 2 ani în urmă
părinte
comite
f9beab3f8b

+ 38 - 0
src/login/client/client.go

@@ -0,0 +1,38 @@
+package client
+
+import (
+	"fmt"
+	"golang.org/x/net/context"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
+	"login/proto"
+	qu "qfw/util"
+	"spiderutil"
+)
+
+var Client proto.SimulateLoginClient
+
+func InitSimulateLoginClient() {
+	defer qu.Catch()
+	fmt.Println("address:", spiderutil.Config.LoginServerAddress)
+	conn, err := grpc.Dial(spiderutil.Config.LoginServerAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	if err != nil {
+		fmt.Println("Init Connect Error: ", err)
+	}
+	Client = proto.NewSimulateLoginClient(conn)
+}
+
+func GetSimulateLoginInfo(site, stype, header, param string) (result string) {
+	defer qu.Catch()
+	req := &proto.LoginReq{
+		Site:   site,
+		Stype:  stype,
+		Param:  param,
+		Header: header,
+	}
+	resp, err := Client.GetSimulateLoginInfo(context.Background(), req)
+	if err != nil {
+		qu.Debug("Go Run Js Get Result Error:", err)
+	}
+	return resp.LoginInfo
+}

+ 14 - 0
src/login/config.json

@@ -0,0 +1,14 @@
+{
+  "serverport": ":8032",
+  "bideditor": {
+    "addr": "192.168.3.166:27082",
+    "db": "editor",
+    "size": 10,
+    "coll": "lua_login",
+    "username": "",
+    "password": ""
+  },
+  "timeout": 60,
+  "proxyaddr": "http://cc.spdata.jianyu360.com/crawl/proxy/socks5/fetch",
+  "proxyauthor": "Basic amlhbnl1MDAxOjEyM3F3ZSFB"
+}

+ 240 - 0
src/login/proto/login.pb.go

@@ -0,0 +1,240 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.0
+// 	protoc        v3.19.4
+// source: proto/login.proto
+
+package proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+//model
+type LoginReq struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Stype  string `protobuf:"bytes,1,opt,name=stype,proto3" json:"stype,omitempty"`
+	Site   string `protobuf:"bytes,2,opt,name=site,proto3" json:"site,omitempty"`
+	Param  string `protobuf:"bytes,3,opt,name=param,proto3" json:"param,omitempty"`
+	Header string `protobuf:"bytes,4,opt,name=header,proto3" json:"header,omitempty"`
+}
+
+func (x *LoginReq) Reset() {
+	*x = LoginReq{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_login_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LoginReq) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LoginReq) ProtoMessage() {}
+
+func (x *LoginReq) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_login_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use LoginReq.ProtoReflect.Descriptor instead.
+func (*LoginReq) Descriptor() ([]byte, []int) {
+	return file_proto_login_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *LoginReq) GetStype() string {
+	if x != nil {
+		return x.Stype
+	}
+	return ""
+}
+
+func (x *LoginReq) GetSite() string {
+	if x != nil {
+		return x.Site
+	}
+	return ""
+}
+
+func (x *LoginReq) GetParam() string {
+	if x != nil {
+		return x.Param
+	}
+	return ""
+}
+
+func (x *LoginReq) GetHeader() string {
+	if x != nil {
+		return x.Header
+	}
+	return ""
+}
+
+type LoginResp struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	LoginInfo string `protobuf:"bytes,1,opt,name=loginInfo,proto3" json:"loginInfo,omitempty"`
+}
+
+func (x *LoginResp) Reset() {
+	*x = LoginResp{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_login_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LoginResp) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LoginResp) ProtoMessage() {}
+
+func (x *LoginResp) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_login_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use LoginResp.ProtoReflect.Descriptor instead.
+func (*LoginResp) Descriptor() ([]byte, []int) {
+	return file_proto_login_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *LoginResp) GetLoginInfo() string {
+	if x != nil {
+		return x.LoginInfo
+	}
+	return ""
+}
+
+var File_proto_login_proto protoreflect.FileDescriptor
+
+var file_proto_login_proto_rawDesc = []byte{
+	0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x62, 0x0a, 0x08, 0x4c, 0x6f,
+	0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x79, 0x70, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04,
+	0x73, 0x69, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x74, 0x65,
+	0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x29,
+	0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x6c,
+	0x6f, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+	0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x32, 0x4a, 0x0a, 0x0d, 0x53, 0x69, 0x6d,
+	0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x39, 0x0a, 0x14, 0x47, 0x65,
+	0x74, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x49, 0x6e,
+	0x66, 0x6f, 0x12, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
+	0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x69,
+	0x6e, 0x52, 0x65, 0x73, 0x70, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_proto_login_proto_rawDescOnce sync.Once
+	file_proto_login_proto_rawDescData = file_proto_login_proto_rawDesc
+)
+
+func file_proto_login_proto_rawDescGZIP() []byte {
+	file_proto_login_proto_rawDescOnce.Do(func() {
+		file_proto_login_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_login_proto_rawDescData)
+	})
+	return file_proto_login_proto_rawDescData
+}
+
+var file_proto_login_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_proto_login_proto_goTypes = []interface{}{
+	(*LoginReq)(nil),  // 0: proto.LoginReq
+	(*LoginResp)(nil), // 1: proto.LoginResp
+}
+var file_proto_login_proto_depIdxs = []int32{
+	0, // 0: proto.SimulateLogin.GetSimulateLoginInfo:input_type -> proto.LoginReq
+	1, // 1: proto.SimulateLogin.GetSimulateLoginInfo:output_type -> proto.LoginResp
+	1, // [1:2] is the sub-list for method output_type
+	0, // [0:1] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_proto_login_proto_init() }
+func file_proto_login_proto_init() {
+	if File_proto_login_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_proto_login_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LoginReq); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_proto_login_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LoginResp); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_proto_login_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_proto_login_proto_goTypes,
+		DependencyIndexes: file_proto_login_proto_depIdxs,
+		MessageInfos:      file_proto_login_proto_msgTypes,
+	}.Build()
+	File_proto_login_proto = out.File
+	file_proto_login_proto_rawDesc = nil
+	file_proto_login_proto_goTypes = nil
+	file_proto_login_proto_depIdxs = nil
+}

+ 22 - 0
src/login/proto/login.proto

@@ -0,0 +1,22 @@
+syntax = "proto3";
+
+option go_package = "/proto";
+
+package proto;
+
+
+//model
+message LoginReq {
+  string stype = 1;
+  string site  = 2;
+  string param = 3;
+  string header = 4;
+}
+message LoginResp {
+  string loginInfo = 1;
+}
+
+//service
+service SimulateLogin {
+  rpc GetSimulateLoginInfo(LoginReq) returns(LoginResp);
+}

+ 105 - 0
src/login/proto/login_grpc.pb.go

@@ -0,0 +1,105 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc             v3.19.4
+// source: proto/login.proto
+
+package proto
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// SimulateLoginClient is the client API for SimulateLogin service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type SimulateLoginClient interface {
+	GetSimulateLoginInfo(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginResp, error)
+}
+
+type simulateLoginClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewSimulateLoginClient(cc grpc.ClientConnInterface) SimulateLoginClient {
+	return &simulateLoginClient{cc}
+}
+
+func (c *simulateLoginClient) GetSimulateLoginInfo(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginResp, error) {
+	out := new(LoginResp)
+	err := c.cc.Invoke(ctx, "/proto.SimulateLogin/GetSimulateLoginInfo", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// SimulateLoginServer is the server API for SimulateLogin service.
+// All implementations must embed UnimplementedSimulateLoginServer
+// for forward compatibility
+type SimulateLoginServer interface {
+	GetSimulateLoginInfo(context.Context, *LoginReq) (*LoginResp, error)
+	mustEmbedUnimplementedSimulateLoginServer()
+}
+
+// UnimplementedSimulateLoginServer must be embedded to have forward compatible implementations.
+type UnimplementedSimulateLoginServer struct {
+}
+
+func (UnimplementedSimulateLoginServer) GetSimulateLoginInfo(context.Context, *LoginReq) (*LoginResp, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetSimulateLoginInfo not implemented")
+}
+func (UnimplementedSimulateLoginServer) mustEmbedUnimplementedSimulateLoginServer() {}
+
+// UnsafeSimulateLoginServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to SimulateLoginServer will
+// result in compilation errors.
+type UnsafeSimulateLoginServer interface {
+	mustEmbedUnimplementedSimulateLoginServer()
+}
+
+func RegisterSimulateLoginServer(s grpc.ServiceRegistrar, srv SimulateLoginServer) {
+	s.RegisterService(&SimulateLogin_ServiceDesc, srv)
+}
+
+func _SimulateLogin_GetSimulateLoginInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(LoginReq)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(SimulateLoginServer).GetSimulateLoginInfo(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/proto.SimulateLogin/GetSimulateLoginInfo",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(SimulateLoginServer).GetSimulateLoginInfo(ctx, req.(*LoginReq))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// SimulateLogin_ServiceDesc is the grpc.ServiceDesc for SimulateLogin service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var SimulateLogin_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "proto.SimulateLogin",
+	HandlerType: (*SimulateLoginServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "GetSimulateLoginInfo",
+			Handler:    _SimulateLogin_GetSimulateLoginInfo_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "proto/login.proto",
+}

+ 159 - 0
src/login/server.go

@@ -0,0 +1,159 @@
+package main
+
+import (
+	"context"
+	"encoding/json"
+	"github.com/imroc/req"
+	"google.golang.org/grpc"
+	"login/proto"
+	"mongodb"
+	"net"
+	"qfw/util"
+	"spiderutil"
+	"sync"
+	"time"
+)
+
+type Login struct {
+	Username string `bson:"username"`
+	Password string `bson:"password"`
+	Site     string `bson:"site"`
+	Url      string `bson:"url"`
+	Cookie   string `bson:"cookie"`
+}
+
+var (
+	MgoE      *mongodb.MongodbSim    //mgo链接
+	LoginColl string                 //登录信息存储表
+	LoginMap  map[string]*Login      //已保持登录的站点相关信息
+	LoginLock map[string]*sync.Mutex //当前正在模拟登录的站点信息
+)
+
+func init() {
+	util.ReadConfig(&spiderutil.Config) //初始化参数
+	//初始化mgo
+	MgoE = &mongodb.MongodbSim{
+		MongodbAddr: spiderutil.Config.BidEditor.Addr,
+		DbName:      spiderutil.Config.BidEditor.Db,
+		Size:        spiderutil.Config.BidEditor.Size,
+		UserName:    spiderutil.Config.BidEditor.Username,
+		Password:    spiderutil.Config.BidEditor.Password,
+	}
+	MgoE.InitPool()
+	//其它
+	LoginColl = spiderutil.Config.BidEditor.Coll
+	LoginMap = map[string]*Login{}
+	LoginLock = map[string]*sync.Mutex{}
+	//初始化已模拟登陆的网站信息
+	InitLoggedInfo()
+}
+
+func InitLoggedInfo() {
+	query := map[string]interface{}{
+		"delete": false,
+	}
+	list, _ := MgoE.Find("lua_login", query, nil, nil, false, -1, -1)
+	for _, l := range *list {
+		site := util.ObjToString(l["site"])
+		if loginByte, err := json.Marshal(l); err == nil {
+			login := &Login{}
+			if err = json.Unmarshal(loginByte, login); err != nil {
+				util.Debug(site, "初始化登录信息失败")
+			} else {
+				LoginMap[site] = login
+				LoginLock[site] = &sync.Mutex{}
+			}
+		} else {
+			util.Debug(site, "初始化登录信息失败")
+		}
+	}
+	util.Debug("当前共有登陆信息:", len(LoginMap))
+}
+
+type SimulateLoginService struct {
+	proto.SimulateLoginServer
+}
+
+//server
+func main() {
+	util.Debug("port", spiderutil.Config.ServerPort)
+	lis, err := net.Listen("tcp", spiderutil.Config.ServerPort)
+	if err != nil {
+		util.Debug("failed to listen: ", err)
+	}
+	//create a server
+	s := grpc.NewServer()
+	//注册事件
+	proto.RegisterSimulateLoginServer(s, &SimulateLoginService{})
+	s.Serve(lis)
+}
+
+func (login *SimulateLoginService) GetSimulateLoginInfo(ctx context.Context, req *proto.LoginReq) (*proto.LoginResp, error) {
+	defer util.Catch()
+	result := getCookie(req.Stype, req.Site, req.Param, req.Header)
+	util.Debug(result)
+	return &proto.LoginResp{
+		LoginInfo: result,
+	}, nil
+}
+
+func getCookie(stype, site, param, head string) (cookie string) {
+	LoginLock[site].Lock()
+	defer LoginLock[site].Unlock()
+	loginInfo := LoginMap[site]
+	if loginInfo == nil {
+		return "No Account Password!"
+	}
+	if stype == "login" { //登录
+		cookie = login(loginInfo.Url, param, head)
+		loginInfo.Cookie = cookie
+	} else { //获取
+		if loginInfo.Cookie != "" {
+			cookie = loginInfo.Cookie
+		} else { //登录
+			cookie = login(loginInfo.Url, param, head)
+			loginInfo.Cookie = cookie
+		}
+	}
+	return cookie
+}
+
+func login(url, param, head string) (cookie string) {
+	headers := map[string]string{}
+	data := map[string]string{}
+	if head != "" {
+		json.Unmarshal([]byte(head), &headers)
+	}
+	if param != "" {
+		json.Unmarshal([]byte(param), &data)
+	}
+	client := req.C().SetTimeout(spiderutil.Config.TimeOut * time.Second)
+	times := 0
+	for times <= 3 {
+		times++
+		if times == 3 { //第三次登录使用代理
+			//设置代理
+			proxyIp := spiderutil.GetProxyAddr(spiderutil.Config.ProxyAddr, spiderutil.Config.ProxyAuthor) //获取代理地址
+			util.Debug("proxy:", proxyIp)
+			client.SetProxyURL(proxyIp) //设置代理IP
+		}
+		request := client.R()
+		if len(headers) > 0 {
+			request.SetHeaders(headers)
+		}
+		resp, err := request.SetFormData(data).Post(url)
+		//resp, err := request.Post(url)
+		if err != nil || resp.StatusCode != 200 {
+			return ""
+		}
+		util.Debug(resp.Cookies())
+		if len(resp.Cookies()) != 0 {
+			cookieByte, err := json.Marshal(resp.Cookies())
+			if err == nil {
+				cookie = string(cookieByte)
+			}
+		}
+	}
+
+	return cookie
+}

+ 52 - 0
src/spiderutil/redisbloom.go

@@ -0,0 +1,52 @@
+package spiderutil
+
+import (
+	"fmt"
+	redisbloom "github.com/RedisBloom/redisbloom-go"
+	qu "qfw/util"
+	"strings"
+)
+
+var bloomRedisClient map[string]*redisbloom.Client
+
+func InitBloomRedisClient(addr string) {
+	bloomRedisClient = map[string]*redisbloom.Client{}
+	addrs := strings.Split(addr, ",") //暂时不支持同两个相同名称的bloom过滤器在不同服务器
+	for _, v := range addrs {
+		name_addr := strings.Split(v, "=")
+		pool := redisbloom.NewSingleHostPool(name_addr[1], nil)
+		bloomRedisClient[name_addr[0]] = &redisbloom.Client{
+			Pool: pool,
+		}
+	}
+}
+
+//Add
+func AddBloomRedis(name, key string) bool {
+	defer qu.Catch()
+	if bloomclient := bloomRedisClient[name]; bloomclient != nil {
+		exists, err := bloomclient.Add(name, key)
+		if err != nil {
+			fmt.Println("Error:", err)
+		}
+		return exists
+	} else {
+		fmt.Println("Redis Client is Null")
+		return false
+	}
+}
+
+//Exists
+func ExistsBloomRedis(name, key string) bool {
+	defer qu.Catch()
+	if bloomclient := bloomRedisClient[name]; bloomclient != nil {
+		exists, err := bloomclient.Exists(name, key)
+		if err != nil {
+			fmt.Println("Error:", err)
+		}
+		return exists
+	} else {
+		fmt.Println("Redis Client is Null")
+		return false
+	}
+}

+ 2 - 0
src/spiderutil/sysconfig.go

@@ -100,6 +100,8 @@ type config struct {
 	TimeOut                         time.Duration `json:"timeout"`
 	//js相关
 	JsServerAddress string `json:"jsserveraddress"`
+	//模拟登陆
+	LoginServerAddress string `json:"loginserveraddress"`
 	//系统库
 	SystemDB dbInfo `json:"systemdb"`
 	//基本数据库