JAVA转GO

news2024/11/19 9:39:55

GO

环境配置

go环境

下载go并安装(win下),环境变量他自己要配置上

https://dl.google.com/go/go1.21.3.windows-amd64.msi

验证是否安装成功:

//打开cmd
go version

VSCODE环境

下载VSCODE…略

配置VSCODE的环境

下载插件

image-20231016151040758

go开发工具包

打开cmd,或者VSCODE自带的终端,逐条复制

//打开go mod ,go mod相当于java的maven
go env -w GO111MODULE=on
//设置代理
go env -w GOPROXY=https://goproxy.cn,direct

然后再在VSCODE上下载工具包

Ctrl+Shift+P

输入go

点第一个,安装工具包

image-20231016151403203

全选,确定

image-20231016151434951

等待下载

go mod

go modules是官方推荐的替换GOPATH而诞生的依赖库管理工具(相当于java的maven),之前的包都放在GOPATH了

首先自己下载的go路径应该是在C盘

go env

image-20231016151625930

这样创建一个初始化案例(先转到需要初始化的文件下)

go mod init example.com/m

image-20231016151651807

发现出现了像配置文件的东西,这是用于记录依赖库和版本号的东西

image-20231016152034034

如果使用网上说的初始化

PS D:\work\GoWork\modtest> go mod init
go: cannot determine module path for source directory D:\work\GoWork\modtest (outside GOPATH, module path must be specified)

Example usage:
        'go mod init example.com/m' to initialize a v0 or v1 module    
        'go mod init example.com/m/v2' to initialize a v2 module       

Run 'go help mod init' for more information.

会提示没有指定module

意思是后面要取一个模块的名字,打开生成的go.mod文件就能看见,说明这个模块名字是module

image-20231016153050406

GO的语法

GO的小知识点

go的程序中一行就表示一个语句的结束,如果要多个语句写一行就必须用";"人为区分,实际开发中不用写分号

go的注释

//单行注释

/*

多行注释
*/

标识符:用来命名变量.类型等程序实体,一个标识符就是一个或多个字母和数字,下划线组成,第一个字符不能是数字(就是java的变量名)

字符串连接

package main
import "fmt"
func main() {
    fmt.Println("Google" + "Runoob")
    //输出GoogleRunoob
}

GO的空格

分隔标识符、关键字、运算符和表达式,以提高代码的可读性

一般VSCODE会自动帮忙填空格

在函数调用时,函数名和左边等号之间要使用空格,参数之间也要使用空格。

例如:

result := add(2, 3)

关键字和保留字

关键字

下面列举了 Go 代码中会使用到的 25 个关键字或保留字:

breakdefaultfuncinterfaceselect
casedefergomapstruct
chanelsegotopackageswitch
constfallthroughifrangetype
continueforimportreturnvar

除了以上介绍的这些关键字,Go 语言还有 36 个预定义标识符:

appendboolbytecapclosecomplexcomplex64complex128uint16
copyfalsefloat32float64imagintint8int16uint32
int32int64iotalenmakenewnilpanicuint64
printprintlnrealrecoverstringtrueuintuint8uintptr

程序一般由关键字、常量、变量、运算符、类型和函数组成。

程序中可能会使用到这些分隔符:括号 (),中括号 [] 和大括号 {}。

程序中可能会使用到这些标点符号:.,;:

数据类型

按照语言分:

序号类型和描述
1布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
2数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
3字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
4派生类型: 包括:(a) 指针类型(Pointer)(b) 数组类型© 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型
数字类型

Go 也有基于架构的类型,例如:int、uint 和 uintptr。

序号类型和描述
1uint8 无符号 8 位整型 (0 到 255)
2uint16 无符号 16 位整型 (0 到 65535)
3uint32 无符号 32 位整型 (0 到 4294967295)
4uint64 无符号 64 位整型 (0 到 18446744073709551615)
5int8 有符号 8 位整型 (-128 到 127)
6int16 有符号 16 位整型 (-32768 到 32767)
7int32 有符号 32 位整型 (-2147483648 到 2147483647)
8int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
浮点型
序号类型和描述
1float32 IEEE-754 32位浮点型数
2float64 IEEE-754 64位浮点型数
3complex64 32 位实数和虚数
4complex128 64 位实数和虚数

其他数字类型

以下列出了其他更多的数字类型:

序号类型和描述
1byte 类似 uint8
2rune 类似 int32
3uint 32 或 64 位
4int 与 uint 一样大小
5uintptr 无符号整型,用于存放一个指针

四种变量的声明方式

单变量,多变量,全局变量,变量类型

package main

import (
	"fmt"
)

// 声明全局变量 方法一,方法二,方法三
var gA int = 100
var gB = "qwd"

//方法四的  :=只能用在函数体内
//gc :=200

func main() {
	//方法一:声明一个变量,默认值是0
	var a int
	fmt.Println("a = ", a)
	//方法二:声明一个变量,初始化一个值
	var b int = 100
	fmt.Println("b = ", b)

	//方法三:初始化值的时候,省去数据类型,自动匹配数据类型
	var c = 1
	fmt.Println("c =", c)

	var d = "1"
	fmt.Printf("d=%s ,type of d = %T\n", d, d) //输出这里用的Printf,,输出类型

	//方法四,常用的,省去var关键字,自动匹配
	e := 100.12
	fmt.Println("e=", e)

	//声明多变量
	//var xx,yy int=100,120
	var xx, yy = "a0", 12
	fmt.Println("xx=", xx, ",yy=", yy)

	//多行的,多变量声明
	var (
		vv int  = 100
		jj bool = true
	)
	fmt.Println("vv=", vv, ",jj=", jj)

}

int的零值为0

bool的零值为false

字符串的零值为""

下面类型的零值为nil

var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error 是接口

值与引用

使用等号就是将一个变量的值赋值给另一个变量,例如j=i,实际上是在内存中将i的值进行了拷贝

获取内存地址: &i

也称为指针

变量需要被使用,不然会报错

极简的混合赋值:

a, b, c := 5, 7, "abc"

格式化从字符串

Go 语言中使用 fmt.Sprintffmt.Printf 格式化字符串并赋值给新串:

  • Sprintf 根据格式化参数生成格式化的字符串并返回该字符串。
  • Printf 根据格式化参数生成格式化的字符串并写入标准输出。

GO语言常量

在程序运行时不会改变的量

const b = "ab"

常量枚举

const(
	Unknown=0
	Female=1
	Male=2
)

常量函数使用

可以使用len(),cap(), unsafe.Sizeof()计算值,函数必须是内置函数

package main

import "unsafe"
const (
    a = "abc"
    b = len(a)
    c = unsafe.Sizeof(a)
)

func main(){
    println(a, b, c)
}

iota(特殊常量)

可被编译器修改的常量,在const关键词出现时重置为0,

在const中每新增一行常量声明就使iota计数一次

用法:

package main

import "fmt"

