2024年4月

一、gomock 工具介绍

gomock
是一个 Go 语言的测试框架,在实际项目中,需要进行单元测试的时候。却往往发现有一大堆依赖项。这时候就是
Gomock
大显身手的时候了,用于编写单元测试时模拟和测试依赖于外部服务的代码。它允许你创建模拟对象(Mock Objects),这些对象可以预设期望的行为,以便在测试时模拟外部依赖,通常使用它对代码中的那些接口类型进行mock。

原本 Go 团队提供了一个 mock 工具
https://github.com/golang/mock
,但在今年放弃维护了,改用
https://github.com/uber-go/mock

二、安装

要安装
gomock
,你可以使用 Go 包管理器
go get

go install go.uber.org/mock/mockgen@latest

三、使用

首先确保你已经安装了
gomock
,并且在项目中执行了
go mod tidy

3.1 指定三个参数

在使用
mockgen
生成模拟对象(Mock Objects)时,通常需要指定三个主要参数:

  • source
    :这是你想要生成模拟对象的接口定义所在的文件路径。
  • destination
    :这是你想要生成模拟对象代码的目标路径。
  • package
    :这是生成代码的包名。

3.2 使用命令为接口生成 mock 实现

一旦你指定了上述参数,
mockgen
就会为你提供的接口生成模拟实现。生成的模拟实现将包含一个
EXPECT
方法,用于设置预期的行为,以及一些方法实现,这些实现将返回默认值或调用真实的实现。

例如,如果你的接口定义在
./webook/internal/service/user.go
文件中,你可以使用以下命令来生成模拟对象:

mockgen -source=./webook/internal/service/user.go -package=svcmocks destination=./webook/internal/service/mocks/user.mock.go

3.3 使用make 命令封装处理mock

在实际项目中,你可能会使用
make
命令来自动化构建过程,包括生成模拟对象。你可以创建一个
Makefile

make.bash
文件,并添加一个目标来处理
mockgen
的调用。例如:

# Makefile 示例
# mock 目标 ,可以直接使用 make mock命令
.PHONY: mock
# 生成模拟对象
mock:
	@mockgen -source=internal/service/user.go -package=svcmocks -destination=internal/service/mocks/user.mock.go
	@mockgen -package=redismocks -destination=internal/repository/cache/redismocks/cmdable.mock.go github.com/redis/go-redis/v9 Cmdable
	@go mod tidy

最后,只要我们执行
make mock
命令,就会生成
mock
文件。

四、接口单元测试步骤

  1. 想清楚整体逻辑
  2. 定义想要(模拟)依赖项的
    interface
    (接口)
  3. 使用
    mockgen
    命令对所需mock的interface生成mock文件
  4. 编写单元测试的逻辑,在测试中使用mock
  5. 进行单元测试的验证

三、小黄书Service层单元测试

这里我们已注册接口为例子,代码如下:

// gmock/webook/backend/internal/web/user.go
func (u *UserHandler) SignUp(ctx *gin.Context) {
	type SignUpReq struct {
		Email           string `json:"email"`
		ConfirmPassword string `json:"confirmPassword"`
		Password        string `json:"password"`
	}

	var req SignUpReq
	// Bind 方法会根据 Content-Type 来解析你的数据到 req 里面
	// 解析错了,就会直接写回一个 400 的错误
	if err := ctx.Bind(&req); err != nil {
		return
	}

	ok, err := u.emailExp.MatchString(req.Email)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !ok {
		ctx.String(http.StatusOK, "你的邮箱格式不对")
		return
	}
	if req.ConfirmPassword != req.Password {
		ctx.String(http.StatusOK, "两次输入的密码不一致")
		return
	}
	ok, err = u.passwordExp.MatchString(req.Password)
	if err != nil {
		// 记录日志
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !ok {
		ctx.String(http.StatusOK, "密码必须大于8位,包含数字、特殊字符")
		return
	}

	// 调用一下 svc 的方法
	err = u.svc.SignUp(ctx, domain.User{
		Email:    req.Email,
		Password: req.Password,
	})
	if err == service.ErrUserDuplicateEmail {
		ctx.String(http.StatusOK, "邮箱冲突")
		return
	}
	if err != nil {
		ctx.String(http.StatusOK, "系统异常")
		return
	}

	ctx.String(http.StatusOK, "注册成功")
}

执行命令,生成mock文件:

mockgen -source=./webook/internal/service/user.go -package=svcmocks destination=./webook/internal/service/mocks/user.mock.go

接着我们编写单元测试,代码如下:

// gmock/webook/backend/internal/web/user_test.go
package web

import (
	"bytes"
	"context"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"
	"golang.org/x/crypto/bcrypt"
	"net/http"
	"net/http/httptest"
	"testing"
	"webook/internal/domain"
	"webook/internal/service"
	svcmocks "webook/internal/service/mocks"
)

func TestEncrypt(t *testing.T) {
	_ = NewUserHandler(nil, nil)
	password := "hello#world123"
	encrypted, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		t.Fatal(err)
	}
	err = bcrypt.CompareHashAndPassword(encrypted, []byte(password))
	assert.NoError(t, err)
}

func TestNil(t *testing.T) {
	testTypeAssert(nil)
}

func testTypeAssert(c any) {
	_, ok := c.(*UserClaims)
	println(ok)
}

