快速学习go-zero

go的web框架有很多,目前go的社区大家对于框架的态度也不尽相同,有些轻量级的框架,但是也就代表整合第三方中间件就需要自己根据客户端进行封装,比如gin+gorm,也有些功能完全但是被认为丢失了go本身轻量设计的初衷,
比如goframe,而同样的微服务有很多框架,国内比较出门的就是go-zero ,有专门的开发工具goctl,让开发者只需要关注业务代码即可完成微服务的上线。

介绍

go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。

go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。

开发者只需要编写业务代码 就可以完成微服务的构建
红色代表需要开发者手写的部分!
在这里插入图片描述

所以重点是通过goctl 来根据编写的api文件和proto文件快速生成代码,让大部分时间只关心业务,缩短时间成本,这一点使用起来比goframe成熟一些(个人感觉),写下该笔记也是因为现在看文档的时候觉得go-zero 没有之前的文档那样对新手友好了

安装教程

安装官方脚手架 go 从ctroller

#官方脚手架
go install github.com/zeromicro/go-zero/tools/goctl@latest
#protobuf 工具
goctl env check --install --verbose --force
#框架
go get -u github.com/zeromicro/go-zero@latest

验证版本

goctl -version

快速入门

web模块

//生成 api web模块 目录greet
goctl api new greet
cd greet
go mod init
go mod tidy
go run greet.go -f etc/greet-api.yaml

此时访问localhost:8888就会得到一个null 但是控制器有对应的输出日志

根据api文件 生成web服务 官方的快速入门案列 zero-doc/doc/shorturl.md at main · zeromicro/zero-doc (github.com)

#生成一个基本的api文件 api是go-zero的一个文件 用于goctl来快速的生成web代码
goctl api -o shorturl.api

替换内容为