func main() {
    const (
            a = iota   //0
            b          //1
            c          //2
            d = "ha"   //独立值,iota += 1
            e          //"ha"   iota += 1
            f = 100    //iota +=1
            g          //100  iota +=1
            h = iota   //7,恢复计数
            i          //8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i)
    //0 1 2 ha ha 100 100 7 8
}

GO运算符

算术运算符

下表列出了所有Go语言的算术运算符。假定 A 值为 10,B 值为 20。

运算符描述实例
+相加A + B 输出结果 30
-相减A - B 输出结果 -10
*相乘A * B 输出结果 200
/相除B / A 输出结果 2
%求余B % A 输出结果 0
++自增A++ 输出结果 11
自减A-- 输出结果 9

关系运算符

下表列出了所有Go语言的关系运算符。假定 A 值为 10,B 值为 20。

运算符描述实例
==检查两个值是否相等,如果相等返回 True 否则返回 False。(A == B) 为 False
!=检查两个值是否不相等,如果不相等返回 True 否则返回 False。(A != B) 为 True
>检查左边值是否大于右边值,如果是返回 True 否则返回 False。(A > B) 为 False
<检查左边值是否小于右边值,如果是返回 True 否则返回 False。(A < B) 为 True
>=检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。(A >= B) 为 False
<=检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。(A <= B) 为 True

逻辑运算符

下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。

运算符描述实例
&&逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。(A && B) 为 False
||逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。(A || B) 为 True
!逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。!(A && B) 为 True

位运算符

位运算符对整数在内存中的二进制位进行操作。

下表列出了位运算符 &, |, 和 ^ 的计算:

pqp & qp | qp ^ q
00000
01011
11110
10011

赋值运算符

下表列出了所有Go语言的赋值运算符。

运算符描述实例
=简单的赋值运算符,将一个表达式的值赋给一个左值C = A + B 将 A + B 表达式结果赋值给 C
+=相加后再赋值C += A 等于 C = C + A
-=相减后再赋值C -= A 等于 C = C - A
*=相乘后再赋值C *= A 等于 C = C * A
/=相除后再赋值C /= A 等于 C = C / A
%=求余后再赋值C %= A 等于 C = C % A
<<=左移后赋值C <<= 2 等于 C = C << 2
>>=右移后赋值C >>= 2 等于 C = C >> 2
&=按位与后赋值C &= 2 等于 C = C & 2
^=按位异或后赋值C ^= 2 等于 C = C ^ 2
|=按位或后赋值C |= 2 等于 C = C | 2

下表列出了Go语言的其他运算符。

运算符描述实例
&返回变量存储地址&a; 将给出变量的实际地址。
*指针变量。*a; 是一个指针变量

条件运算符

Go 语言提供了以下几种条件判断语句:

语句描述
if 语句if 语句 由一个布尔表达式后紧跟一个或多个语句组成。
if…else 语句if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。
if 嵌套语句你可以在 ifelse if 语句中嵌入一个或多个 ifelse if 语句。
switch 语句switch 语句用于基于不同条件执行不同动作。
select 语句select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。

注意:Go 没有三目运算符,所以不支持 ?: 形式的条件判断。

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

循环语句

计算1到10的数字之和

//没得括号,而且{必须放在后面

package main

import "fmt"

func main() {
   sum := 0
      for i := 0; i <= 10; i++ {
         sum += i
      }
   fmt.Println(sum)
}

使用循环外变量

package main

import "fmt"

func main() {
   sum := 1
   for ; sum <= 10; {
      sum += sum
   }
   fmt.Println(sum)

   // 这样写也可以,更像 While 语句形式
   for sum <= 10{
      sum += sum
   }
   fmt.Println(sum)
}

死循环

就一个for加花括号

package main

import "fmt"

func main() {
   sum := 0
   for {
      sum++ // 无限循环下去
   }
   fmt.Println(sum) // 无法输出
}

停止死循环就用 ctrl+C

迭代输出

package main

import "fmt"

func main() {
	strings := []string{"google", "runoob"}
	for i, s := range strings {
		fmt.Println(i, s)
	}

	numbers := [6]int{1, 2, 3, 5}
	for i, x := range numbers {
		fmt.Printf("第 %d 位 x 的值 = %d\n", i, x)
	}
}

迭代输出省略key和value

package main
import "fmt"

func main() {
    map1 := make(map[int]float32)
    map1[1] = 1.0
    map1[2] = 2.0
    map1[3] = 3.0
    map1[4] = 4.0
    
    // 读取 key 和 value
    for key, value := range map1 {
      fmt.Printf("key is: %d - value is: %f\n", key, value)
    }

    // 读取 key
    for key := range map1 {
      fmt.Printf("key is: %d\n", key)
    }

    // 读取 value
    for _, value := range map1 {
      fmt.Printf("value is: %f\n", value)
    }
}
输出
key is: 4 - value is: 4.000000
key is: 1 - value is: 1.000000
key is: 2 - value is: 2.000000
key is: 3 - value is: 3.000000
key is: 1
key is: 2
key is: 3
key is: 4
value is: 1.000000
value is: 2.000000
value is: 3.000000
value is: 4.000000

函数

最少有个main()函数

命名格式:

func function_name( [parameter list] ) [return_types] {
   函数体
}

func:函数由 func 开始声明
function_name:函数名称,参数列表和返回值类型构成了函数签名。
parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
函数体:函数定义的代码集合。

例子:

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
   /* 声明局部变量 */
   var result int

   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result 
}

GO函数可以返回多个值

package main

import "fmt"

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("Google", "Runoob")
   fmt.Println(a, b)
}

函数的值传递

/* 定义相互交换值的函数 */
func swap(x, y int) int {
   var temp int

   temp = x /* 保存 x 的值 */
   x = y    /* 将 y 值赋给 x */
   y = temp /* 将 temp 值赋给 y*/

   return temp;
}

后面的int是指(int,int),好像可以简写

引用传递

/* 定义交换值函数*/
func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保持 x 地址上的值 */
   *x = *y      /* 将 y 值赋给 x */
   *y = temp    /* 将 temp 值赋给 y */
}