func TestUserHandler_SignUp(t *testing.T) {
	testCases := []struct {
		name string

		mock func(ctrl *gomock.Controller) service.UserService

		reqBody string

		wantCode int
		wantBody string
	}{
		{
			name: "注册成功",
			mock: func(ctrl *gomock.Controller) service.UserService {
				usersvc := svcmocks.NewMockUserService(ctrl)
				usersvc.EXPECT().SignUp(gomock.Any(), domain.User{
					Email:    "123@qq.com",
					Password: "hello#world123",
				}).Return(nil)
				// 注册成功是 return nil
				return usersvc
			},

			reqBody: `
{
	"email": "123@qq.com",
	"password": "hello#world123",
	"confirmPassword": "hello#world123"
}
`,
			wantCode: http.StatusOK,
			wantBody: "注册成功",
		},
		{
			name: "参数不对,bind 失败",
			mock: func(ctrl *gomock.Controller) service.UserService {
				usersvc := svcmocks.NewMockUserService(ctrl)
				// 注册成功是 return nil
				return usersvc
			},

			reqBody: `
{
	"email": "123@qq.com",
	"password": "hello#world123"
`,
			wantCode: http.StatusBadRequest,
		},
		{
			name: "邮箱格式不对",
			mock: func(ctrl *gomock.Controller) service.UserService {
				usersvc := svcmocks.NewMockUserService(ctrl)
				return usersvc
			},

			reqBody: `
{
	"email": "123@q",
	"password": "hello#world123",
	"confirmPassword": "hello#world123"
}
`,
			wantCode: http.StatusOK,
			wantBody: "你的邮箱格式不对",
		},
		{
			name: "两次输入密码不匹配",
			mock: func(ctrl *gomock.Controller) service.UserService {
				usersvc := svcmocks.NewMockUserService(ctrl)
				return usersvc
			},

			reqBody: `
{
	"email": "123@qq.com",
	"password": "hello#world1234",
	"confirmPassword": "hello#world123"
}
`,
			wantCode: http.StatusOK,
			wantBody: "两次输入的密码不一致",
		},
		{
			name: "密码格式不对",
			mock: func(ctrl *gomock.Controller) service.UserService {
				usersvc := svcmocks.NewMockUserService(ctrl)
				return usersvc
			},
			reqBody: `
{
	"email": "123@qq.com",
	"password": "hello123",
	"confirmPassword": "hello123"
}
`,
			wantCode: http.StatusOK,
			wantBody: "密码必须大于8位,包含数字、特殊字符",
		},
		{
			name: "邮箱冲突",
			mock: func(ctrl *gomock.Controller) service.UserService {
				usersvc := svcmocks.NewMockUserService(ctrl)
				usersvc.EXPECT().SignUp(gomock.Any(), domain.User{
					Email:    "123@qq.com",
					Password: "hello#world123",
				}).Return(service.ErrUserDuplicateEmail)
				// 注册成功是 return nil
				return usersvc
			},

			reqBody: `
{
	"email": "123@qq.com",
	"password": "hello#world123",
	"confirmPassword": "hello#world123"
}
`,
			wantCode: http.StatusOK,
			wantBody: "邮箱冲突",
		},
		{
			name: "系统异常",
			mock: func(ctrl *gomock.Controller) service.UserService {
				usersvc := svcmocks.NewMockUserService(ctrl)
				usersvc.EXPECT().SignUp(gomock.Any(), domain.User{
					Email:    "123@qq.com",
					Password: "hello#world123",
				}).Return(errors.New("随便一个 error"))
				// 注册成功是 return nil
				return usersvc
			},

			reqBody: `
{
	"email": "123@qq.com",
	"password": "hello#world123",
	"confirmPassword": "hello#world123"
}
`,
			wantCode: http.StatusOK,
			wantBody: "系统异常",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			defer ctrl.Finish()
			server := gin.Default()
			// 用不上 codeSvc
			h := NewUserHandler(tc.mock(ctrl), nil)
			h.RegisterRoutes(server)

			req, err := http.NewRequest(http.MethodPost,
				"/users/signup", bytes.NewBuffer([]byte(tc.reqBody)))
			require.NoError(t, err)
			// 数据是 JSON 格式
			req.Header.Set("Content-Type", "application/json")
			// 这里你就可以继续使用 req

			resp := httptest.NewRecorder()
			// 这就是 HTTP 请求进去 GIN 框架的入口。
			// 当你这样调用的时候,GIN 就会处理这个请求
			// 响应写回到 resp 里
			server.ServeHTTP(resp, req)

			assert.Equal(t, tc.wantCode, resp.Code)
			assert.Equal(t, tc.wantBody, resp.Body.String())

		})
	}
}

func TestMock(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	usersvc := svcmocks.NewMockUserService(ctrl)

	usersvc.EXPECT().SignUp(gomock.Any(), gomock.Any()).
		Return(errors.New("mock error"))

	//usersvc.EXPECT().SignUp(gomock.Any(), domain.User{
	//	Email: "124@qq.com",
	//}).Return(errors.New("mock error"))

	err := usersvc.SignUp(context.Background(), domain.User{
		Email: "123@qq.com",
	})
	t.Log(err)
}

四、flags

gomock
有一些命令行标志,可以帮助你控制生成过程。这些标志通常在
gomock
工具的帮助下使用,例如
gomock generate

mockgen
命令用来为给定一个包含要mock的接口的Go源文件,生成mock类源代码。它支持以下标志:

  • -source
    :包含要mock的接口的文件。
  • -destination
    :生成的源代码写入的文件。如果不设置此项,代码将打印到标准输出。
  • -package
    :用于生成的模拟类源代码的包名。如果不设置此项包名默认在原包名前添加
    mock_
    前缀。
  • -imports
    :在生成的源代码中使用的显式导入列表。值为foo=bar/baz形式的逗号分隔的元素列表,其中bar/baz是要导入的包,foo是要在生成的源代码中用于包的标识符。
  • -aux_files
    :需要参考以解决的附加文件列表,例如在不同文件中定义的嵌入式接口。指定的值应为foo=bar/baz.go形式的以逗号分隔的元素列表,其中bar/baz.go是源文件,foo是
    -source
    文件使用的文件的包名。
  • -build_flags
    :(仅反射模式)一字不差地传递标志给go build
  • -mock_names
    :生成的模拟的自定义名称列表。这被指定为一个逗号分隔的元素列表,形式为
    Repository = MockSensorRepository,Endpoint=MockSensorEndpoint
    ,其中
    Repository
    是接口名称,
    mockSensorrepository
    是所需的mock名称(mock工厂方法和mock记录器将以mock命名)。如果其中一个接口没有指定自定义名称,则将使用默认命名约定。
  • -self_package
    :生成的代码的完整包导入路径。使用此flag的目的是通过尝试包含自己的包来防止生成代码中的循环导入。如果mock的包被设置为它的一个输入(通常是主输入),并且输出是stdio,那么mockgen就无法检测到最终的输出包,这种情况就会发生。设置此标志将告诉 mockgen 排除哪个导入
  • -copyright_file
    :用于将版权标头添加到生成的源代码中的版权文件
  • -debug_parser
    :仅打印解析器结果
  • -exec_only
    :(反射模式) 如果设置,则执行此反射程序
  • -prog_only
    :(反射模式)只生成反射程序;将其写入标准输出并退出。
  • -write_package_comment
    :如果为true,则写入包文档注释 (godoc)。(默认为true)

五、打桩(stub)

在测试中,打桩是一种测试术语,用于为函数或方法设置一个预设的返回值,而不是调用真实的实现。在
gomock
中,打桩通常通过设置期望的行为来实现。
例如,您可以为
myServiceMock

DoSomething
方法设置一个期望的行为,并返回一个特定的错误。这可以通过调用
myServiceMock.EXPECT().DoSomething().Return(error)
来实现。
在单元测试中,使用
gomock
可以帮助你更有效地模拟外部依赖,从而编写更可靠和更高效的测试。通常用来屏蔽或补齐业务逻辑中的关键代码方便进行单元测试。

屏蔽:不想在单元测试用引入数据库连接等重资源

补齐:依赖的上下游函数或方法还未实现

gomock
支持针对参数、返回值、调用次数、调用顺序等进行打桩操作。

参数

参数相关的用法有:

  • gomock.Eq(value):表示一个等价于value值的参数
  • gomock.Not(value):表示一个非value值的参数
  • gomock.Any():表示任意值的参数
  • gomock.Nil():表示空值的参数
  • SetArg(n, value):设置第n(从0开始)个参数的值,通常用于指针参数或切片

六、总结

6.1 测试用例定义