type (expandReq {shorten string `form:"shorten"`}expandResp {url string `json:"url"`}
)type (shortenReq {url string `form:"url"`}shortenResp {shorten string `json:"shorten"`}
)service shorturl-api {@handler ShortenHandlerget /shorten(shortenReq) returns(shortenResp)@handler ExpandHandlerget /expand(expandReq) returns(expandResp)
}
  • service shorturl-api { 这一行定义了 service 名字

  • @server 部分用来定义 server 端用到的属性

  • handler 定义了服务端 handler 名字

  • get /shorten(shortenReq) returns(shortenResp) 定义了 get 方法的路由、请求参数、返回参数等

  • type生成的交互结构体也会在对应文件夹

感觉和proto文件差不多 也是修改该文件快速生成代码

进行该目录 根据api文件 生成代码

goctl api go -api shorturl.api -dir .

后续如果数据结构以及更新接口也是编辑api文件,编辑生成的go文件会报错不应该生成源文件

goctl api go -api order.api -dir .

并且不会覆盖已经写好的逻辑

etc/web-api.yaml exists, ignored generation
internal/config/config.go exists, ignored generation
web.go exists, ignored generation
internal/svc/servicecontext.go exists, ignored generation
internal/handler/webhandler.go exists, ignored generation
internal/handler/orderhandler.go exists, ignored generation
internal/logic/weblogic.go exists, ignored generation
internal/logic/orderlogic.go exists, ignored generation
Done.
生成的web目录结构

.
├── api
│ ├── etc
│ │ └── shorturl-api.yaml // 配置文件
│ ├── internal
│ │ ├── config
│ │ │ └── config.go // 定义配置
│ │ ├── handler
│ │ │ ├── expandhandler.go // 实现 expandHandler
│ │ │ ├── routes.go // 定义路由处理
│ │ │ └── shortenhandler.go // 实现 shortenHandler
│ │ ├── logic
│ │ │ ├── expandlogic.go // 实现 ExpandLogic
│ │ │ └── shortenlogic.go // 实现 ShortenLogic
│ │ ├── svc
│ │ │ └── servicecontext.go // 定义 ServiceContext
│ │ └── types
│ │ └── types.go // 定义请求、返回结构体
│ ├── shorturl.api
│ └── shorturl.go // main 入口定义
├── go.mod
└── go.sum

在这里插入图片描述

  • type存放api生成的接口响应值和请求值

  • svc上下文变量 在rpc模块的时候需要经常用到

  • logic 是逻辑实现模块 类似mvc中接口的实现类

  • handler为路由注册,一个路由/XXX/XX 对应一个handler

    • 任意打开生成的一个handler

      在这里插入图片描述

      和gin goframe一样也是对原生http请求进行封装 其中调用logic把开发者的逻辑和返回值写回http响应体

  • config 运行时候的上下文配置

运行

go run shorturl.go -f etc/shorturl.yaml

启动类解析代码

#如果把这里的目录地址换成项目的引用地址
var configFile = flag.String("f", "shorturl/api/etc/shorturl.yaml", "the config file")
#作用是自定义指令 如果没有使用-f指定配置文件 则默认是第二个参数位置
func main() {flag.Parse()var c config.Configconf.MustLoad(*configFile, &c)server := rest.MustNewServer(c.RestConf)//关闭服务defer server.Stop()ctx := svc.NewServiceContext(c)handler.RegisterHandlers(server, ctx)fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)server.Start()
}

就可以不用写-f参数 在idea运行

生成web模块的其他语言代码

goctl api java -api greet.api -dir greet
goctl api dart -api greet.api -dir greet

官网框架概述 | go-zero Documentation

rpc协议采用的是grpc 需要根据官网安装工具

rpc模块

新建一个目录

在 shorturl 目录下创建 rpc/transform 目录

rpc/transform

在 rpc/transform 目录下编写 transform.proto 文件

可以通过命令生成 proto 文件模板

goctl rpc -o transform.proto

修改后文件将内容替换如下:

syntax = "proto3";package transform;option go_package = "./transform";message expandReq{string shorten = 1;
}message expandResp{string url = 1;
}message shortenReq{string url = 1;
}message shortenResp{string shorten = 1;
}service  transformer{rpc expand(expandReq) returns(expandResp);rpc shorten(shortenReq) returns(shortenResp);
}

用 goctl 生成 rpc 代码,在 rpc/transform 目录下执行命令

goctl rpc protoc student.proto --go_out=. --go-grpc_out=. --zrpc_out=.

如果后续更新接口 响应体信息 生成代码单不覆盖已经写好的逻辑结构 支付该rpc 结构部分

 protoc --go_out=. --go-grpc_out=. order.proto

注意:不能在 GOPATH 目录下执行以上命令

文件结构如下:

rpc/transform
├── etc
│ └── transform.yaml // 配置文件
├── internal
│ ├── config
│ │ └── config.go // 配置定义
│ ├── logic
│ │ ├── expandlogic.go // expand 业务逻辑在这里实现
│ │ └── shortenlogic.go // shorten 业务逻辑在这里实现
│ ├── server
│ │ └── transformerserver.go // 调用入口, 不需要修改
│ └── svc
│ └── servicecontext.go // 定义 ServiceContext,传递依赖
├── transform
│ ├── transform.pb.go
│ └── transform_grpc.pb.go
├── transform.go // rpc 服务 main 函数
├── transform.proto
└── transformer
└── transformer.go // 提供了外部调用方法,无需修改
执行 go mod tidy 整理依赖

启动 etcd server (已经安装好)

etcd

启动 rpc 服务直接可以运行,如下:

go run transform.go -f etc/transform.yaml

Starting rpc server at 127.0.0.1:888…
查看服务是否注册,以下值为参考值,主要观察 etcd 有注册到 transform.rpc 的 key 和 8080 端口即可,各自机器的 ip 结果不一样。

etcdctl get transform.rpc --prefix
#PS C:\Users\侯> etcdctl get transform.rpc --prefix
#transform.rpc/7587881007321565706
#169.254.67.206:888
目录文件解析

在这里插入图片描述

大致和api web项目的目录差不多 区别在于没有handler

和pro文件名一样的是goctl生成服务端代码

在这里插入图片描述

logic用来实现

如果后续需要抛出的接口很多 就续写proto文件即可

 protoc --go_out=. --go-grpc_out=. student.proto   

因为rpc注册到etcd的时,服务之间内部采用key通信

在这里插入图片描述

模拟真实场景

现在为了模拟一个真实场景 有以下学生商城的业务需求 用户向网关发送请求(采用普通web服务模拟)传递订单id ,网关转发该请求到order,order拿到该id以后向学生服务发起请求获取学生info的业务场景

用户->网关(web)->orderservice->studentservice 这样的微服务查询

如果是在java的那一套,编写配置类,编写接口,响应体,请求体需要的体量就大一些,go-zero使用api文件和grpc(proto)文件完成微服务的快速编写

目录结构

在这里插入图片描述

一个网关 一个学生服务 一个订单

编写student-service

我个人的习惯对于需求的链式调用是从底层写到高层

student.proto

syntax = "proto3";
//指定生成的proto部分文件输出路径
option go_package="./student";
package student;service StudentService {rpc GetStudentInfo (StudentRequest) returns (StudentResponse);
}message StudentRequest {int32 student_id = 1;
}message StudentResponse {int32 student_id = 1;string name = 2;int32 age = 3;
}

定义了学生服务 ,该服务只有一个接口获取学生个人信息

goctl rpc protoc student.proto --go_out=. --go-grpc_out=. --zrpc_out=.

生成项目模板后 对grpc 只要熟悉的开发者就可以找到 在指定的输出路径有 生成的grpc服务和客户端

在这里插入图片描述

点击idea的实现提示就会跳转到 internal 目录下的server目录 ,而其中就包含 logic实列化来处理

func (s *StudentServiceServer) GetStudentInfo(ctx context.Context, in *student.StudentRequest) (*student.StudentResponse, error) {l := logic.NewGetStudentInfoLogic(ctx, s.svcCtx)return l.GetStudentInfo(in)
}

所以我们需要重写的就是生成在logic获取学生信息接口

模拟数据中,只要由id为1就可以成功调用

func (l *GetStudentInfoLogic) GetStudentInfo(req *student.StudentRequest) (*student.StudentResponse, error) {fmt.Println("学生服务被调用")fmt.Printf("得到id%d\n", req.StudentId)if req.StudentId == 1 {return &student.StudentResponse{StudentId: req.StudentId,Name:      "坏学生乔治",Age:       20,}, nil}return nil, nil
}

启动类的配置路径换成项目的引用路径

var configFile = flag.String("f", "quick_start/student_service/etc/student.yaml", "the config file")

点击启动

查看etcd keeper (etcd 的ui 插件) 可以看到已经成功注册了

在这里插入图片描述

提一下其中生成的studentservice 该目录包含了go-zero生成的接口交互服务文件,到时候服务之间交互就是引入的该文件


package studentserviceimport ("context""quickstart/quick_start/student_service/student""github.com/zeromicro/go-zero/zrpc""google.golang.org/grpc"
)type (StudentRequest  = student.StudentRequestStudentResponse = student.StudentResponseStudentService interface {GetStudentInfo(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (*StudentResponse, error)}defaultStudentService struct {cli zrpc.Client}
)func NewStudentService(cli zrpc.Client) StudentService {return &defaultStudentService{cli: cli,}
}func (m *defaultStudentService) GetStudentInfo(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (*StudentResponse, error) {client := student.NewStudentServiceClient(m.cli.Conn())return client.GetStudentInfo(ctx, in, opts...)
}
编写order-service

最底部的学生服务被定义好后 ,那么就要编写订单服务,和学生服务不同的是,订单服务因为调用了学生服务,那么订单服务就要聚合学生服务

在order目录编写该文件

编写order.proto

syntax = "proto3";
option go_package="./order";
package order;service OrderService {
rpc GetOrderInfo (OrderRequest) returns (OrderResponse);
}message OrderRequest {
int32 order_id = 1;
}message OrderResponse {
int32 order_id = 1;
int32 student_id = 2;
string data = 3;
}

该文件也是定义了一个接口,以及其中响应体定义的data 用于装载student服务响应的json对象字符串

goctl rpc protoc order.proto --go_out=. --go-grpc_out=. --zrpc_out=.

运行配置文件

自身也是微服务中的一环除去自身注册之外还需要定义一个需要引用到的学生rpc接口配置

etic/服务.yaml

Name: order-service
ListenOn: 127.0.0.1:9090
StudentRpcConf:Etcd:Hosts:- localhost:2379Key: student.rpcEtcd:Hosts:- 127.0.0.1:2379Key: order.rpc

上下文配置文件

因为需要使用到其他模块,go-zero服务之前的交互是通过封装的 zrpc等文件 所以需要引入上下文

internal中的config.go文件

package configimport "github.com/zeromicro/go-zero/zrpc"// Config is the configuration structure
type Config struct {zrpc.RpcServerConfStudentRpcConf zrpc.RpcClientConf
}

上下文件之中进行注册

svc目录下的上下文文件

package svcimport ("github.com/zeromicro/go-zero/zrpc""quickstart/quick_start/order_service/internal/config"
//引入的就是student中 goctl生成的交互客户端文件"quickstart/quick_start/student_service/studentservice"
)type ServiceContext struct {Config     config.ConfigStudentRpc studentservice.StudentService
}func NewServiceContext(c config.Config) *ServiceContext {return &ServiceContext{Config: c,//从配置文件服务端StudentRpc: studentservice.NewStudentService(zrpc.MustNewClient(c.StudentRpcConf)),}
}

好了那么就可以进行服务的逻辑实现 对应的逻辑实现

func (l *GetOrderInfoLogic) GetOrderInfo(req *order.OrderRequest) (*order.OrderResponse, error) {fmt.Println("订单服务服务被调用")fmt.Println("订单ID:", req.OrderId)if req.OrderId == 1 {// TODO: 模拟联查除订单id查询除学生idstudentResp, err := l.svcCtx.StudentRpc.GetStudentInfo(l.ctx, &student.StudentRequest{StudentId: 1})if err != nil {return nil, err}fmt.Println("学生信息:", studentResp)toString, _ := jsonx.MarshalToString(studentResp)return &order.OrderResponse{OrderId:   req.OrderId,StudentId: studentResp.StudentId,Data:      toString,}, nil}return nil, nil
}

到此位置 俩个微服务接口也就写完了,只需要编写网关 ,用户游览器给网关这个web服务发起请求,然后传递id 1就可以完成交互

和上面的一样修改启动类配置为引用地址启动

成功注册到etcd

在这里插入图片描述

web网关

编写web.api文件

syntax = "v1"type Request {Name string `path:"name,options=you|me"`
}type OderRequest {ID int `path:"id"`
}type Response {Message string `json:"message"`
}type OrderResponse {Message string `json:"message"`Data    string `json:"data"`Code    int    `json:"code"`
}service web-api {@handler WebHandlerget /from/:name (Request) returns (Response)@handler OrderHandlerget /order/:id (OderRequest) returns (OrderResponse)
}

有俩个接口 其中一个没有用 文件是从官网案列粘贴的模板 所以不管

web网关负责的是和直接用户交互,所以到时候其他服务获取的数据就装在data字段即可

goctl api go -api web.api -dir .

生成代码后 ,同理开发者只要写逻辑相关代码即可

配置文件

定义调用服务的相关配置


Name: web-api
Host: 0.0.0.0
Port: 8888
Mode: dev
OrderRpcConf:Etcd:Hosts:- localhost:2379Key: order.rpc

上下文运行环境配置中引入

这里引入的字段名和配置文件中对应,zrpc源码根据这个进行赋值

import ("github.com/zeromicro/go-zero/rest""github.com/zeromicro/go-zero/zrpc"
)type Config struct {rest.RestConf//定义引入的rpc服务OrderRpcConf zrpc.RpcClientConf
}

rpc服务注册到上下文

	"github.com/zeromicro/go-zero/zrpc""quickstart/quick_start/order_service/orderservice""quickstart/quick_start/web/internal/config"
)type ServiceContext struct {Config   config.ConfigOrderRpc orderservice.OrderService
}func NewServiceContext(c config.Config) *ServiceContext {return &ServiceContext{Config:   c,OrderRpc: orderservice.NewOrderService(zrpc.MustNewClient(c.OrderRpcConf)),}
}

logic完成调用逻辑

func (l *OrderLogic) Order(req *types.OderRequest) (resp *types.OrderResponse, err error) {// todo: add your logic here and delete this lineid := req.IDfmt.Printf("Orderid:%d", id)//新建一个rpc接口需要的参数o := new(order.OrderRequest)o.OrderId = int32(id)if info, err := l.svcCtx.OrderRpc.GetOrderInfo(l.ctx, o); nil != err {fmt.Printf("远程调用rpc失败")} else {resp := new(types.OrderResponse)fmt.Println("订单信息:", info)resp.Data = info.Dataresp.Code = 0resp.Message = "服务调用成"return resp, nil}//t := new(types.Response)return
}

启动网关

api文件中定义的路由 ,是restful形式

get /order/:id (OderRequest) returns (OrderResponse)

生成的代码自然也是

func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {server.AddRoutes([]rest.Route{{Method:  http.MethodGet,Path:    "/from/:name",Handler: WebHandler(serverCtx),},{Method:  http.MethodGet,Path:    "/order/:id",Handler: OrderHandler(serverCtx),},},)
}

游览器访问

http://localhost:8888/order/1

成功输出

在这里插入图片描述

被调用服务日志也是成功输出

在这里插入图片描述

那么go-zero微服务的快速开发模式其实以及了解完全了,
剩下的就是对框架本身的一些规则感觉和gin这些go web框架差不多的部分了,中间件,路由绑定,参数和返回和其他数据库,微服务的中间件的整合了,由于篇幅问题 下次笔者在进行书写

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/52752.shtml

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

相关文章

rockyliunx 救援模式下禁用docker

目录地址 /usr/lib/systemd/system/docker.service 进入系统界面: 选择系统 按E 按e出现 如下界面,找到 quite 后面添加 init/bin/bash 按 ctrl x 保存 后,到如下界面 加载文件系统为读写 输入命令 mount -o remount, rw / 修改docer.s…

docker的安装+docker镜像的基本操作

一.docker的介绍 1、Docker 是什么? Docker 是⼀个开源的应⽤容器引擎,可以实现虚拟化,完全采⽤“沙 盒”机制,容器之间不会存在任何接⼝。 Docker 通过 Linux Container(容器)技术将任意…

【线程池】

什么是线程池? 线程池是一个可以复用线程的技术。简单来说,线程池是一种基于池化技术的思想来管理线程的技术,旨在减少线程的创建和销毁次数,提高系统的响应速度和吞吐量。它预先创建了一定数量的线程,并将这些线程放…

Java中的定时器(Timer)

目录 一、什么是定时器? 二、标准库中的定时器 三、实现自定义定时器 一、什么是定时器? 定时器就像一个"闹钟",当它到达设定的时间后,就会执行预定的代码。 例如,我们在TCP的超时重传机制中讲过,如果服务器在规定…

DNS劫持问题

目录 DNS劫持概述 定义 图示 ​编辑图示说明 DNS劫持的原理 1. DNS请求与响应过程 图示 ​编辑2. 劫持发生点 本地劫持 路由器劫持 中间人攻击 图示 ​编辑图示说明 DNS劫持的影响 1. 对个人用户的影响 图示 ​编辑图示说明 2. 对企业的影响 图示 ​编辑图示…

0828作业+梳理

一、作业 代码&#xff1a; #include <iostream>using namespace std;using datatype int; //类型重命名 #define MAX 2 //宏定义 //结构体定义 struct Sqlist { private:datatype *data; //顺序表数组int size 0; //数组大小int len 0; …

Ubuntu 16.04下Firefox版本更新

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; 在Ubuntu 16.04上更新Firefox的过程可能涉及多个步骤&#xff0c;具体取决于你的需求&#xff0c;比如是要安装一个稳定版本&#xff0c;还是需要使用最新的开发者版本或beta版本。下面我将详细介绍如何在Ub…

Python酷库之旅-第三方库Pandas(105)

目录 一、用法精讲 456、pandas.DataFrame.rdiv方法 456-1、语法 456-2、参数 456-3、功能 456-4、返回值 456-5、说明 456-6、用法 456-6-1、数据准备 456-6-2、代码示例 456-6-3、结果输出 457、pandas.DataFrame.rtruediv方法 457-1、语法 457-2、参数 457-3…

搭建面向切面编程项目

此项目在整合Mybatis基础上修改&#xff0c;可参考主页的整合Mybatis文章 注解版本 第一步 引入maven坐标 <!-- 切面编程所需jar包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId>…

学生管理系统升级(登录注册 + 关联学生管理系统)

新增需求 这是在昨天的基础初代版本上面新增一个登录注册忘记密码的功能 需求分析 注册 登录 忘记密码 user类代码呈现 package StudentSystem;public class User {private String username;private String password;private String personID;private String phoneNumber;pu…

PHP同城派送多区域运营配送小程序源码

&#x1f69a;&#x1f4a8;「同城派送多区域运营小程序」——让每一份需求快速触达&#xff01;&#x1f308;&#x1f680; &#x1f525; 开篇燃爆&#xff1a;同城生活新风尚&#xff0c;一键速达不是梦&#xff01; Hey小伙伴们&#xff0c;你还在为找不到合适的同城服务…

推荐并整理一波vscode插件(哪些内置了,哪些好用)

文章目录 背景现在还在用的&#xff08;21款&#xff09;Chinese(Simplified)简体中文Chinese LoremLorem ipsumCode Runner&#xff08;很推荐&#xff09;Codeium: AI Coding Autocomplete&#xff08;推荐&#xff09;Draw.io IntegrationESLintHighlight Matching TagJavaS…

甄选范文“论软件系统建模方法及其应用”,软考高级论文,系统架构设计师论文

论文真题 软件系统建模(Software System Modeling)是软件开发中的重要环节,通过构建软件系统模型可以帮助系统开发人员理解系统、抽取业务过程和管理系统的复杂性,也可以方便各类人员之间的交流。软件系统建模是在系统需求分析和系统实现之间架起的一座桥梁,系统开发人员…

【二叉树】OJ题目

&#x1f31f;个人主页&#xff1a;落叶 目录 单值⼆叉树 【单值二叉树】代码 相同的树 【相同二叉树】代码 对称⼆叉树 【对称二叉树】代码 另一颗树的子树 【另一颗树的子树】代码 二叉树的前序遍历 【二叉树前序遍历】代码 二叉树的中序遍历 【二叉树中序遍历】…

NVIDIA将在Hot Chips 2024会议上展示Blackwell服务器装置

NVIDIA 将在 Hot Chips 2024 上展示其 Blackwell 技术堆栈&#xff0c;并在本周末和下周的主要活动中进行会前演示。对于 NVIDIA 发烧友来说&#xff0c;这是一个激动人心的时刻&#xff0c;他们将深入了解NVIDIA的一些最新技术。然而&#xff0c;Blackwell GPU 的潜在延迟可能…

【freeDiameter】服务端和客户端的连接流程

连接流程详解 进程启动时&#xff0c;先使用main_cmdline解析命令行参数&#xff0c;比如使用-c就会使用指定路径的配置文件&#xff0c;使用-d就会启用后台进程。 之后使用fd_core_initialize初始化核心库。具体会先使用fd_conf_init初始化配置&#xff0c;比如设置各项的默…

最长的一帧学习 part3

文章目录 八、osgUtil:: SceneView::cull ()part1 初始化必要的SceneView类成员变量part2 立体显示的处理part3 执行SceneView::cullStage函数&#xff0c;它也是场景视图筛选工作的核心函数part3.1 首先统计场景中的遮挡节点&#xff08;OccluderNode&#xff09;&#xff0c;…

缺失ffmpeg.dll要用什么修复方法?快速恢复丢失的ffmpeg.dll文件

多媒体软件用户常常会遭遇一个提示&#xff1a;系统无法找到ffmpeg.dll文件。这类情况经常在启动视频编辑软件、流媒体播放应用或其他音视频处理工具时出现&#xff0c;导致相关程序无法正确加载和执行。ffmpeg.dll是一种关键的动态链接库文件&#xff0c;负责处理复杂的视频和…

ssrf+redis未授权访问漏洞复现

ssrfredis未授权访问漏洞复现 一&#xff0c;pikachu靶场练习 docker拉取环境&#xff1a; docker run -d -p 8765:80 8023/pikachu-expect:latest国内很多加速源都用不成&#xff0c;配置代理拉取即可&#xff0c;配置方式如下&#xff1a; 1&#xff0c;新建目录 mkdir -…