变量作用域

函数内定义的变量称为局部变量
函数外定义的变量称为全局变量
函数定义中的变量称为形式参数

数组

例子:

var balance [10]float32
var numbers [5]int
var numbers = [5]int{1, 2, 3, 4, 5}
numbers := [5]int{1, 2, 3, 4, 5}

多维数组:

a := [3][4]int{  
 {0, 1, 2, 3} ,   /*  第一行索引为 0 */
 {4, 5, 6, 7} ,   /*  第二行索引为 1 */
 {8, 9, 10, 11},   /* 第三行索引为 2 */
}

var threedim [5][10][4]int

函数里的数组:

void myFunction(param []int)
{
.
.
.
}

指针

取地址符&

取值 *

package main

import "fmt"

func main() {
   var a int= 20   /* 声明实际变量 */
   var ip *int        /* 声明指针变量 */

   ip = &a  /* 指针变量的存储地址 */

   fmt.Printf("a 变量的地址是: %x\n", &a  )

   /* 指针变量的存储地址 */
   fmt.Printf("ip 变量储存的指针地址: %x\n", ip )

   /* 使用指针访问值 */
   fmt.Printf("*ip 变量的值: %d\n", *ip )
}

空指针

if(ptr != nil)     /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

指针交换

package main

import "fmt"

func main() {
    /* 定义局部变量 */
   var a int = 100
   var b int= 200
   a, b = b, a

   fmt.Printf("交换后 a 的值 : %d\n", a )
   fmt.Printf("交换后 b 的值 : %d\n", b )
}

结构体

相当于对象

package main

import "fmt"

type Books struct {
	title   string
	author  string
	subject string
	book_id int
}

func main() {
	// 创建一个新的结构体
	fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})

	// 也可以使用 key => value 格式

	fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})

	// 忽略的字段为 0 或 空
	fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})

	//访问结构成员

	var book1 Books
	book1.author = "这是作者"
	book1.book_id = 123
	fmt.Println(book1)
	fmt.Println(book1.author)

}

Go语言切片

相当于arraylist,可以动态追加元素的数组(动态数组)

切片不需要说明长度。

或使用 make() 函数来创建切片:

var slice1 []type = make([]type, len)

也可以简写为

slice1 := make([]type, len)

len()和cap()函数

len()是计算长度的,cap()是测量切片最长长度(额外长度?)

切片截取

package main

import "fmt"

func main() {
	/* 创建切片 */
	numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
	printSlice(numbers)

	/* 打印原始切片 */
	fmt.Println("numbers ==", numbers)

	/* 打印子切片从索引1(包含) 到索引4(不包含)*/
	fmt.Println("numbers[1:4] ==", numbers[1:4])

	/* 默认下限为 0*/
	fmt.Println("numbers[:3] ==", numbers[:3])

	/* 默认上限为 len(s)*/
	fmt.Println("numbers[4:] ==", numbers[4:])

	numbers1 := make([]int, 0, 5)
	printSlice(numbers1)

	/* 打印子切片从索引  0(包含) 到索引 2(不包含) */
	number2 := numbers[:2]
	printSlice(number2)

	/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
	number3 := numbers[2:5]
	printSlice(number3)

}

func printSlice(x []int) {
	fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}


append()和copy()

例:
numbers = append(numbers,-2)//追加数字"-2"
numbers = append(numbers,-2,2,99)//追加数字"-2,2,99"

//创建切片numbers1,为上个切片的两倍容量
numbers1 := make([]int, len(numbers), (cap(numbers))*2)


//从numbers拷贝到numbers1
copy(numbers1,numbers)


Range(迭代元素)

用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素

for key, value := range numbers {
		fmt.Printf("%d-->%d", key, value)
	}
	//key在map之类的正常用,在切片/数组之类的key是数组索引

可以迭代字符串,迭代出来是Unicode

Map

键值对

//创建一个空map,键是string,值是int
m := make(map[string]int)
//创建带有初始容量的
m := make(map[string]int,10)
//创建自带键值对的
// 使用字面量创建 Map
m := map[string]int{
    "apple": 1,
    "banana": 2,
    "orange": 3,
}

// 获取键值对
value := m["apple"]
value, ok := m["pear"]  
//如果键不存在,ok 的值为 false,v2 的值为该类型的零值
//这里不能用bool,因为bool是关键字
//也记得不要取名为map,map也是关键字,小写有些是关键字