测试用例定义,最完整的情况下应该包含:

  • 名字
    :简明扼要说清楚你测试的场景,建议用中文。
  • 预期输入
    :也就是作为你方法的输入。如果测试的是定义在类型上的方法,那么也可以包含类型实例。
  • 预期输出
    :你的方法执行完毕之后,预期返回的数据。如果方法是定义在类型上的方法,那么也可以包含执行之后的实例的状态。
  • mock
    :每一个测试需要使用到的mock状态。单元测试里面常见,集成测试一般没有。
  • 数据准备
    :每一个测试用例需要的数据。集成测试里常见。
  • 数据清理
    :每一个测试用例在执行完毕之后,需要执行一些数据清理动作。集成测试里常见。

如果你要测试的方法很简单,那么你用不上全部字段。

6.2 设计测试用例

测试用例定义和运行测试用例都是很模板化的东西。测试用例就是要根据具体的方法来设计。

  • 如果是单元测试:看代码,最起码做到分支覆盖。
  • 如果是集成测试:至少测完业务层面的主要正常流程和主要异常流程。

单元测试覆盖率做到80%以上,在这个要求之下,只有极少数的异常分支没有测试。其它测试就不是我们研发要考虑的了,让测试团队去搞。

6.3 执行测试用例代码

测试用例定义出来之后,怎么执行这些用例,就已经呼之欲出了。

这里分成几个部分:

  • 初始化 mock 控制器
    ,每个测试用例都有独立的 mock 控制器。
  • 使用控制器 ctrl 调用 tc.mock
    ,拿到 mock 的 UserService 和 CodeService。
  • 使用 mock 的服务初始化 UserHandler,并且注册路由。
  • 构造 HTTP 请求和响应 Recorder
  • 发起调用 ServeHTTP

6.4 运行测试用例

测试里面的
testCases
是一个匿名结构体的切片,所以运行的时候就是直接遍历。

那么针对每一个测试用例:

  • 首先调用mock部分,或者执行before。
  • 执行测试的方法。
  • 比较预期结果。
  • 调用after方法。

注意运行的时候,先调用了
t.Run
,并且传入了测试用例的名字。

6.5 不是所有的场景都很好测试

即便你的代码写得非常好,但是有一些场景基本上不可能测试到。
如图中的
error
分支,就是属于很难测试的。

因为
bcrypt
包你控制不住,
Generate
这个方法只有在超时的时候才会返回
error
。那么你不测试也是可以的,代码
review
可以确保这边正确处理了
error

记住:没有测试到的代码,一定要认真
review

小黄书单元测试代码:
https://github.com/tao-xiaoxin/demo/tree/main/gotest/gmock/webook/backend

最近一段时间在研究AI技术在.Net平台的使用,目前AI绝大部分是使用Python开发,偶然一次在头条看到一篇ML.NET的介绍,是Net平台下开放源代码的跨平台机器学习框架。ML.NET详细介绍

https://dotnet.microsoft.com/zh-cn/apps/machinelearning-ai/ml-dotnet

一开始学习的是图像分类和目标检测,整个ML.NET学习过程中走了不少弯路;目标检测最开始使用VS插件ML.NET Model Builder进行数据训练,发现执行效率低下。使用Vott进行图片标注发现也有不少bug,视频文件标注导出后文件路径识别不了。最后,找到了一个效率很高的方式,使用yolo导出onnx模型,yolo数据集使用Python训练和导出onnx,最后在.Net下进行调用即可。

一、车牌识别实现基本步骤

1. 数据标注,可以使用LabImg或其他标注工具

2.训练数据,训练车牌样式,训练文字和颜色ORC识别

3.导出onnx格式模型

4.使用ML.NET调用模型

二、整合到IoTBrowser

IoTBrowser增加Dynamic Api插件框架,另外找了一个Yolov5Net包,默认支持Net6,后面移植到.Net Framework下。

C#调用代码很简单

public AjaxResponse CarNo(string inArgs) {
var ar = new Infrastructure.Web.AjaxResponse();
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(inArgs);
var path = string.Empty;
var beginTime = DateTime.Now;
if (obj.path != null)
{
path = obj.path;
}
var image = System.Drawing.Image.FromFile(path);
var predictions = yolo.Predict(image);

if (predictions.Count < 1) {
ar.Error("没有检测到车牌");
return ar;
}
foreach (var prediction in predictions) // iterate predictions to draw results
{
double score = Math.Round(prediction.Score, 2);
var labelRect = prediction.Rectangle;
var twoLayers = (labelRect.Height / labelRect.Width) > 0.5;
//定义截取矩形
System.Drawing.Rectangle cropArea = new System.Drawing.Rectangle((int)labelRect.X < 0 ? 0 : (int)labelRect.X, (int)labelRect.Y < 0 ? 0 : (int)labelRect.Y, (int)labelRect.Width, (int)labelRect.Height);
//定义Bitmap对象
System.Drawing.Bitmap bmpImage = new System.Drawing.Bitmap(image);
//进行裁剪
System.Drawing.Bitmap bmpCrop = bmpImage.Clone(cropArea, bmpImage.PixelFormat);
//保存成新文件
//bmpCrop.Save(Path.Combine(path, (fileName + "_" + dtNow + num + "_clone.png")), ImageFormat.Png);

var yoloOcrpredictions = yoloOcr.Predict(bmpCrop);
if (yoloOcrpredictions.Length > 0)
{
ar.Data = (new {carNo = yoloOcrpredictions[0] ,color = yoloOcrpredictions[1] });
}
}
return ar;
}

js端调用更简单

var filePath ="";
var ar = await dds.dynamic.api.exectuce({
pluginName: "CarDetectApi",
actionName: "CarNo",
actionData: JSON.stringify({
path: filePath
})
})
if (ar.Success && ar.Data) {
var data = ar.Data;
self.resultInfo = data.carNo + "--" + data.color
} else {
self.resultInfo = ar.Message;
}

三、实现效果

支持的格式:

1.图片绝对文件路径

2.RTMP协议取帧识别

3.Mp4或ts文件
取帧识别

现在,刷视频已经成为我们生活中的一部分,而且很容易一看就停不下来。你有没有好奇过,它是如何在海量的视频里,找到让你感兴趣的视频?又是如何让你可以通过关键字,搜索到与之相关的视频内容的呢?这一切都离不开计算机对视频内容的分析和理解。

计算机是如何“看懂”海量视频的呢?
视频本质上是一系列连续的图像帧,按照一定的帧率播放,从而形成连续的动态效果
。因此,计算机分析视频的基本原理就是:解码(视频转图片)-> 分析/推理(AI 算法)-> 编码(结果呈现)

尽管这看起来就寥寥几步,但其中涉及许多技术细节和复杂的算法。比如,如何将训练好的 AI 图像算法模型,快速部署落地到实际应⽤场景中呢?对于没有接触过计算机视觉(Computer Vision,后简称 CV)的程序员,或是纯搞算法的算法工程师,要实现+落地 AI 视频分析相关功能可能会有点难度。然而,随着视频在日常生活中的普及和应用越来越广泛,处理和分析视频类数据的需求也在逐渐增加。

