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

go-zero开发入门之网关往rpc服务传递数据2

go-zero 的网关服务实际是个 go-zero 的 API 服务,也就是一个 http 服务,或者说 rest 服务。http 转 grpc 使用了开源的 grpcurl 库,当网关需要往 rpc 服务传递额外的数据,比如鉴权数据的时候,通过 http 的 header 进行:

func AuthMiddleware(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) {authResp, err := authClient.Authenticate(r.Context(), &authReq) // 调用鉴权服务r.Header.Set("Grpc-Metadata-myuid", authResp.UserId) // 往 rpc 服务传递额外数据next.ServeHTTP(w, r)
}

rpc 服务端从 metadata 取出数据:

func (l *QueryUserLogic) QueryUser(in *user.UserReq) (*user.UserResp, error) {vals := metadata.ValueFromIncomingContext(l.ctx, "gateway-myuid")uid = vals[0]
}

这里有两个需要注意的地方,在网关侧的名必须以“Grpc-Metadata-”打头,而 rpc 服务端必须以“gateway-”打头,这是 go-zero 的 gateway/internal/headerprocessor.go 写死的规则:

const (metadataHeaderPrefix = "Grpc-Metadata-"metadataPrefix       = "gateway-"
)// ProcessHeaders builds the headers for the gateway from HTTP headers.
func ProcessHeaders(header http.Header) []string {var headers []stringfor k, v := range header {if !strings.HasPrefix(k, metadataHeaderPrefix) { // 判断是否以“Grpc-Metadata-”打头(网关侧传递的)continue // 非以“Grpc-Metadata-”打头的都会被丢弃掉}key := fmt.Sprintf("%s%s", metadataPrefix, strings.TrimPrefix(k, metadataHeaderPrefix)) // 替换为新的前缀“gateway-”(rpc 服务端看到的)for _, vv := range v {headers = append(headers, key+":"+vv)}}return headers
}

调用栈:

(dlv) bt0  0x00000000019da092 in github.com/zeromicro/go-zero/gateway/internal.ProcessHeadersat ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/gateway/internal/headerprocessor.go:151  0x00000000019dc40a in github.com/zeromicro/go-zero/gateway.(*Server).prepareMetadataat ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/gateway/server.go:1752  0x00000000019dbf69 in github.com/zeromicro/go-zero/gateway.(*Server).buildHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/gateway/server.go:1323  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21224  0x00000000008ca162 in net/http.Handler.ServeHTTP-fmat <autogenerated>:15  0x000000000195adc5 in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21226  0x000000000195adc5 in gateway/middleware.AuthMiddleware // 网关代码at ./Getting-Started-with-Go-zero/gateway_login/gateway/middleware/login_and_auth.go:987  0x000000000195a325 in gateway/middleware.LoginAndAuthMiddleware.func1at ./Getting-Started-with-Go-zero/gateway_login/gateway/middleware/login_and_auth.go:378  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21229  0x0000000001969169 in github.com/zeromicro/go-zero/rest/handler.GunzipHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/gunziphandler.go:26
10  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
11  0x000000000196b3bf in github.com/zeromicro/go-zero/rest/handler.MaxBytesHandler.func2.1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/maxbyteshandler.go:24
12  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
13  0x000000000196ba15 in github.com/zeromicro/go-zero/rest/handler.MetricHandler.func1.1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/metrichandler.go:21
14  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
15  0x000000000196c243 in github.com/zeromicro/go-zero/rest/handler.RecoverHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/recoverhandler.go:21
16  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
17  0x000000000196d45c in github.com/zeromicro/go-zero/rest/handler.(*timeoutHandler).ServeHTTP.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/timeouthandler.go:82
18  0x0000000000471921 in runtime.goexitat /usr/local/go/src/runtime/asm_amd64.s:1598(dlv) bt0  0x00000000019d636a in google.golang.org/grpc/metadata.NewOutgoingContextat ./go/pkg/mod/google.golang.org/grpc@v1.59.0/metadata/metadata.go:1651  0x00000000019d636a in github.com/fullstorydev/grpcurl.InvokeRPCat ./go/pkg/mod/github.com/fullstorydev/grpcurl@v1.8.9/invoke.go:1362  0x00000000019dc058 in github.com/zeromicro/go-zero/gateway.(*Server).buildHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/gateway/server.go:1323  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21224  0x00000000008ca162 in net/http.Handler.ServeHTTP-fmat <autogenerated>:15  0x000000000195adc5 in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21226  0x000000000195adc5 in gateway/middleware.AuthMiddleware // 网关代码at ./Getting-Started-with-Go-zero/gateway_login/gateway/middleware/login_and_auth.go:987  0x000000000195a325 in gateway/middleware.LoginAndAuthMiddleware.func1at ./Getting-Started-with-Go-zero/gateway_login/gateway/middleware/login_and_auth.go:378  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:21229  0x0000000001969169 in github.com/zeromicro/go-zero/rest/handler.GunzipHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/gunziphandler.go:26
10  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
11  0x000000000196b3bf in github.com/zeromicro/go-zero/rest/handler.MaxBytesHandler.func2.1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/maxbyteshandler.go:24
12  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
13  0x000000000196ba15 in github.com/zeromicro/go-zero/rest/handler.MetricHandler.func1.1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/metrichandler.go:21
14  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
15  0x000000000196c243 in github.com/zeromicro/go-zero/rest/handler.RecoverHandler.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/recoverhandler.go:21
16  0x000000000089f52f in net/http.HandlerFunc.ServeHTTPat /usr/local/go/src/net/http/server.go:2122
17  0x000000000196d45c in github.com/zeromicro/go-zero/rest/handler.(*timeoutHandler).ServeHTTP.func1at ./go/pkg/mod/github.com/zeromicro/go-zero@v1.6.0/rest/handler/timeouthandler.go:82
18  0x0000000000471921 in runtime.goexitat /usr/local/go/src/runtime/asm_amd64.s:1598

在文件 zrpc/internal/clientinterceptors/tracinginterceptor.go 中调用了 metadata.NewOutgoingContext:

func startSpan(ctx context.Context, method, target string) (context.Context, trace.Span) {md, ok := metadata.FromOutgoingContext(ctx)if !ok {md = metadata.MD{}}tr := otel.Tracer(ztrace.TraceName)name, attr := ztrace.SpanInfo(method, target)ctx, span := tr.Start(ctx, name, trace.WithSpanKind(trace.SpanKindClient),trace.WithAttributes(attr...))ztrace.Inject(ctx, otel.GetTextMapPropagator(), &md)ctx = metadata.NewOutgoingContext(ctx, md)return ctx, span
}// UnaryTracingInterceptor returns a grpc.UnaryClientInterceptor for opentelemetry.
func UnaryTracingInterceptor(ctx context.Context, method string, req, reply any,cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {ctx, span := startSpan(ctx, method, cc.Target())defer span.End()ztrace.MessageSent.Event(ctx, 1, req)err := invoker(ctx, method, req, reply, cc, opts...)ztrace.MessageReceived.Event(ctx, 1, reply)if err != nil {s, ok := status.FromError(err)if ok {span.SetStatus(codes.Error, s.Message())span.SetAttributes(ztrace.StatusCodeAttr(s.Code()))} else {span.SetStatus(codes.Error, err.Error())}return err}span.SetAttributes(ztrace.StatusCodeAttr(gcodes.OK))return nil
}

拦截器:

./zrpc/internal/rpcserver.go:		interceptors = append(interceptors, serverinterceptors.UnaryTracingInterceptor)
./zrpc/internal/client.go:		interceptors = append(interceptors, clientinterceptors.UnaryTracingInterceptor)

服务端代码:

//zrpc/internal/rpcserver.go
func (s *rpcServer) buildUnaryInterceptors() []grpc.UnaryServerInterceptor {var interceptors []grpc.UnaryServerInterceptorif s.middlewares.Trace {interceptors = append(interceptors, serverinterceptors.UnaryTracingInterceptor)}if s.middlewares.Recover {interceptors = append(interceptors, serverinterceptors.UnaryRecoverInterceptor)}if s.middlewares.Stat {interceptors = append(interceptors,serverinterceptors.UnaryStatInterceptor(s.metrics, s.middlewares.StatConf))}if s.middlewares.Prometheus {interceptors = append(interceptors, serverinterceptors.UnaryPrometheusInterceptor)}if s.middlewares.Breaker {interceptors = append(interceptors, serverinterceptors.UnaryBreakerInterceptor)}return append(interceptors, s.unaryInterceptors...)
}func (s *rpcServer) Start(register RegisterFn) error {lis, err := net.Listen("tcp", s.address)if err != nil {return err}unaryInterceptorOption := grpc.ChainUnaryInterceptor(s.buildUnaryInterceptors()...)streamInterceptorOption := grpc.ChainStreamInterceptor(s.buildStreamInterceptors()...)options := append(s.options, unaryInterceptorOption, streamInterceptorOption)server := grpc.NewServer(options...)register(server)// register the health check serviceif s.health != nil {grpc_health_v1.RegisterHealthServer(server, s.health)s.health.Resume()}s.healthManager.MarkReady()health.AddProbe(s.healthManager)// we need to make sure all others are wrapped up,// so we do graceful stop at shutdown phase instead of wrap up phasewaitForCalled := proc.AddShutdownListener(func() {if s.health != nil {s.health.Shutdown()}server.GracefulStop()})defer waitForCalled()return server.Serve(lis)
}

客户端代码:

//zrpc/internal/client.go
func (c *client) buildUnaryInterceptors(timeout time.Duration) []grpc.UnaryClientInterceptor {var interceptors []grpc.UnaryClientInterceptorif c.middlewares.Trace {interceptors = append(interceptors, clientinterceptors.UnaryTracingInterceptor)}if c.middlewares.Duration {interceptors = append(interceptors, clientinterceptors.DurationInterceptor)}if c.middlewares.Prometheus {interceptors = append(interceptors, clientinterceptors.PrometheusInterceptor)}if c.middlewares.Breaker {interceptors = append(interceptors, clientinterceptors.BreakerInterceptor)}if c.middlewares.Timeout {interceptors = append(interceptors, clientinterceptors.TimeoutInterceptor(timeout))}return interceptors
}func (c *client) buildDialOptions(opts ...ClientOption) []grpc.DialOption {var cliOpts ClientOptionsfor _, opt := range opts {opt(&cliOpts)}var options []grpc.DialOptionif !cliOpts.Secure {options = append([]grpc.DialOption(nil),grpc.WithTransportCredentials(insecure.NewCredentials()))}if !cliOpts.NonBlock {options = append(options, grpc.WithBlock())}options = append(options,grpc.WithChainUnaryInterceptor(c.buildUnaryInterceptors(cliOpts.Timeout)...),grpc.WithChainStreamInterceptor(c.buildStreamInterceptors()...),)return append(options, cliOpts.DialOptions...)
}func (c *client) dial(server string, opts ...ClientOption) error {options := c.buildDialOptions(opts...)timeCtx, cancel := context.WithTimeout(context.Background(), dialTimeout)defer cancel()conn, err := grpc.DialContext(timeCtx, server, options...)if err != nil {service := serverif errors.Is(err, context.DeadlineExceeded) {pos := strings.LastIndexByte(server, separator)// len(server) - 1 is the index of last charif 0 < pos && pos < len(server)-1 {service = server[pos+1:]}}return fmt.Errorf("rpc dial: %s, error: %s, make sure rpc service %q is already started",server, err.Error(), service)}c.conn = connreturn nil
}// NewClient returns a Client.
func NewClient(target string, middlewares ClientMiddlewaresConf, opts ...ClientOption) (Client, error) {cli := client{middlewares: middlewares,}svcCfg := fmt.Sprintf(`{"loadBalancingPolicy":"%s"}`, p2c.Name)balancerOpt := WithDialOption(grpc.WithDefaultServiceConfig(svcCfg))opts = append([]ClientOption{balancerOpt}, opts...)if err := cli.dial(target, opts...); err != nil {return nil, err}return &cli, nil
}

相关文章:

go-zero开发入门之网关往rpc服务传递数据2

go-zero 的网关服务实际是个 go-zero 的 API 服务&#xff0c;也就是一个 http 服务&#xff0c;或者说 rest 服务。http 转 grpc 使用了开源的 grpcurl 库&#xff0c;当网关需要往 rpc 服务传递额外的数据&#xff0c;比如鉴权数据的时候&#xff0c;通过 http 的 header 进行…...

Cron介绍,以及常见的cron表达式

目录 一.cron介绍 1.什么是Cron&#xff1f; 2.Cron语法 时间字段的取值范围如下&#xff1a; 时间字段支持以下特殊字符&#xff1a; 下面是一些示例&#xff1a; 3.虚拟机安装cron(centos7展示) 二.常见的cron表达式 一.cron介绍 1.什么是Cron&#xff1f; Cron是一个…...

智能优化算法应用:基于协作搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于协作搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于协作搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.协作搜索算法4.实验参数设定5.算法结果6.…...

分布式训练通信NCCL之Ring-Allreduce详解

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…...

os_util 工具类和方法的实现

一、前置说明 总体目录&#xff1a;《从 0-1 搭建企业级 APP 自动化测试框架》上节回顾&#xff1a;在 init_appium_and_devices 的实现思路分析 小节中&#xff0c;分析了实现 init_appium_and_devices 的思路&#xff0c;梳理出了必要的工具类和方法。本节目标&#xff1a;完…...

uview表单校验带星号

uView表单校验带星号可以通过设置required属性来实现。在uView中&#xff0c;可以使用组件来实现表单校验&#xff0c;具体步骤如下&#xff1a; 1、在需要校验的表单元素上添加required属性&#xff0c;例如&#xff1a; <u-form :model"detailInfo" ref"d…...

vue+element实现动态表格:根据后台返回的属性名和字段动态生成可变表格

现有一个胡萝卜厂生产不同品种的胡萝卜&#xff0c;为了便于客户了解产品&#xff0c;现需在官网展示胡萝卜信息。现有的萝卜信息&#xff1a;编号&#xff08;id&#xff09;、名称&#xff08;name&#xff09;、保质期&#xff08;age&#xff09;、特点&#xff08;remark&…...

云渲染UE4像素流送搭建(winows、ubuntu单实例与多实例像素流送)

windows/ubuntu20.4下UE4.27.2像素流送 像素流送技术可以将服务器端打包的虚幻引擎应用程序在客户端的浏览器上运行&#xff0c;用户可以通过浏览器操作虚幻引擎应用程序&#xff0c;客户端无需下载虚幻引擎&#xff0c;本文实现两台机器通过物理介质网线实现虚幻引擎应用程序…...

Unity VR Pico apk安装失败:INSTALL_FAILED_UPDATE_INCOMPATIBLE

我的报错&#xff1a; PICO4企业版。安装apk&#xff0c;报错“安装失败。&#xff08;所属的Unity项目打包的apk&#xff0c;被我在同一台pico4安装了20次&#xff09; 调试方法&#xff1a; PIco4发布使用UNITY开发的Vr应用&#xff0c;格式为apk&#xff0c;安装的时候发生…...

Prompt 提示工程学习笔记

一、Prompt设计的四个关键要素&#xff1a; 任务描述、输入数据、上下文信息、提示风格 &#xff08;1&#xff09;任务描述&#xff1a;描述想要让LLM遵循的指令。描述应详细清晰&#xff0c;可进一步使用关键词突出特殊设置&#xff0c;从而更好地指导LLM工作。 &#xff0…...

STM32实现三个小灯亮

led.c #include"led.h"void Led_Init(void) {GPIO_InitTypeDef GPIO_VALUE; //???RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???GPIO_VALUE.GPIO_ModeGPIO_Mode_Out_PP;//???? ????GPIO_VALUE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2|GPIO_P…...

1861_什么是H桥

Grey 全部学习内容汇总&#xff1a; GitHub - GreyZhang/g_hardware_basic: You should learn some hardware design knowledge in case hardware engineer would ask you to prove your software is right when their hardware design is wrong! 1861_什么是H桥 H桥电路可以…...

【计算机四级(网络工程师)笔记】操作系统运行机制

目录 一、中央处理器&#xff08;CPU&#xff09; 1.1CPU的状态 1.2指令分类 二、寄存器 2.1寄存器分类 2.2程序状态字&#xff08;PSW&#xff09; 三、系统调用 3.1系统调用与一般过程调用的区别 3.2系统调用的分类 四、中断与异常 4.1中断 4.2异常 &#x1f308;嗨&#xff…...

Swagger快速入门

1、Swagger快速入门 1.1 swagger介绍 官网&#xff1a;https://swagger.io/ Swagger 是一个规范和完整的Web API框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。 功能主要包含以下几点: A. 使得前后端分离开发更加方便&#xff0c;有利于团队协作…...

数据结构之<堆>的介绍

1.简介 堆是一种特殊的数据结构&#xff0c;通常用于实现优先队列。堆是一个可以被看作近似完全二叉树的结构&#xff0c;并且具有一些特殊的性质&#xff0c;根据这些性质&#xff0c;堆被分为最大堆&#xff08;或者大根堆&#xff0c;大顶堆&#xff09;和最小堆两种。 2.…...

使用Ubuntu22+Minikube快速搭建K8S开发环境

安装Vmware 这一步&#xff0c;可以参考我的如下课程。 安装Ubuntu22 下载ISO镜像 这里我推荐从清华镜像源下载&#xff0c;速度会快非常多。 下载地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/22.04.3/ 如果你报名了我的这门视频课程&#xf…...

【中小型企业网络实战案例 二】配置网络互连互通

​【中小型企业网络实战案例 一】规划、需求和基本配置-CSDN博客 热门IT技术视频教程&#xff1a;https://xmws-it.blog.csdn.net/article/details/134398330?spm1001.2014.3001.5502 配置接入层交换机 1.以接入交换机ACC1为例&#xff0c;创建ACC1的业务VLAN 10和20。 <…...

Azure Machine Learning - Azure OpenAI GPT 3.5 Turbo 微调教程

本教程将引导你在Azure平台完成对 gpt-35-turbo-0613 模型的微调。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0c;阿里云认证的资深架构师&…...

运维大模型探索之 Text2PromQL 问答机器人

作者&#xff1a;陈昆仪&#xff08;图杨&#xff09; 大家下午好&#xff0c;我是来自阿里云可观测团队的算法工程师陈昆仪。今天分享的主题是“和我交谈并获得您想要的PromQL”。今天我跟大家分享在将AIGC技术运用到可观测领域的探索。 今天分享主要包括5个部分&#xff1a;…...

虚拟机VMware:变动ip修改固定ip

1、配置ip地址 vi /etc/sysconfig/network-scripts/ifcfg-ens33修改为&#xff1a; 修改如下&#xff1a;TYPE"Ethernet" # 网络类型为以太网 BOOTPROTO"static" # 手动分配ip NAME"ens33" # 网卡…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践&#xff0c;很多人以为AI已经强大到不需要程序员了&#xff0c;其实不是&#xff0c;AI更加需要程序员&#xff0c;普通人…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析

MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录&#xff0c;这个目录下存放着许多可执行文件。与其他系统的可执行文件类似&#xff0c;这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中&#xff0c;用…...