//修改元素
m["apple"]=5

//获取Map的长度
len:=len(m)

//遍历
for k,v:=range m{
    fmt.Printf("key=%s,value=%d\n",k,v)
}

//删除元素
delete(m,"banana")

递归

记得写好退出条件

func recursion() {
   recursion() /* 函数调用自身 */
}

func main() {
   recursion()
}

类型转换

整形转浮点

var a int = 10
var b float64 = float64(a)
//貌似像强转,但是换了括号位置

字符串类型转换

字符串转成整数

var str string="10"
var num int
num,_=strconv.Atoi(str)
//conv的意思是转换器/卷积
//Atoi的意思是ASCII to Integer

整数转换成字符串

num := 123
str := strconv.Itoa(num)

浮点转换字符串

num := 3.14
str := strconv.FormatFloat(num, 'f', 2, 64)
//f是格式说明符, 2是精度 小数点后两位,  64是表示浮点的位数 表示双精度

可以兼容的转换(int8转int64之类的)

	var i8 int8 = 12
	var i64 = int64(i)

接口

接口可以让我们将不同类型绑定到一组公共方法上,从而实现多态和灵活的设计

Go的接口是隐式实现的(如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口)

package main

import (
	"fmt"
)

//接口类型
type Phone interface {
	call()
}

//诺基亚手机类型,用结构体代替对象
type NokiaPhone struct {
	str string
}

//诺基亚手机的call方法 ,带有参数,是为了类似于java中的this
func (nokiaPhone NokiaPhone) call() {
	nokiaPhone.str = "I am Nokia"
	fmt.Println(nokiaPhone.str, ", I can call you! ")
}

//苹果手机的结构体
type IPhone struct {
    
}

//苹果手机的call方法
func (iPhone IPhone) call() {
	fmt.Println("I am iPhone, I can call you!")
}

func main() {
	var phone Phone

	phone = new(NokiaPhone)
	phone.call()

	phone = new(IPhone)
	phone.call()

}

错误处理

内置错误接口提供了简单的错误处理机制

error类型是一个接口类型

在返回值的后面加上错误处理

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // 实现
}

自定义异常结构(类)

package main

import (
    "fmt"
)

// 定义一个 DivideError 结构
type DivideError struct {
    dividee int
    divider int
}

// 实现 `error` 接口
func (de *DivideError) Error() string {
    strFormat := `
    Cannot proceed, the divider is zero.
    dividee: %d
    divider: 0
`
    return fmt.Sprintf(strFormat, de.dividee)
}

// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
    if varDivider == 0 {
            dData := DivideError{
                    dividee: varDividee,
                    divider: varDivider,
            }
            errorMsg = dData.Error()
            return
    } else {
            return varDividee / varDivider, ""
    }

}

func main() {

    // 正常情况
    if result, errorMsg := Divide(100, 10); errorMsg == "" {
            fmt.Println("100/10 = ", result)
    }
    // 当除数为零的时候会返回错误信息
    if _, errorMsg := Divide(100, 0); errorMsg != "" {
            fmt.Println("errorMsg is: ", errorMsg)
    }

}

并发

通过关键字开启goroutine

goroutine就是轻量级线程,

//开启新线程方法
go func1(x,y,z)

//go是关键字,func1是方法,xyz是参数

通道(channel)

传递数据的一个数据结构

用于两个goroutine传递指定类型的值来同步运行和通讯

操作符<-用于指定通道方向,没有指定方向就是表示双向通道

//channel创建
ch :=make(chan int)

ch <- v //把v发送给ch
v :<- ch  //从ch里接收数据,赋值给v


默认情况不带缓冲区,意思就是像水龙头接水一样,错过了就不管

带缓冲的通道

ch :=make(chan int, 100)//100是指,可hu
package main

import (
	"fmt"
)

func fibonacci(n int, c chan int) {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

func main() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
	// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
	// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
	// 会结束,从而在接收第 11 个数据的时候就阻塞了。
	for i := range c {
		fmt.Println(i)
	}
}

小练习:

Gin

Gin是一个 go 写的 web 框架,具有高性能的优点。官方地址:https://github.com/gin-gonic/gin

Gin用下面语句导一下包,就能用

 go get -u github.com/gin-gonic/gin

Gin的简单实践:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"os"
	"time"
)