因此,今天 HelloGitHub 带来了一款开源的视频分析/结构化框架——VideoPipe,旨在让开发视频分析应用像使用 Django 写 Web 一样方便。
VideoPipe 独创的管道可视化显示,让每一步的处理状态都可以一目了然
。该框架能够轻松集成各种 CV 领域的模型,通过即插即用的方式轻松实现 AI 加持下的视频分析,适用于视频结构化、图片搜索、人脸识别、安防领域的行为分析(⻋牌识别、交通事故检测)等场景。

GitHub 地址:
https://github.com/sherlockchou86/VideoPipe

下面,让我们跟着该项目的作者(周智)一起来了解、上手 VideoPipe,然后深入其内部学习更多的技术细节。

一、介绍

VideoPipe 这是一个用于视频分析和结构化的框架,采用 C++ 编写、依赖少、易上手。它就像一个管道每个节点相互独立可自行搭配,用来构建不同类型的视频分析管道,适用于视频结构化、图片搜索、人脸识别、安防领域的行为分析(如交通事件检测)等场景。

你只需准备好模型并了解如何解析其输出即可,推理可以基于不同的后端实现,如 OpenCV::DNN(默认)、TensorRT、PaddleInference、ONNXRuntime 等,任何你喜欢的都可以。

通过上面的 VideoPipe 工作示意图,可以发现它提供了以下功能:

  • 流读取/推送:⽀持主流的视频流协议,如 udp、rtsp、rtmp、文件。
  • 视频解码/编码:⽀持基于 OpenCV/GStreamer 的视频和图片解/编码(⽀持硬件加速)。
  • 基于深度学习的算法推理:⽀持基于深度学习算法的多级推理,例如⽬标检测、图像分类、特征提取。
  • ⽬标跟踪:⽀持⽬标追踪,例如 IOU、SORT 跟踪算法等。
  • ⾏为分析(BA):⽀持基于跟踪的⾏为分析,例如越线、停⻋、违章等交通判断。
  • 数据代理:⽀持将结构化数据(json/xml/⾃定义格式)以 kafka/Sokcet 等⽅式推送到云端、文件或其他
    第三⽅平台。
  • 录制:⽀持特定时间段的视频录制,特定帧的截图。
  • 屏幕显⽰(OSD):支持将模型输出结果绘制到帧上。

对比功能类似、耳熟能详的 DeepStream(英伟达)和 mxVision(华为)框架,
VideoPipe 更易于使⽤和调试、具备更好的可移植性,它完全由原生 C++ 编写,仅依赖于少量主流的第三方模块(如 OpenCV)
。同时提供了可视化管道,框架的运行状态会自动在屏幕上刷新,包括管道中每个连接点的 fps、缓存大小、延迟等信息,你可以根据这些运行信息快速定位处理时的瓶颈所在。

名称 是否开源 学习门槛 适用平台 性能 三方依赖
DeepStream 仅限英伟达
mxVision 仅限华为
VideoPipe 不限平台

二、快速上手

VideoPipe 对机器硬件没有要求,仅用 CPU 都可以运行,不需要额外的加速卡。而且项目中还提供了丰富的示例代码,下面让我们通过运行一个简单的「人脸识别」示例,快速上手该框架。

/*
* 名称:1-1-N sample
* 完整代码位于:samples/1-1-N_sample.cpp
* 功能说明:1个视频输入,1个视频分析任务(人脸检测和识别),2个输出(屏幕输出/RTMP推流输出)
* 注意:模型和视频文件需要自行准备
*/

int main() {
    VP_SET_LOG_INCLUDE_CODE_LOCATION(false);
    VP_SET_LOG_INCLUDE_THREAD_ID(false);
    VP_LOGGER_INIT();

    // 1、创建节点
    // 视频获取 Node
    auto file_src_0 = std::make_shared<vp_nodes::vp_file_src_node>("file_src_0", 0, "./test_video/10.mp4", 0.6);
    // 2、模型推理 Node
    // 一级推理:人脸检测
    auto yunet_face_detector_0 = std::make_shared<vp_nodes::vp_yunet_face_detector_node>("yunet_face_detector_0", "./models/face/face_detection_yunet_2022mar.onnx");
    // 二级推理:人脸识别
    auto sface_face_encoder_0 = std::make_shared<vp_nodes::vp_sface_feature_encoder_node>("sface_face_encoder_0", "./models/face/face_recognition_sface_2021dec.onnx");
    // 3、OSD Node
    // 处理结果绘制到帧上
    auto osd_0 = std::make_shared<vp_nodes::vp_face_osd_node_v2>("osd_0");
    // 屏幕展示
    auto screen_des_0 = std::make_shared<vp_nodes::vp_screen_des_node>("screen_des_0", 0);
    // 推流展示
    auto rtmp_des_0 = std::make_shared<vp_nodes::vp_rtmp_des_node>("rtmp_des_0", 0, "rtmp://192.168.77.60/live/10000");

    // 构建管道,将节点的处理结果关联起来
    yunet_face_detector_0->attach_to({file_src_0});
    sface_face_encoder_0->attach_to({yunet_face_detector_0});
    osd_0->attach_to({sface_face_encoder_0});

    // 管道自动拆分,通过屏幕/推流输出结果
    screen_des_0->attach_to({osd_0});
    rtmp_des_0->attach_to({osd_0});

    // 启动管道
    file_src_0->start();

    // 可视化管道
    vp_utils::vp_analysis_board board({file_src_0});
    board.display();
}

通过阅读上面的示例代码,可以发现
VideoPipe 框架将视频分析/处理的步骤,抽象成了一个管道(pipe),每一步的处理都是管道中的一个节点(Node)
,处理流程如下:

  1. 视频读取 Node:完成读取视频和解码的工作
  2. 模型推理 Node:分为人脸检测和人脸识别两个模型
  3. OSD Node:将模型输出的处理结果绘制到帧上
  4. 构建管道:将上述节点依次连接,并将结果分成屏幕输出和推流输出,
  5. 启动:启动程序,并展示管道的运行情况

代码运⾏后,会出现上面的 3 个画⾯。它们分别是管道运⾏状态图(状态⾃动刷新)、屏幕显⽰结果(GUI)、播放器显⽰结果(RTMP),至此就算上手 VideoPipe 了!

三、技术原理

接下来,将详细介绍 VideoPipe 框架实现的技术原理和细节,干货来啦!在深入了解 VideoPipe 框架技术细节之前,我们需要先弄清楚视频的整体处理流程。

3.1 视频结构化应⽤的核⼼环节

