js做网站跳转/公司建网站多少钱

js做网站跳转,公司建网站多少钱,平谷武汉阳网站建设,wordpress 重写url插件本文目录 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://www.mzph.cn/web/71463.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习反向传播

一、白话解释 梯度其实就是导数&#xff0c;除了用符号求导也可以用近似求导&#xff1a; 然后更新ww-学习率*导数 反向传播就是链式求导 向前计算&#xff1a;对每个节点求偏导 在前向传播的时候&#xff0c;进行一次前向计算的时候就可以把每一条线的偏导数都知道 前向传…

JavaWeb-HttpServletRequest请求域接口

文章目录 HttpServletRequest请求域接口HttpServletRequest请求域接口简介关于请求域和应用域的区别 请求域接口中的相关方法获取前端请求参数(getParameter系列方法)存储请求域名参数(Attribute系列方法)获取客户端的相关地址信息获取项目的根路径 关于转发和重定向的细致剖析…

deepseek在pycharm 中的配置和简单应用

对于最常用的调试python脚本开发环境pycharm&#xff0c;如何接入deepseek是我们窥探ai代码编写的第一步&#xff0c;熟悉起来总没坏处。 1、官网安装pycharm社区版&#xff08;免费&#xff09;&#xff0c;如果需要安装专业版&#xff0c;需要另外找破解码。 2、安装Ollama…

动态规划01背包问题系列一>最后一块石头的重量II

这里写目录标题 题目分析&#xff1a;状态表示&#xff1a;状态转移方程&#xff1a;初始化&#xff1a;填表顺序&#xff1a;返回值&#xff1a;代码呈现&#xff1a;优化版本&#xff1a;代码呈现&#xff1a; 题目分析&#xff1a; 状态表示&#xff1a; 状态转移方程&#…

逐行拆解 C 语言:数据类型、变量

今日&#xff0c;我们即将踏上一段充满趣味与挑战的学习之旅&#xff0c;深度钻研数据类型的多样奥秘&#xff0c;解锁变量创建的实用技巧。不仅如此&#xff0c;还会邂逅两个实用的基础库函数&#xff0c;探索它们在程序中穿针引线的奇妙作用。同时&#xff0c;几个简洁却强大…

【音视频】ffplay简单过滤器

一、ffplay简单过滤器 视频旋转&#xff1a;借助transpose滤镜 ffplay -i 1.mp4 -vf transpose1这里选择不同的数字是不同的方向&#xff1a; 视频翻转&#xff1a;借助hflip/vflip实现水平和垂直翻转&#xff1a; 水平翻转 ffplay 1.mp4 -vf hflip垂直翻转 ffplay 1.mp4 …

4G工业路由器在公交充电桩中的应用与优势

随着电动公交车的普及&#xff0c;公交充电桩的稳定运行和高效管理是交通营运部门最关心的问题。4G工业路由器凭借其卓越的数据采集和通讯能力&#xff0c;成为实现充电桩智能化管理的关键。 公交充电桩运维管理需求概述&#xff1a; 1.实时性&#xff1a;实时监控充电状态、剩…

【LangChain 数据连接封装】 文档加载器、文档处理器

小结&#xff1a; 文档处理部分&#xff0c;建议在实际应用中详细测试后使用与向量数据库的链接部分本质是接口封装&#xff0c;向量数据库需要自己选型类似 LlamaIndex&#xff0c;LangChain 也提供了丰富的 Document Loaders DocumentLoaders和 Text Splitters Text Splitte…

Springboot集成dubbo完整过程(三)

准备工作 1&#xff0c;准备mysql服务环境2&#xff0c;准备redis服务环境3&#xff0c;准备zookeeper服务环境4&#xff0c;准备逆向生成bean的xml配置文件5&#xff0c;准备slf4j日志xml配置文件6&#xff0c;准备一个sql脚本 1&#xff0c;搭建创建服务工程 1&#xff0c;创…

电池管理系统(BMS)架构详细解析:原理与器件选型指南

BMS&#xff08;电池管理系统&#xff09;架构详细讲解 从你提供的BMS&#xff08;Battery Management System&#xff09;架构图来看&#xff0c;主要涉及到电池监控模块、通信模块、功率控制模块等部分。下面我将详细讲解该架构的各个功能模块及其工作原理。 1. 电池管理核…