func main() {
	router := gin.Default()
	router.Use(RequestTimingMiddleware)
	router.GET("/hi", func(context *gin.Context) {
		context.JSON(200, gin.H{"msg": "hello"})
	})

	router.GET("/healthz", func(context *gin.Context) {
		//获取请求头
		requestHeader := context.Request.Header
		//添加VERSION环境变量
		requestHeader.Add("VERSION", os.Getenv("VERSION"))

		//Server 端记录访问日志包括客户端 IP,HTTP 返回码,输出到 server 端的标准输出
		fmt.Printf("Client IP为%s, HTTP Status: %d, Method: %s, Path: %s\n", context.ClientIP(), context.Writer.Status(), context.Request.Method, context.Request.URL.Path)
		//迭代设置请求头
		for key, values := range requestHeader {
			for _, value := range valu2es {
				context.Writer.Header().Set(key, value)
			}
		}
		//返回
		context.JSON(200, gin.H{"msg": "返回请求"})
	})

	router.Run(":8099")
}

// 时间计算中间件
func RequestTimingMiddleware(c *gin.Context) {
	startTime := time.Now()
	c.Next()
	endTime := time.Now()
	countTime := endTime.Sub(startTime)

	fmt.Printf("耗时: %v\n", countTime)
}

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

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

相关文章

OFDM基本原理

一、OFDM简述 OFDM 的全称是 Orthogonal Frequency Divisition Multiplexing&#xff0c;是一种多载波调制技术&#xff0c;其能有效对抗频率选择性衰落&#xff0c;克服信号符号间干扰。OFDM的技术的核心思想是将宽频率载波划分成多个带宽较小的正交子载波&#xff0c;并使用这…

2020年亚太杯APMCM数学建模大赛B题美国总统的经济影响分析求解全过程文档及程序

2020年亚太杯APMCM数学建模大赛 B题 美国总统的经济影响分析 原题再现&#xff1a; 美国总统选举每四年举行一次。 2020年是美国总统大选年&#xff0c;共和党候选人唐纳德特朗普和民主党对手乔拜登竞选总统。 甲乙双方候选人在金融贸易&#xff0c;经济金融治理&#xff0c;…

【嵌入式开发问答】不是普通的嵌入式八股

1. 进程、线程、堆栈、溢出 【问&#xff1a;】 进程的堆栈的物理内存是什么时候分配的&#xff1f; 堆栈的大小限制是多大&#xff1f;这个限制可以调整吗&#xff1f; 当堆栈发生溢出后应用程序会发生什么&#xff1f; 【答&#xff1a;】

七个开发者不可不知的VS Code小技巧

本文就来分享 7 个极大提高开发效率的 VS Code 技巧&#xff01; 目录 1.固定滚动&#xff08;Sticky Scroll&#xff09; 2.命令面板&#xff08;Command Palette&#xff09; 3.自定义代码片段&#xff08;Custom Snippets&#xff09; 4.文件查找&#xff08;File Finde…

软件测试方法分类

软件测试方法种类繁多&#xff0c;有白盒测试、黑盒测试、静态测试、动态测试、集成测试等等&#xff0c;记忆起来容易混乱&#xff0c;傻傻分不清楚&#xff0c;如果把软件测试方法进行分类, 就会清晰很多。现在小峰把常用的软件测试方法列出来&#xff0c;让大家更容易区分记…

百度发布文心大模型4.0,百度搜索实现重构;AI报告2023

&#x1f989; AI新闻 &#x1f680; 百度发布文心大模型4.0&#xff0c;百度搜索实现重构 摘要&#xff1a;10月17日&#xff0c;百度创始人李彦宏在百度世界2023上发布了文心大模型4.0&#xff0c;并开启邀请测试。这是迄今为止最强大的文心大模型&#xff0c;提升了理解、…

vue 自定义指令 -- 指令的值

vue 自定义指令 – 指令的值 **创建 工程&#xff1a; H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day05\准备代码\05-自定义指令-指令的值 vue --vers…

HarmonyOS 语言基础类库开发指南上线啦!

语言基础类库提供哪些功能&#xff1f;多线程并发如何实现&#xff1f;TaskPool&#xff08;任务池&#xff09;和 Worker 在实现和使用场景上有何不同&#xff1f; 针对开发者关注的并发等语言基础类库的相关能力&#xff0c;我们在新推出的语言基础类库开发指南中提供了详细的…

pip报failed to create process

