当前位置: 首页 > wzjs >正文

如何查询网站是织梦做的做网站公证需要费用是多少

如何查询网站是织梦做的,做网站公证需要费用是多少,石狮网站建设公司,超越时空网上书城网站建设方案本文目录 1. 回顾2. Zap日志3. 配置4. 引入gprc梳理gRPC思路优雅关闭gRPC 1. 回顾 上篇文章我们进行了路由搭建,引入了redis,现在来看看对应的效果。 首先先把前端跑起来,然后点击注册获取验证码。 再看看控制台输出和redis是否已经有记录&…

在这里插入图片描述

本文目录

  • 1. 回顾
  • 2. Zap日志
  • 3. 配置
  • 4. 引入gprc
    • 梳理gRPC思路
    • 优雅关闭gRPC

1. 回顾

上篇文章我们进行了路由搭建,引入了redis,现在来看看对应的效果。

首先先把前端跑起来,然后点击注册获取验证码。

在这里插入图片描述

再看看控制台输出和redis是否已经有记录,验证没问题,现在redis这个环节是打通了。

在这里插入图片描述

2. Zap日志

go中原生的日志比较一般,我们可以集成一个流行的日志库进来。

这里用uber开源的zap日志库,在common路径下安装zap:go get -u go.uber.org/zap。

然后再安装一个日志分割库,go get -u github.com/natefinch/lumberjack,因为日志的存储有几种方式,比如按照日志级别将日志记录到不同的文件,按照业务来分别记录不同级别的日志,按照包结构划分记录不同级别日志。debug级别以上记录一个,info以上记录一个,warn以上记录一个

common路径下创建logs.go,然后编写对应的代码。