视频结构化是将非结构化数据(视频/图片)转换为结构化数据的过程。非结构化数据通常包括:视频、图像、⾳频、⾃然语⾔文本,⽽结构化数据主要包括诸如 JSON、XML 或数据库中的数据表等,这些数据可以直接由机器(程序)处理。具体到视频(含图片,下同)结构化的过程,主要涉及以下核⼼部分:

  • 读取流:从⽹络或本地机器获取视频流。
  • 解码:将字节流解码为帧,因为算法只能作⽤于图像。
  • 推理:对图像进⾏深度学习推理,如检测、分类或特征提取。
  • 跟踪:跟踪视频中的⽬标。
  • ⾏为分析/逻辑处理:分析⽬标的轨迹、属性。
  • OSD:在图像上显⽰结果,⽤于调试或得到直观效果。
  • 消息代理:将结构化数据推送到外部,供业务平台使⽤。
  • 编码:对包含结果的帧进⾏编码,以便传输、存储。
  • 推送流:将字节流推送到外部或直接保存

上述每个环节对应 VideoPipe 中的⼀种插件类型,即代码中的 Node 对象。下面我们将逐一讲解 VideoPipe 的 Node、数据流、钩子的技术细节和实现。

3.2 Node

VideoPipe 中的每个 Node 负责⼀种任务(严格遵循单⼀职责原则),例如解码或推理。我们可以将许多节点串在⼀起构建成管道,并让视频数据流经整个管道。每个 Node 内部都有两个队列,⼀个⽤于缓存上游节点推送的数据,另⼀个⽤于缓存等待被推送到下游节点的数据。我们可以在两个队列之间编写逻辑代码,这是典型的⽣产者-消费者模式。

VideoPipe 中有三种类型的节点,分别是:

  1. SRC节点:源节点,数据被创建的地⽅(内部只有⼀个队列,⽤于缓存被推送到下游节点的数据)。
  2. MID节点:中间节点,数据将在此处理。
  3. DES节点:⽬标节点,数据消失的地⽅(内部只有⼀个队列,⽤于缓存来⾃上游节点的数据)。

每个节点本⾝具有合并多个上游节点和拆分成多个下游节点的能⼒。注意,默认情况下节点在将数据从⼀个节点传输到另⼀个节点时使⽤浅拷⻉和等值拷⻉。如果您需要深拷⻉或希望按通道索引传输数据(希望数据不混淆),则在分裂点添加⼀个
vp_split_node
类型节点。

3.3 数据流

视频是一种重量级数据,因此频繁进行深拷贝会降低管道的性能。实际上,VideoPipe 中两个节点之间传递的数据默认使用智能指针,一旦数据由源节点创建,数据内容在整个管道中大多数时间不会被复制。但如果需要,我们可以指定深度拷贝模式,使用
vp_split_node
类型节点。

视频由连续的帧组成,因此 VideoPipe 逐帧处理这些帧,所以帧元数据中的帧索引也会连续增加。

3.4 钩子

钩子是一种机制,让主体在发生某些事件时通知检测者,VideoPipe 也支持钩子。管道触发回调函数
std::function
与外部代码通信,例如实时推送管道自身的 fps、延迟和其他状态信息。我们在编写回调函数内部代码时,不允许有阻塞出现,否则影响整个管道性能。

钩子有助于调试我们的应用程序,并快速找出整个管道中的瓶颈,VideoPipe 框架中自带的可视化工具
vp_analysis_board
就是依赖于钩子机制实现的。

3.5 如何实现新的 Node 类型

首先
vp_node
是 VideoPipe 中所有节点的基类,我们可以定义一个从
vp_node
派生的新节点类,并重写一些虚函数:

  • handle_frame_meta
    :处理流经当前节点的帧数据。
  • handle_control_meta
    :处理流经当前节点的控制指令数据。

帧数据指的是 VideoPipe 中的
vp_frame_meta
,其中包含与帧相关的数据,如帧索引、数据缓冲区、原始宽度等等。控制指令数据指的是 VideoPipe 中的
vp_control_meta
,其中包含与命令相关的数据,例如记录视频、记录图像等。并非所有流经当前节点的数据都应该被处理,只需要处理我们感兴趣的内容。

四、最后

目前,基于深度学习的视频分析技术的入门门槛还是比较高的,一些成熟的框架比如 DeepStream、mxVision 等,它们大多晦涩难懂、上手门槛高、对于新手不太友好。所以,我就花了两年的业余时间创建了 VideoPipe 视频分析框架,我的想法很简单就是想让
初学者能够快速了解视频分析相关技术栈,轻松地在自己机器上跑通一个人脸识别的应用
,让更多人掌握视频分析相关技术,同时搞清楚应该从哪里开始。

我深知这是一件道阻且长的事情,所以 VideoPipe 在诞生之初就是完全开源,我希望能够借助开源的力量让它“发光发热”,真正地做到降低开发视频分析应用的门槛,帮助更多的开发者进入到视频分析的领域。

GitHub 地址:
https://github.com/sherlockchou86/VideoPipe

最后,感谢「HelloStar 计划」提供的机会,能够让更多人了解 VideoPipe 框架。我作为开源生态的受益者,深知开源的力量和责任,此举也是希望 VideoPipe 项目能够成为一座连接对视频分析、结构化技术感兴趣的小伙伴的桥梁,能够和大家一起交流学习、共同进步、回馈开源社区!

开心一刻

今天,老婆给我发消息

老婆:老公,儿子从隔壁邻居家回来了

老婆:是先打还是先洗?

我:先洗吧,万一打错人了呢

老婆:先洗脸吧,没错就边打边洗

起因

在我们的固有认知中,
mysql-connector-java-5.x.x
连接的是
MySQL5
,而
mysql-connector-java-8.x.x
连接的是
MySQL8

如果用
mysql-connector-java-5.x.x
连接
MySQL8
,或者用
mysql-connector-java-8.x.x
连接
MySQL5
,会出问题

至于出什么问题,呃...,我没想过,也没试过,我相信你们也是一样的

但无意间,我尝试了下,确实有问题

一开始,我的一个
demo
是基于
MySQL 5.7.36

mysql-connector-java 5.1.26

MyBatis-Plus 3.1.0

示例代码:
play_it_safe

因为
MySQL 5.7.36
被我删了,而本机(
windows 10
)上正好有
MySQL 8.0.26

那就用
MySQL 8.0.26
来跑
demo
呗,只修改数据库连接的
url

username

password

然后就出现了一系列的异常

java.math.BigInteger cannot be cast to java.lang.Long

启动直接异常