使用pip命令的时候报failed to create process 1、错误提示窗口如下图 2、报这个错误的原因&#xff0c;是因为你改动了python的目录名称或位置。因为&#xff0c;我的电脑是安装了anaconda2和anaconda3的&#xff0c;我想让python2和python3共存&#xff0c;就将anaconda2和a…

一些前端面试思考

回流和重绘 先牢记这句话&#xff0c;回流必将引起重绘&#xff0c;而重绘不一定会引起回流。回流的代价要远大于重绘。 当你给一个元素更换颜色&#xff0c;这样的行为是不会影响页面布局的&#xff0c;DOM树不会变化&#xff0c;但颜色变了&#xff0c;渲染树得重新渲染页面&…

可以充当销售的高品质画册是怎么制作的?

一本优秀的宣传画册就如同一个销售员&#xff0c;具有卓越的灵魂&#xff0c;可以让人达到赏心悦目的视觉效果。如何做出这种高品质的宣传画册呢&#xff1f; 其实很简单&#xff0c;FLBOOK里面汇集了各种各样的宣传画册模板 &#xff0c;只需一键套用模板制作&#xff0c;你也…

算法通过村第十五关-超大规模|白银笔记|经典问题

文章目录 前言从40个亿中产生一个不存在的整数位图存储数据的原理使用10MB来存储如何确定分块的区间 用2GB内存在20亿的整数中找到出现次数最多的数从100亿个URL中查找的问题40亿个非负整数中找出两次的数。总结 前言 提示&#xff1a;人生之中总有空白&#xff0c;但有时&…

2023年安徽省安全员C证证模拟考试题库及安徽省安全员C证理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年安徽省安全员C证证模拟考试题库及安徽省安全员C证理论考试试题是由安全生产模拟考试一点通提供&#xff0c;安徽省安全员C证证模拟考试题库是根据安徽省安全员C证最新版教材&#xff0c;安徽省安全员C证大纲整理…

idea中还原dont ask again

背景 在使用idea打开另外一个项目的时候&#xff0c;一不小心勾选为当前项目而且是不在下次询问&#xff0c;导致后面每次打开新的项目都会把当前项目关闭&#xff0c;如下图所示 下面我们就一起看一下如何把这个询问按钮还原回来 preferences/settings->Appearance&…

安全事件报告和处置制度

1、总则 1.1、目的 为了严密规范XXXXX单位信息系统的安全事件处理程序&#xff0c;确保各业务系统的正常运行和系统及网络的安全事件得到及时响应、处理和跟进&#xff0c;保障网络和系统持续安全运行&#xff0c;确保XXXXX单位重要计算机信息系统的实体安全、运行安全和数据…

数字孪生技术:餐饮业智能化的未来

随着科技的不断进步&#xff0c;数字孪生技术正逐渐渗透到各行各业&#xff0c;为餐饮业带来了颠覆性的改变。这一技术的应用&#xff0c;已经在餐饮行业中崭露头角&#xff0c;并在提高效率、提供更好的客户体验、降低成本等方面发挥了关键作用。 数字孪生技术允许餐饮企业创…

中国人民大学与加拿大女王大学金融硕士:在该奋斗的岁月里,对得起每一寸光阴

在这个快速变化的世界中&#xff0c;金融行业面临不断更新的挑战和机遇。为了应对这些挑战&#xff0c;中国人民大学与加拿大女王大学合作举办金融硕士项目&#xff0c;旨在培养具有国际视野、扎实的金融理论基础和实战经验的专业人才。 中国人民大学和加拿大女王大学金融硕士…

python元组、拆包和装包

注意 元组不能修改元素 元组&#xff1a;如果元素为字符串且元素为1个&#xff0c;必须加一个&#xff0c; ********* t1 (aa,) 下标和切片 in not in for ... in ... 元组转为列表 拆包、装包

【Linux】userdel 命令使用

userdel命令用于删除用户帐号。 语法 userdel [选项] [用户帐号] 命令选项及作用 执行令 userdel--help 执行命令结果 参数 -f, --force 强制删除用户账号 -h, --help 显示此帮助信息并推出 -r, --remove 删除主目录和邮件池 -R, -…

QT_day1

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口相关设置this->setWindowTitle("登录窗口");this->setWindowIcon(QIcon("C:\\Users\\EDY\\Desktop\\pictrue\\qq.png"));this->setWindowFlag(Qt::…