package logsimport ("github.com/gin-gonic/gin""github.com/natefinch/lumberjack""go.uber.org/zap""go.uber.org/zap/zapcore""net""net/http""net/http/httputil""os""runtime/debug""strings""time"
)var lg *zap.Loggertype LogConfig struct {DebugFileName string `json:"debugFileName"`InfoFileName  string `json:"infoFileName"`WarnFileName  string `json:"warnFileName"`MaxSize       int    `json:"maxsize"`MaxAge        int    `json:"max_age"`MaxBackups    int    `json:"max_backups"`
}// InitLogger 初始化Logger
func InitLogger(cfg *LogConfig) (err error) {writeSyncerDebug := getLogWriter(cfg.DebugFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)writeSyncerInfo := getLogWriter(cfg.InfoFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)writeSyncerWarn := getLogWriter(cfg.WarnFileName, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)encoder := getEncoder()//文件输出debugCore := zapcore.NewCore(encoder, writeSyncerDebug, zapcore.DebugLevel)infoCore := zapcore.NewCore(encoder, writeSyncerInfo, zapcore.InfoLevel)warnCore := zapcore.NewCore(encoder, writeSyncerWarn, zapcore.WarnLevel)//标准输出consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())std := zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel)core := zapcore.NewTee(debugCore, infoCore, warnCore, std)lg = zap.New(core, zap.AddCaller())zap.ReplaceGlobals(lg) // 替换zap包中全局的logger实例,后续在其他包中只需使用zap.L()调用即可return
}func getEncoder() zapcore.Encoder {encoderConfig := zap.NewProductionEncoderConfig()encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoderencoderConfig.TimeKey = "time"encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoderencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoderencoderConfig.EncodeCaller = zapcore.ShortCallerEncoderreturn zapcore.NewJSONEncoder(encoderConfig)
}func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {lumberJackLogger := &lumberjack.Logger{Filename:   filename,MaxSize:    maxSize,MaxBackups: maxBackup,MaxAge:     maxAge,}return zapcore.AddSync(lumberJackLogger)
}// GinLogger 接收gin框架默认的日志
func GinLogger() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()path := c.Request.URL.Pathquery := c.Request.URL.RawQueryc.Next()cost := time.Since(start)lg.Info(path,zap.Int("status", c.Writer.Status()),zap.String("method", c.Request.Method),zap.String("path", path),zap.String("query", query),zap.String("ip", c.ClientIP()),zap.String("user-agent", c.Request.UserAgent()),zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),zap.Duration("cost", cost),)}
}// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
func GinRecovery(stack bool) gin.HandlerFunc {return func(c *gin.Context) {defer func() {if err := recover(); err != nil {// Check for a broken connection, as it is not really a// condition that warrants a panic stack trace.var brokenPipe boolif ne, ok := err.(*net.OpError); ok {if se, ok := ne.Err.(*os.SyscallError); ok {if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {brokenPipe = true}}}httpRequest, _ := httputil.DumpRequest(c.Request, false)if brokenPipe {lg.Error(c.Request.URL.Path,zap.Any("error", err),zap.String("request", string(httpRequest)),)// If the connection is dead, we can't write a status to it.c.Error(err.(error)) // nolint: errcheckc.Abort()return}if stack {lg.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),zap.String("stack", string(debug.Stack())),)} else {lg.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),)}c.AbortWithStatus(http.StatusInternalServerError)}}()c.Next()}
}

然后在main.go中来初始化我们的日志。

在这里插入图片描述
然后可以把对应的log地方进行更改了,比如下面的地方。
在这里插入图片描述
然后来验证一下是否能正常生成日志文件,正常生成了,没问题。
正常生成了

3. 配置

日志我们用了zap做集成,算是一个改进,但是配置比较复杂, 所以我们这里需要进一步优化这个配置。

配置我们引入viper进行操作,也非常简单,直接上图和代码吧,在user里边装viper这个包。

go get github.com/spf13/viper

首先在user下面创建cofig目录,然后创建config.yaml配置文件,然后创建config.go代码读取配置。

在这里插入图片描述

config.go的代码如下所示。

package configimport ("github.com/go-redis/redis/v8""github.com/spf13/viper""log""os""test.com/project-common/logs"
)var C = InitConfig()type Config struct {viper *viper.ViperSC    *ServerConfig
}type ServerConfig struct {Name stringAddr string
}func InitConfig() *Config {conf := &Config{viper: viper.New()}workDir, _ := os.Getwd()conf.viper.SetConfigName("config")conf.viper.SetConfigType("yaml")conf.viper.AddConfigPath(workDir + "/config")conf.viper.AddConfigPath("etc/msproject/user")//读取configerr := conf.viper.ReadInConfig()if err != nil {log.Fatalln(err)}conf.ReadServerConfig()conf.InitZapLog() //初始化zap日志return conf
}func (c *Config) InitZapLog() {//从配置中读取日志配置,初始化日志lc := &logs.LogConfig{DebugFileName: c.viper.GetString("zap.debugFileName"),InfoFileName:  c.viper.GetString("zap.infoFileName"),WarnFileName:  c.viper.GetString("zap.warnFileName"),MaxSize:       c.viper.GetInt("maxSize"),MaxAge:        c.viper.GetInt("maxAge"),MaxBackups:    c.viper.GetInt("maxBackups"),}err := logs.InitLogger(lc)if err != nil {log.Fatalln(err)}
}func (c *Config) ReadServerConfig() {sc := &ServerConfig{}sc.Name = c.viper.GetString("server.name")sc.Addr = c.viper.GetString("server.addr")c.SC = sc
}// 读redis的配置
func (c *Config) ReadRedisConfig() *redis.Options {return &redis.Options{Addr:     c.viper.GetString("redis.host") + ":" + c.viper.GetString("redis.port"),Password: c.viper.GetString("redis.password"), // no password setDB:       c.viper.GetInt("db"),                // use default DB}
}

对应的redis.go中原本new一个redis客户端的代码也需要更改了,改为已有的读取配置的函数 ReadRedisConfig()

在这里插入图片描述
并且把原来main.go中关于zap的相关配置文件删除即可。
在这里插入图片描述
然后重新启动下,看看是否能够运行,ok,启动没问题。

在这里插入图片描述

4. 引入gprc

可以通过引入一个API把对应的服务连起来,可以把各种服务提出来,然后通过API进行定义。

在这里插入图片描述
api\proto下新建一个名为login.service.proto的文件,然后编写代码。

syntax = "proto3";
package login.service.v1;
option go_package = "project-user/pkg/service/login.service.v1";message CaptchaMessage {string mobile = 1;
}
message CaptchaResponse{
}
service LoginService {rpc GetCaptcha(CaptchaMessage) returns (CaptchaResponse) {}
}

然后在proto路径下,运行命令:protoc --go_out=./gen --go_opt=paths=source_relative --go-grpc_out=./gen --go-grpc_opt=paths=source_relative login_service.proto,就可以生成对应文件了。

因为是第一版,所以我们先在gen下生成,然后复制移动到service下面,防止后面不断根据功能进行修改,而导致新生成的被覆盖。

那么我们来看看这login-service_grpc.pb.go文件到底生成了什么东西。

在这里插入图片描述


LoginServiceClient 是一个接口,定义了客户端可以调用的 GetCaptcha 方法。该方法接收一个 CaptchaMessage 请求,返回一个 CaptchaResponse 响应。

loginServiceClientLoginServiceClient 的具体实现,通过 NewLoginServiceClient 函数创建。它使用 grpc.ClientConnInterface 来发起 RPC 调用。

loginServiceClient.GetCaptcha 方法中,通过 c.cc.Invoke 发起 GetCaptcha 方法的 RPC 调用。它将请求数据序列化并发送到服务器,然后等待响应。

在这里插入图片描述

LoginServiceServer 是服务器端的接口,定义了 GetCaptcha 方法。所有实现该接口的服务器端逻辑必须嵌入 UnimplementedLoginServiceServer,以确保向前兼容性。
UnimplementedLoginServiceServer 提供了一个默认的未实现方法的错误响应,返回 codes.Unimplemented 状态码。

在这里插入图片描述

也就屙是说,接口还包含一个方法 mustEmbedUnimplementedLoginServiceServer(),这是一个空方法,用于确保实现者嵌入了 UnimplementedLoginServiceServer

这是 UnimplementedLoginServiceServerGetCaptcha 方法的默认实现。它返回 nil 作为响应,并通过 status.Errorf 返回一个带有 codes.Unimplemented 状态码的错误,表明该方法未被实现。这种设计确保了即使服务实现者没有实现某些方法,调用这些方法时也不会导致程序崩溃,而是返回一个明确的错误。

mustEmbedUnimplementedLoginServiceServer() 是一个空方法,用于确保服务实现者嵌入了 UnimplementedLoginServiceServer

testEmbeddedByValue() 是一个辅助方法,用于在运行时检查 UnimplementedLoginServiceServer 是否被正确嵌入(通过值而不是指针)。这避免了在方法调用时出现空指针引用。

type LoginServiceServer interface {GetCaptcha(context.Context, *CaptchaMessage) (*CaptchaResponse, error)mustEmbedUnimplementedLoginServiceServer()
}// UnimplementedLoginServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedLoginServiceServer struct{}func (UnimplementedLoginServiceServer) GetCaptcha(context.Context, *CaptchaMessage) (*CaptchaResponse, error) {return nil, status.Errorf(codes.Unimplemented, "method GetCaptcha not implemented")
}
func (UnimplementedLoginServiceServer) mustEmbedUnimplementedLoginServiceServer() {}
func (UnimplementedLoginServiceServer) testEmbeddedByValue()                      {}

所以主要是为了,确保向前兼容性:通过嵌入 UnimplementedLoginServiceServer,服务实现者可以在未来版本中添加新方法,而不会破坏现有实现。

梳理gRPC思路

首先我们实现了gRPC,那么原本的api下面的user相关的我们可以删除了。

来看看我们实现了什么。

首先main.go中的相关代码如下。

在这里插入图片描述
然后在service中我们实现了login_service.go代码,如下。

package login_service_v1import ("context""errors""fmt""go.uber.org/zap""log"common "test.com/project-common""test.com/project-common/logs""test.com/project-user/pkg/dao""test.com/project-user/pkg/repo""time"
)type LoginService struct {UnimplementedLoginServiceServercache repo.Cache
}func New() *LoginService {return &LoginService{cache: dao.Rc,}
}func (ls LoginService) GetCaptcha(ctx context.Context, msg *CaptchaMessage) (*CaptchaResponse, error) {//1.获取参数moblie := msg.Mobilefmt.Println(moblie)//2.校验参数if !common.VerifyMoblie(moblie) {return nil, errors.New("手机号不合法")}//3.生成验证码(随机4位或者6位)code := "123456"//4.调用短信平台(三方,放入go协程中执行,接口可以快速响应,短信几秒到无所谓)go func() {time.Sleep(1 * time.Second)zap.L().Info("短信平台调用成功,发送短信 INFO")logs.LG.Debug("短信平台调用成功,发送短信 debug")zap.L().Error("短信平台调用成功,发送短信 error")// redis 假设后续缓存在mysql或者mongo当中,也有可能存储在别的当中// 所以考虑用接口实现,面向接口编程“低耦合,高内聚“// 5.存储验证码redis,设置过期时间15分钟即可c, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()err := ls.cache.Put(c, "REGISTER_"+moblie, code, 15*time.Minute)if err != nil {log.Printf("验证码存入redis出错,causer by :%v\n", err)}log.Printf("将手机号和验证码存入redis成功:REGISTER %s : %s", moblie, code)}()return &CaptchaResponse{}, nil
}

并且在router中更新了如下代码。

package routerimport ("github.com/gin-gonic/gin""google.golang.org/grpc""log""net""test.com/project-user/config"loginServiceV1 "test.com/project-user/pkg/service/login.service.v1"
)// Router 接口
type Router interface {Route(r *gin.Engine)
}type RegisterRouter struct {
}func New() *RegisterRouter {return &RegisterRouter{}
}func (*RegisterRouter) Route(ro Router, r *gin.Engine) {ro.Route(r)
}var routers []Routerfunc InitRouter(r *gin.Engine) {for _, ro := range routers {ro.Route(r)}
}type gRPCConfig struct {Addr         stringRegisterFunc func(*grpc.Server)
}func RegisterGrpc() *grpc.Server {c := gRPCConfig{Addr: config.C.GC.Addr,RegisterFunc: func(g *grpc.Server) {//注册loginServiceV1.RegisterLoginServiceServer(g, loginServiceV1.New())}}s := grpc.NewServer()c.RegisterFunc(s)lis, err := net.Listen("tcp", c.Addr)if err != nil {log.Println("cannot listen")}//把服务放到协程里边go func() {err = s.Serve(lis)if err != nil {log.Println("server started error", err)return}}()return s
}

在这里插入图片描述

好,有点复杂,这里我们画图梳理下关系。

首先在router.go文件中,我们声明了RegisterGrpc(),这是gRPC服务的入口点,主要是配置grpc配置,包括服务地址还有注册函数,并且创建gRPC实例,然后注册登录服务,最后是启动gRPC服务器(在goroutine中运行的。)

login_service.go 中:LoginService 结构体:实现了 gRPC 服务接口,New() 函数:创建 LoginService 实例,GetCaptcha() 方法:实现具体的验证码获取业务逻辑。

所以调用关系如下。
在这里插入图片描述
在这里插入图片描述
所以为什么要使用协程?因为如果不使用协程,s.Serve(lis)会阻塞主线程,导致后续代码无法继续运行,这样可以运行gRPC服务器与HTTP服务器(gin)同时运行。

gRPC可以独立运行,不影响主程序的其他功能。


优雅关闭gRPC

在main函数中,还有个stop,这是闭包函数,说实话这是第一次看到闭包函数的使用场景,首先我们捕获了外部变量gc,gc也就是gRPC服务器实例,然后定义了服务关闭的具体行为,也就是停止gRPC服务,作为参数传给srv.Run。

当Run函数接受一个stop函数作为参数,注释一种依赖注入的设计模式,当收到指令之后,会把gRPC给关闭了。

虽然 stop 函数被传入,但它并不会立即执行,代码会在 <-quit 这行被阻塞。只有当程序收到 SIGINT 或 SIGTERM 信号时(比如按 Ctrl+C),才会继续往下执行,然后才会检查 stop != nil 并执行 stop 函数。


文章转载自:

http://O4KBc3Sj.jjzjn.cn
http://u8xY2grf.jjzjn.cn
http://XIX27QUN.jjzjn.cn
http://8O2UFrcs.jjzjn.cn
http://CpShKrUd.jjzjn.cn
http://1nbIbQ2q.jjzjn.cn
http://kOhMgmlQ.jjzjn.cn
http://B9k1bDdw.jjzjn.cn
http://0aeIjakP.jjzjn.cn
http://TjRKS2Fh.jjzjn.cn
http://F4pT3Ngk.jjzjn.cn
http://piMcuJc8.jjzjn.cn
http://6X6prnZF.jjzjn.cn
http://kBOM3vH6.jjzjn.cn
http://z3UbdfrC.jjzjn.cn
http://NCO8bRBK.jjzjn.cn
http://FCIIfS0E.jjzjn.cn
http://vetxdlRi.jjzjn.cn
http://1D8VWw94.jjzjn.cn
http://AdjkORdF.jjzjn.cn
http://zwoJUtnz.jjzjn.cn
http://KtZc4pr8.jjzjn.cn
http://MyuxSujQ.jjzjn.cn
http://QYc9tp9K.jjzjn.cn
http://Kw9ti2OZ.jjzjn.cn
http://YNJQUAqg.jjzjn.cn
http://lY4SRx3Y.jjzjn.cn
http://wV90D2WJ.jjzjn.cn
http://68jKsldl.jjzjn.cn
http://8RzSKVjs.jjzjn.cn
http://www.dtcms.com/wzjs/638076.html

相关文章:

  • 做破解网站合法wordpress 餐饮主题
  • 网站h1标签怎么做景德镇网站网站建设
  • 外贸类网站电视云网站建设
  • 安徽省建设厅官方网站进不去wordpress 股票
  • 网站建设的学习嘉兴做网络推广的公司
  • 网站上线盈利网站推广的优化
  • 网站建设服务标准化程序员做个网站要多少钱呢
  • 深圳php网站开发免费购物网站程序
  • 网站建设验收报告范本网站标签页在哪里设置
  • 做外贸要自己建网站吗网站建设要多少费用
  • 深圳市罗湖区住房和建设局网站wordpress右侧
  • 我做动作你来猜的网站安庆跨境电商建站哪家好
  • WordPress站内链接设置wordpress如何设置邮箱验证码
  • 制作网站软件wordpress高并发
  • 开发网站的基本过程襄阳seo公司
  • 相册特效手机网站文件管理
  • 网站建设和邮箱的关联做听书网站怎么做
  • 徐州网站开发要多少钱wordpress打印代码
  • 制作动画网站模板wordpress搬家 打开404
  • 网站设计的要素做医疗设备的网站
  • 国展网站建设php语言网站开发公司北京
  • 传媒公司营销网站谷歌seo推广
  • 自己做网站卖阀门网站如何免费做SEO优化
  • 在哪里有人做网站长春网站快照优化公司
  • 网站地图 htmlapp开发制作在哪儿
  • FLASK做wiki网站高密 网站建设
  • 自己做的网站如何用手机去查看海南 网站制作
  • 杭州网站推广营销怎么做电影网站app
  • 网站 例学校的网站的代码模板
  • 佛山网站建设定制wordpress图片主题中文版