java.sql.SQLException: java.lang.ClassCastException: java.math.BigInteger cannot be cast to java.lang.Long
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
1078) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
989) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
975) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
920) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.buildCollationMapping(ConnectionImpl.java:
1074) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:
3593) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:
2534) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:
2304) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.
<init>(ConnectionImpl.java:834) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.JDBC4Connection.
<init>(JDBC4Connection.java:47) ~[mysql-connector-java-5.1.26.jar:na]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:
45) ~[na:1.8.0_91]
at java.lang.reflect.Constructor.newInstance(Constructor.java:
423) ~[na:1.8.0_91]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:
411) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:
416) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:
346) ~[mysql-connector-java-5.1.26.jar:na]
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:
138) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:
358) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:
206) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:
477) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:
560) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.
<init>(HikariPool.java:115) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:
112) [HikariCP-3.4.5.jar:na]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.buildSqlSessionFactory(MybatisSqlSessionFactoryBean.java:
486) [mybatis-plus-extension-3.1.1.jar:3.1.1]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.afterPropertiesSet(MybatisSqlSessionFactoryBean.java:
434) [mybatis-plus-extension-3.1.1.jar:3.1.1]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.getObject(MybatisSqlSessionFactoryBean.java:
633) [mybatis-plus-extension-3.1.1.jar:3.1.1]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration.sqlSessionFactory(MybatisPlusAutoConfiguration.java:
190) [mybatis-plus-boot-starter-3.1.1.jar:3.1.1]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$8802fc19.CGLIB$sqlSessionFactory$
1(<generated>) [mybatis-plus-boot-starter-3.1.1.jar:3.1.1]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$8802fc19$$FastClassBySpringCGLIB$$f9d1deeb.invoke(
<generated>) [mybatis-plus-boot-starter-3.1.1.jar:3.1.1]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:
244) [spring-core-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:
331) [spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$8802fc19.sqlSessionFactory(
<generated>) [mybatis-plus-boot-starter-3.1.1.jar:3.1.1]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43) ~[na:1.8.0_91]
at java.lang.reflect.Method.invoke(Method.java:
498) ~[na:1.8.0_91]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:
154) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:
650) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:
635) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:
1336) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:
1176) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:
556) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:
516) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$
0(AbstractBeanFactory.java:324) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:
234) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:
322) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:
202) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:
276) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:
1307) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:
1227) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:
1509) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:
1404) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:
593) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:
516) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$
0(AbstractBeanFactory.java:324) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:
234) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:
322) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:
202) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:
878) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:
879) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:
551) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:
141) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:
747) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:
397) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
315) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
1226) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
1215) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at com.qsl.PlayItSafeApplication.main(PlayItSafeApplication.java:
16) ~[classes/:na]
Caused by: java.lang.ClassCastException: java.math.BigInteger cannot be cast to java.lang.Long
at com.mysql.jdbc.ConnectionImpl.buildCollationMapping(ConnectionImpl.java:
1019) ~[mysql-connector-java-5.1.26.jar:na]
...
64 common frames omitted

View Code

这确实出乎了我的意料,我认为的是:启动不会异常,运行中出现异常

既然异常来了,那就好好看看呗

怎么看?直接从异常堆栈中看,找到
Caused by

直接点击框住的,来到
mysql-connector-java 5.1.26
的源码

这个
for
循环遍历的是
sortedCollationMap

indexEntry
则表示遍历的当前元素

我问个问题:
java.math.BigInteger cannot be cast to java.lang.Long
异常是谁抛出来的,
getKey()
还是
intValue()

很简单,用排除法嘛

Long
是有
intValue()
方法的,只会
NPE
,不会
java.math.BigInteger cannot be cast to java.lang.Long

那就是
getKey()
抛出的异常呗,我们猜想下什么情况会抛出这个异常

indexEntry
声明的类型虽然是
Map.Entry<Long, String>
,但实际它的
key
不是
Long
类型,而是
BigInteger
类型?

再往上追溯一下,
sortedCollationMap
声明的类型是
TreeMap<Long, String>
,但实际存放元素的
key

BigInteger
类型?

此时你们对这个猜想肯定有疑问:这编译不会报错吗

直接
put
是会编译报错的,但是如果我变通下呢

编译没问题,但是执行会有如下异常

同样也是
int
collationIndex = entry.getKey().intValue();

抛出的异常

但两者原因是否一致,我们还需要对
play_it_safe
验证

如何验证,
debug
调试呗

断点不能打在
connectionimpl.java:1019
这一行,因为此时是遍历
sortedCollationMap
,而非往里面
put
元素

我们需要看,往
sortedCollationMap
里面
put

key
具体是什么类型,所以断点打在
1002

会走 if 分支

F7 跟进去

key
的类型就是
BigInteger
,证明我们的猜想是对的!

但是大家别高兴的太早,根因还没找到,我们得继续跟进
com.mysql.jdbc.ResultSetImpl#getObject(
int
)

重点看这个
switch

继续跟进,你会发现来到
BIGINT
分支

注意,重点来了

这段代码很简单,相信大家都能看到,但我还是分 3 块给大家说明下

1、
unsigned
表示无符号,然后再取反则表示有符号,也就是说如果数据库字段类型是
有符号

bigint
,则用
java.lang.Long
接收字段值并返回

2、如果数据库字段类型是
无符号

bigint
,则用
java.lang.String
来接收字段值,如果值为
null
则直接返回
null

3、如果数据库字段类型是
无符号

bigint
,并且字段值不是
null
,则用
java.math.BigInteger
来接收字段值并返回

此刻相信大家对异常的根因有点明白了,但又没有完全明白,为什么了?

因为没有弄清楚来龙去脉,我给大家捋一下:

1、程序启动过程(初始化)中,mysql-connector-java 5.1.26 会构建字段级字符集/字符序到java字符编码名称的映射

也就是会执行
com.mysql.jdbc.ConnectionImpl#buildCollationMapping
方法

2、
buildCollationMapping
方法中会执行 SQL
SHOW COLLATION
来获取
MySQL
的字符集和字符序

Id:字符序id

Charset:字符集名称

关于
MySQL
的字符集和字符序的更多信息,可查看
记一次字符串末尾空白丢失的排查 → MySQL 是会玩的!

3、从查询结果集中,逐行取第 3 列(
Id
)作为
key
,取第 2 列(
Charset
)作为
value

put

sortedCollationMap

对应代码就是:
Util.resultSetToMap(sortedCollationMap, results, 3, 2);

4、在取
Id
列值的时候,会根据数据库列类型,用不同的
java
类型来接收列值

MySQL 8.0.26
中,
COLLATIONS
表的
ID
列的类型是
bigint unsigned

那么用什么
java
类型来接收?往上面翻一翻,是不是用
java.math.BigInteger
来接收?

MySQL 5.7.36
中,
COLLATIONS
表的
ID
列的类型却是
bigint(11)

所以用
java.lang.Long
来接收

5、
for
循环遍历
sortedCollationMap
,完成数据库字段级字符集/字符序到java字符编码名称的映射

indexEntry.getKey()

MySQL 8.0.26
会抛出异常:
java.math.BigInteger cannot be cast to java.lang.Long

而在
MySQL 5.7.36
则正常,不抛出异常

至此,大家对根因是不是完全明白了?

说的简单点就是:
mysql-connector-java 5.1.26
配不上
MySQL 8

说的详细点就是:
mysql-connector-java 5.1.26
适配的是
COLLATIONS

bigint
类型的
ID
,而非
bigint unsigned
类型的
ID

说的抽象点就是:上岸第一剑 先斩意中人,
MySQL
从 5 上岸到了 8,
mysql-connector-java
如果不跟着上岸的话,会被第一个斩杀

Unknown system variable 'query_cache_size'

因为用的是
都说了能不动就别动,非要去调整,出生产事故了吧
中的
demo

play_it_safe

所以我顺手将
mysql-connector-java
调整到了
5.1.37