决策树(Decision Tree)基础知识

目录 一、回忆1、*机器学习的三要素&#xff1a;1&#xff09;*函数族2&#xff09;*目标函数2.1&#xff09;*模型的其他复杂度参数 3&#xff09;*优化算法 2、*前处理/后处理1&#xff09;前处理&#xff1a;特征工程2&#xff09;后处理&#xff1a;模型选择和模型评估 3、…

Git 批量合并 Commit 并且保留之前的 Commit 快速实现的思路

文章目录 需求Rebase / Pick / squashVim 的快速全局字符串替换 需求 我想把如下的提交 commit&#xff0c;变成一个 Commit&#xff0c;并且合并这些 Commit 的消息到一个节点 Rebase / Pick / squash 我合并到 5e59217 这个hash 上&#xff0c;这样合并后会保留两个 Commit…

基于海思soc的智能产品开发(芯片sdk和linux开发关系)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 随着国产化芯片的推进&#xff0c;在soc领域&#xff0c;越来越多的项目使用国产soc芯片。这些soc芯片&#xff0c;通常来说运行的os不是linux&…

论坛系统测试报告

目录 一、项目背景二、论坛系统测试用例思维导图三、论坛系统测试3.1界面测试3.2登陆测试3.3主页测试3.4个人中心测试 四、自动化测试脚本4.1配置驱动4.2创建浏览器类4.3功能测试4.3.1登陆测试4.3.2注册测试4.3.3主页测试4.3.4帖子编辑4.3.5运行主代码 五、BUG分析六、测试总结…

python量化交易——金融数据管理最佳实践——使用qteasy大批量自动拉取金融数据

文章目录 使用数据获取渠道自动填充数据QTEASY数据拉取功能数据拉取接口refill_data_source()数据拉取API的功能特性多渠道拉取数据实现下载流量控制实现错误重试日志记录其他功能 qteasy是一个功能全面且易用的量化交易策略框架&#xff0c; Github地址在这里。使用它&#x…

upload-labs靶场 1-21通关

目录 1.Pass-01 前端绕过 分析 解题 2.Pass-02 服务器端检测--修改IMME 分析 解题 3.Pass-03 黑名单绕过 分析 解题 4.Pass-04 .htaccess绕过 分析 解题 5.Pass-05 . .绕过和.user.ini绕过 分析 解题 6.Pass-06 大小写绕过 分析 解题 7.Pass-07 空格绕过 分…

【工具】COME对比映射学习用于scRNA-seq数据的空间重构

介绍 单细胞RNA测序&#xff08;scRNA-seq&#xff09;能够在单细胞分辨率下实现高通量转录组分析。固有的空间位置对于理解单细胞如何协调多细胞功能和驱动疾病至关重要。然而&#xff0c;在组织分离过程中&#xff0c;空间信息常常丢失。空间转录组学&#xff08;ST&#xf…

Idea配置注释模板

一、配置类注释模板 打开IDEA&#xff0c;打开settings(快捷键&#xff1a;Ctrl Alt s)&#xff0c;选择Editor&#xff0c;找到File and Code Templates 这里以设置class文件为例&#xff0c;点击Class&#xff0c;在右侧配置以下内容 #if (${PACKAGE_NAME} && $…

从开源大模型工具Ollama存在安全隐患思考企业级大模型应用如何严守安全红线

近日&#xff0c;国家网络安全通报中心通报大模型工具Ollama默认配置存在未授权访问与模型窃取等安全隐患&#xff0c;引发了广泛关注。Ollama作为一款开源的大模型管理工具&#xff0c;在为用户提供便捷的同时&#xff0c;却因缺乏有效的安全管控机制&#xff0c;存在数据泄露…

初识Qt · 信号与槽 · 基础知识

目录 前言&#xff1a; 信号和槽初识 两个问题 前言&#xff1a; 本文我们正式开始介绍信号与槽这个概念&#xff0c;在谈及Qt中的信号与槽这个概念之前&#xff0c;我们不妨回顾一下Linux中的信号&#xff0c;比如发生了除0错误&#xff0c;OS就会给该进程发送一个信号&am…