MySQL
还是
8.0.26
,结果启动出现了另一个异常


java.sql.SQLException: Unknown system variable 'query_cache_size'at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:959) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:
3870) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:
3806) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:
2470) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:
2617) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:
2546) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:
2504) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:
1370) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.loadServerVariables(ConnectionImpl.java:
3861) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:
3289) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:
2298) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:
2084) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.
<init>(ConnectionImpl.java:795) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.JDBC4Connection.
<init>(JDBC4Connection.java:44) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:
45) ~[na:1.8.0_91]
at java.lang.reflect.Constructor.newInstance(Constructor.java:
423) ~[na:1.8.0_91]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:
404) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:
400) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:
327) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:
138) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:
358) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:
206) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:
477) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:
560) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.
<init>(HikariPool.java:115) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:
112) [HikariCP-3.4.5.jar:na]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.buildSqlSessionFactory(MybatisSqlSessionFactoryBean.java:
591) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.afterPropertiesSet(MybatisSqlSessionFactoryBean.java:
422) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.getObject(MybatisSqlSessionFactoryBean.java:
636) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration.sqlSessionFactory(MybatisPlusAutoConfiguration.java:
172) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$eeb10e55.CGLIB$sqlSessionFactory$
2(<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$eeb10e55$$FastClassBySpringCGLIB$$de0ae008.invoke(
<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:
244) [spring-core-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:
331) [spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$eeb10e55.sqlSessionFactory(
<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43) ~[na:1.8.0_91]
at java.lang.reflect.Method.invoke(Method.java:
498) ~[na:1.8.0_91]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:
154) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:
650) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:
635) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:
1336) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:
1176) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:
556) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:
516) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$
0(AbstractBeanFactory.java:324) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:
234) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:
322) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:
202) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:
276) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:
1307) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:
1227) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:
1509) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:
1404) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:
593) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:
516) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$
0(AbstractBeanFactory.java:324) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:
234) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:
322) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:
202) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:
878) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:
879) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:
551) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:
141) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:
747) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:
397) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
315) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
1226) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
1215) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at com.qsl.PlayItSafeApplication.main(PlayItSafeApplication.java:
16) ~[classes/:na]

View Code

看到这个异常,不知道你们第一反应想到的是什么?

我第一反应想到的是
MySQL 8
取消了查询缓存,而
MySQL 5
是有查询缓存的

异常提示的已经很明显了,我们来验证下

MySQL 5.7.36
是有系统变量
query_cache_size


MySQL 8.0.26
是没有的

后面我又将
mysql-connector-java
升级到
5.1.42
,启动还是异常:
Unknown system variable 'query_cache_size'

Could not create connection to database server

因为需要用
MySQL 5.7.36
做对比,所以还是
Docker
快速安装了一个
MySQL 5.7.36

顺带也安装了
MySQL 8.0.26
(前面用的是
Win10
版,这个相当于是
Linux
版本)

然后我又鬼使神差的将连接调整到了
Linux
版的
MySQL 8.0.26


mysql-connector-java

5.1.42
时,启动异常是:
Unknown system variable 'query_cache_size'


mysql-connector-java

5.1.37
时,启动异常是:
Could not create connection to database server


com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:
45) ~[na:1.8.0_91]
at java.lang.reflect.Constructor.newInstance(Constructor.java:
423) ~[na:1.8.0_91]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:
404) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.Util.getInstance(Util.java:
387) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
919) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
898) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
887) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
862) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:
2331) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:
2084) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.
<init>(ConnectionImpl.java:795) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.JDBC4Connection.
<init>(JDBC4Connection.java:44) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:
45) ~[na:1.8.0_91]
at java.lang.reflect.Constructor.newInstance(Constructor.java:
423) ~[na:1.8.0_91]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:
404) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:
400) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:
327) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:
138) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:
358) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:
206) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:
477) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:
560) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.
<init>(HikariPool.java:115) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:
112) [HikariCP-3.4.5.jar:na]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.buildSqlSessionFactory(MybatisSqlSessionFactoryBean.java:
591) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.afterPropertiesSet(MybatisSqlSessionFactoryBean.java:
422) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.getObject(MybatisSqlSessionFactoryBean.java:
636) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration.sqlSessionFactory(MybatisPlusAutoConfiguration.java:
172) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$b0a04890.CGLIB$sqlSessionFactory$
2(<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$b0a04890$$FastClassBySpringCGLIB$$b0fb4ddc.invoke(
<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:
244) [spring-core-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:
331) [spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$b0a04890.sqlSessionFactory(
<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43) ~[na:1.8.0_91]
at java.lang.reflect.Method.invoke(Method.java:
498) ~[na:1.8.0_91]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:
154) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:
650) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:
635) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:
1336) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:
1176) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:
556) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:
516) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$
0(AbstractBeanFactory.java:324) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:
234) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:
322) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:
202) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:
276) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:
1307) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:
1227) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:
1509) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:
1404) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:
593) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:
516) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$
0(AbstractBeanFactory.java:324) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:
234) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:
322) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:
202) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:
878) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:
879) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:
551) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:
141) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:
747) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:
397) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
315) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
1226) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
1215) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at com.qsl.PlayItSafeApplication.main(PlayItSafeApplication.java:
16) ~[classes/:na]
Caused by: java.lang.NullPointerException:
nullat com.mysql.jdbc.ConnectionImpl.getServerCharset(ConnectionImpl.java:3004) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.sendConnectionAttributes(MysqlIO.java:
1908) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:
1837) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:
1207) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:
2254) ~[mysql-connector-java-5.1.37.jar:5.1.37]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:
2285) ~[mysql-connector-java-5.1.37.jar:5.1.37]
...
62 common frames omitted

View Code

关键异常堆栈是:

真正的异常其实是
NPE
,大家可以参考前面讲到的方法去分析


mysql-connector-java

5.1.26
时,启动异常也是:
Could not create connection to database server


com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:
45) ~[na:1.8.0_91]
at java.lang.reflect.Constructor.newInstance(Constructor.java:
423) ~[na:1.8.0_91]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:
411) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.Util.getInstance(Util.java:
386) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
1015) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
989) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
975) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
920) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:
2568) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:
2304) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.
<init>(ConnectionImpl.java:834) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.JDBC4Connection.
<init>(JDBC4Connection.java:47) ~[mysql-connector-java-5.1.26.jar:na]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:
45) ~[na:1.8.0_91]
at java.lang.reflect.Constructor.newInstance(Constructor.java:
423) ~[na:1.8.0_91]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:
411) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:
416) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:
346) ~[mysql-connector-java-5.1.26.jar:na]
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:
138) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:
358) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:
206) ~[HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:
477) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:
560) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.pool.HikariPool.
<init>(HikariPool.java:115) [HikariCP-3.4.5.jar:na]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:
112) [HikariCP-3.4.5.jar:na]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.buildSqlSessionFactory(MybatisSqlSessionFactoryBean.java:
591) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.afterPropertiesSet(MybatisSqlSessionFactoryBean.java:
422) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean.getObject(MybatisSqlSessionFactoryBean.java:
636) [mybatis-plus-extension-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration.sqlSessionFactory(MybatisPlusAutoConfiguration.java:
172) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$8bb2ad81.CGLIB$sqlSessionFactory$
1(<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$8bb2ad81$$FastClassBySpringCGLIB$$fce7a55a.invoke(
<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:
244) [spring-core-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:
331) [spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration$$EnhancerBySpringCGLIB$$8bb2ad81.sqlSessionFactory(
<generated>) [mybatis-plus-boot-starter-3.1.0.jar:3.1.0]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
~[na:1.8.0_91]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
62) ~[na:1.8.0_91]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43) ~[na:1.8.0_91]
at java.lang.reflect.Method.invoke(Method.java:
498) ~[na:1.8.0_91]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:
154) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:
650) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:
635) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:
1336) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:
1176) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:
556) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:
516) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$
0(AbstractBeanFactory.java:324) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:
234) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:
322) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:
202) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:
276) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:
1307) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:
1227) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:
1509) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:
1404) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:
593) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:
516) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$
0(AbstractBeanFactory.java:324) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:
234) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:
322) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:
202) [spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:
878) ~[spring-beans-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:
879) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:
551) ~[spring-context-5.2.9.RELEASE.jar:5.2.9.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:
141) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:
747) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:
397) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
315) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
1226) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:
1215) ~[spring-boot-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at com.qsl.PlayItSafeApplication.main(PlayItSafeApplication.java:
16) ~[classes/:na]
Caused by: java.lang.NullPointerException:
nullat com.mysql.jdbc.ConnectionImpl.getServerCharacterEncoding(ConnectionImpl.java:3279) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.MysqlIO.sendConnectionAttributes(MysqlIO.java:
1940) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:
1866) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:
1252) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:
2486) ~[mysql-connector-java-5.1.26.jar:na]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:
2519) ~[mysql-connector-java-5.1.26.jar:na]
...
62 common frames omitted

View Code

关键异常堆栈是:

总结

MySQL
驱动版本和
MySQL
版本一定要门当户对,否则就会错付

要升就一起升,只升一边,就会出现升的一方看不上没升的那方:上岸第一剑 先斩意中人

1 介绍

在计算机网络领域,代理服务器扮演着至关重要的角色,
它在服务器与客户端之间充当中间人的角色,有效地提高系统的安全性、性能和可扩展性。
其中正向代理和反向代理是两种最为常见的代理方式。本文将详细介绍这两种代理方式的概念、特点、使用场景以及用途,帮助读者更好地理解它们在网络架构中的作用。

2 正向代理(Forward Proxy)

2.1 正向代理概念

正向代理是一种位于客户端和服务器之间的代理服务器。客户端将请求发送给正向代理,然后由代理服务器将请求转发给目标服务器。服务器将响应返回给代理服务器,再由代理服务器将响应转发给客户端。
正向代理对客户端是透明的,客户端无需知道实际服务器的地址。

2.2 正向代理特点

1. 隐藏客户端身份:
正向代理可以
隐藏客户端的真实IP地址
,保护客户端的隐私。
2. 访问控制:
正向代理可以根据一定的规则限制或允许客户端的访问请求,
实现访问控制功能

3. 缓存加速:
正向代理可以缓存经常访问的页面或资源,
提高访问速度,减轻服务器负担

2.3 使用场景

image

1. 突破网络限制:
在某些地区或网络环境下,用户可能无法直接访问某些网站或服务。此时,可以通过设置正向代理来突破这些限制,实现访问。
image

2. 网络安全:
通过正向代理,企业可以监控和管理员工的网络访问行为,隐藏客户端真实IP,防止敏感数据泄露。
3. 内容过滤:
学校、图书馆等公共场所可以通过正向代理过滤不良内容,保护用户免受不良信息的侵害。
4. 提高访问速度:
代理服务器设置硬盘缓冲区,可以将部分高频请求的响应数据保存到缓冲区中,以提高访问速度。

3 反向代理(Reverse Proxy)

3.1 反向代理概念

反向代理是一种位于服务器和客户端之间的代理服务器。客户端将请求发送给反向代理,然后由代理服务器根据一定的规则将请求转发给后端服务器。后端服务器将响应返回给代理服务器,再由代理服务器将响应转发给客户端。
反向代理对客户端是透明的,客户端无需知道实际服务器的地址,只需将反向代理当作目标服务器一样发送请求就可以了。

3.2 反向代理特点

1. 负载均衡:
反向代理可以根据后端服务器的负载情况,将请求分发到不同的服务器上,实现负载均衡,提高系统的整体性能。
2. 安全性增强:
反向代理可以隐藏后端服务器的真实地址和端口,防止直接攻击(如DoS/DDoS)。同时,还可以实现SSL加密、访问控制等安全功能。
3. 缓存优化:
反向代理可以缓存静态资源,减少后端服务器的负载,提高响应速度。

image

3.3 使用场景

1. Web应用:
在Web应用中,反向代理常用于实现
负载均衡、安全访问控制以及缓存优化
等功能。
2. API网关:
API网关通常采用反向代理的方式,对外部请求进行统一管理和调度,实现
API的安全、高效访问

3. CDN加速:
内容分发网络(CDN)通过在全球范围内部署反向代理服务器,实现
内容的就近访问和加速传输

image

4 正反向代理的比较

4.1 相同点

1. 位置和功能:
正向代理和反向代理都位于客户端和真实服务器之间
,它们的主要功能都是将客户端的请求转发给服务器,然后再将服务器的响应转发给客户端。
2. 提高访问速度:
两者都能通过缓存机制提高访问速度
。当客户端请求某个资源时,如果代理服务器已经缓存了该资源,就可以直接从缓存中提供,而无需再次从原始服务器获取,从而节省了时间和带宽。

4.2 不同点

1. 代理对象:
正向代理是为客户端提供代理服务,即服务器不知道真正的客户端是谁。而反向代理则是为服务器提供代理服务,即客户端不知道真正的服务器是谁。
2. 架设位置:
正向代理通常是由客户端架设的,而反向代理则是由服务器架设的。
3. 用途和目的:
正向代理的主要用途是为在防火墙内的局域网客户端提供访问Internet的途径,侧重于解决访问限制问题。而反向代理的主要用途是将防火墙后面的服务器提供给Internet用户访问,其目的在于实现负载均衡、安全防护等。
4. 服务对象:
在正向代理中,服务器不知道真正的用户是谁;而在反向代理中,用户不知道真正的服务器是谁。

4.3 四层和七层中反代的区别

  • 四层中根据用户的IP+PORT来做hash
  • 七层中根据Http协议中的属性来做hash(如Header、Cookies)

4 总结

正向代理和反向代理在计算机网络中各自发挥着重要的作用。
正向代理主要关注客户端的访问需求和安全性,而反向代理则更注重后端服务器的负载均衡、安全性和性能优化。
在实际应用中,我们可以根据具体需求选择合适的代理方式,实现网络架构的优化和升级。