我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Go微服务网关的实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Go微服务网关的实现

Go微服务网关

从核心原理理解网关的本质

网关具备的基本功能:

  • 支持多种协议代理:tcp/http/ websocket/grpc
  • 支持多种负载均衡策略:轮询,权重轮询,hash一致性轮询
  • 支持下游的服务发现:主动探测 / 自动服务发现
  • 支持横向扩展: 加机器就能解决高并发

借助网关处理高可用,高并发

  • 限流:请求QPS限制
  • 熔断:错误率达阈值则服务熔断
  • 降级:确保核心业务可用
  • 权限认证:请求拦截

网络基础大纲

OSI七层网络协议

在这里插入图片描述

经典协议与数据包

​    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-geNZSVqb-1648194571597)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647745113748.png)]

http 协议

GET/HTTP/1.1
Host:www.baidu.com
User-Agent:curl/7.55.1
Accept:*/*

在这里插入图片描述

Websocket握手协议

在这里插入图片描述

在这里插入图片描述

三次握手 与 四次挥手

三次握手的最主要的目的是保证连接是全双工的,可靠更多的是通过重传机制来保证的

因为连接是全双工的,双方必须都收到对方的FIN包及确认才可关闭

TCP报文格式:

在这里插入图片描述

其中比较重要的字段有:

(1)序号(sequence number):Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

(2)确认号(acknowledgement number):Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。

(3)标志位(Flags):共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:

URG:紧急指针(urgent pointer)有效。ACK:确认序号有效。PSH:接收方应该尽快将这个报文交给应用层。RST:重置连接。SYN:发起一个新连接。FIN:释放一个连接。

需要注意的是:

不要将确认序号Ack与标志位中的ACK搞混了。确认方Ack=发起方Seq+1,两端配对。

三次握手连接:

在这里插入图片描述

(1)首先客户端向服务器端发送一段TCP报文,其中:

  • 标记位为`SYN,表示“请求建立新连接”;
  • 序号为Seq=X(X一般为1);
  • 随后客户端进入SYN-SENT阶段。

(2)服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,其中:

  • 标志位为SYN和ACK,表示“确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);
  • 序号为Seq=y;
  • 确认号为Ack=x+1,表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值;随后服务器端进入SYN-RCVD阶段。

(3)客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:

  • 标志位为ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);
  • 序号为Seq=x+1,表示收到服务器端的确认号Ack,并将其值作为自己的序号值;
  • 确认号为Ack=y+1,表示收到服务器端序号Seq,并将其值加1作为自己的确认号Ack的值;
  • 随后客户端进入ESTABLISHED阶段。

服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。

在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。

四次挥手:

在这里插入图片描述

(1)首先客户端想要释放连接,向服务器端发送一段TCP报文,其中:

  • 标记位为FIN,表示“请求释放连接“;
  • 序号为Seq=U;
  • 随后客户端进入FIN-WAIT-1阶段,即半关闭阶段。并且停止在客户端到服务器端方向上发送数据,但是客户端仍然能接收从服务器端传输过来的数据。注意:这里不发送的是正常连接时传输的数据(非确认报文),而不是一切数据,所以客户端仍然能发送ACK确认报文。

(2)服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,随后服务器端结束ESTABLISHED阶段,进入CLOSE-WAIT阶段(半关闭状态)并返回一段TCP报文,其中:

  • 标记位为ACK,表示“接收到客户端发送的释放连接的请求”;
  • 序号为Seq=V;
  • 确认号为Ack=U+1,表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值;
  • 随后服务器端开始准备释放服务器端到客户端方向上的连接。客户端收到从服务器端发出的TCP报文之后,确认了服务器收到了客户端发出的释放连接请求,随后客户端结束FIN-WAIT-1阶段,进入FIN-WAIT-2阶段

前"两次挥手"既让服务器端知道了客户端想要释放连接,也让客户端知道了服务器端了解了自己想要释放连接的请求。于是,可以确认关闭客户端到服务器端方向上的连接了

(3)服务器端自从发出ACK确认报文之后,经过CLOSED-WAIT阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文,其中:

  • 标记位为FIN,ACK,表示“已经准备好释放连接了”。注意:这里的ACK并不是确认收到服务器端报文的确认报文。
  • 序号为Seq=W;
  • 确认号为Ack=U+1;表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值。

随后服务器端结束CLOSE-WAIT阶段,进入LAST-ACK阶段。并且停止在服务器端到客户端的方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。

(4)客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文,其中:

  • 标记位为ACK,表示“接收到服务器准备好释放连接的信号”。
  • 序号为Seq=U+1;表示是在收到了服务器端报文的基础上,将其确认号Ack值作为本段报文序号的值。
  • 确认号为Ack=W+1;表示是在收到了服务器端报文的基础上,将其序号Seq值作为本段报文确认号的值。随后客户端开始在TIME-WAIT阶段等待2MSL

为什么要客户端要等待2MSL呢?见后文。

服务器端收到从客户端发出的TCP报文之后结束LAST-ACK阶段,进入CLOSED阶段。由此正式确认关闭服务器端到客户端方向上的连接。

客户端等待完2MSL之后,结束TIME-WAIT阶段,进入CLOSED阶段,由此完成“四次挥手”。

后“两次挥手”既让客户端知道了服务器端准备好释放连接了,也让服务器端知道了客户端了解了自己准备好释放连接了。于是,可以确认关闭服务器端到客户端方向上的连接了,由此完成“四次挥手”。

与“三次挥手”一样,在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性,一旦出现某一方发出的TCP报文丢失,便无法继续"挥手",以此确保了"四次挥手"的顺利完成。

为什么客户端在TIME-WAIT阶段要等2MSL?

  • 为的是确认服务器端是否收到客户端发出的ACK确认报文
  • 保证TCP协议的全双共连接能够可靠关闭
  • 保证这次连接的重复数据段从网络中消失

当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。MSL指的是(最大的生命周期)Maximum Segment Lifetime:(30秒–1分钟)一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。

服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文;

如果客户端在2MSL内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的ACK确认报文。客户端再次向服务器端发出ACK确认报文,计时器重置,重新开始2MSL的计时;否则客户端在2MSL内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。

所以,客户端要经历时长为2SML的TIME-WAIT阶段;这也是为什么客户端比服务器端晚进入CLOSED阶段的原因

为啥会出现大量的close_wait

  • 首先close_wait一般出现在被动关闭方
  • 并发请求太多导致
  • 被动关闭方未及时释放端口资源导致
func main() {
	//1、监听端口
	listener, err := net.Listen("tcp", "0.0.0.0:9090")
	if err != nil {
		fmt.Printf("listen fail, err: %v\n", err)
		return
	}
	//2.建立套接字连接
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Printf("accept fail, err: %v\n", err)
			continue
		}
		//3. 创建处理协程
		go func(conn net.Conn) {
			defer conn.Close() //思考题:这里不填写会有啥问题?
            //服务端就有一个close,wait状态,客户端就有一个finally 状态
			for {
				var buf [128]byte
				n, err := conn.Read(buf[:])
				if err != nil {
					fmt.Printf("read from connect failed, err: %v\n", err)
					break
				}
				str := string(buf[:n])
				fmt.Printf("receive from client, data: %v\n", str)
			}
		}(conn)
	}
}

TCP为啥需要流量控制

  • 由于通讯双方,网速不同。通讯方任一方发送过快都会导致对方的消息处理不过来,所以就需要数据放到缓冲区中
  • 如果缓冲区满了,发送方还在疯狂发送,那接收方只能把数据包丢弃,因此我们需要控制发送速率
  • 我们缓冲区剩余大小称之为接收窗口,用变量win表示,如果win=0,则发送方停止发送

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VPPRtwrp-1648194571601)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647790417307.png)]

TCP 为啥需要拥塞控制

  • 流量控制与拥塞控制是两个概念,拥塞控制是调节网络的负载
  • 接收方网络资源繁忙,因未及时响应ACK导致发送方重传大量的数据,这样将会导致网络更加的拥堵
  • 拥塞控制是动态调整win大小,不只是依赖缓冲区大小去确定窗口大小

TCP 拥塞控制

  • 慢开始和拥塞避免
  • 快速重传和快速恢复

在这里插入图片描述

在这里插入图片描述

优化步骤3到步骤4:因为网络拥塞,有24直接降到1 ,会造成堵塞

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g8ZHBvyy-1648194571602)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647791444149.png)]

为啥会出现粘包,拆包,如何处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vWmWpIu-1648194571603)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647791724023.png)]

粘包、拆包表现形式

现在假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下:

第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象,此种情况不在本文的讨论范围内。

img

第二种情况,接收端只收到一个数据包,由于TCP是不会出现丢包的,所以这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理。

img

第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。这两种情况如果不加特殊处理,对于接收端同样是不好处理的。

img

产生tcp粘包和拆包的原因

我们知道tcp是以流动的方式传输数据,传输的最小单位为一个报文段(segment)。tcp Header中有个Options标识位,常见的标识为mss(Maximum Segment Size)指的是,连接层每次传输的数据有个最大限制MTU(Maximum Transmission Unit),一般是1500比特,超过这个量要分成多个报文段,mss则是这个最大限制减去TCP的header,光是要传输的数据的大小,一般为1460比特。换算成字节,也就是180多字节。

tcp为提高性能,发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了之后,再将缓冲中的数据发送到接收方。同理,接收方也有缓冲区这样的机制,来接收数据。

发生TCP粘包、拆包主要是由于下面一些原因:

  • 应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。
  • 应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。
  • 进行mss(最大报文长度)大小的TCP分段,当TCP报文长度-TCP头部长度>mss的时候将发生拆包。
  • 接收方法不及时读取套接字(socket)缓冲区数据,这将发生粘包。

如何解决拆包粘包

既然知道了tcp是无界的数据流,且协议本身无法避免粘包,拆包的发生,那我们只能在应用层数据协议上,加以控制。通常在制定传输数据时,可以使用如下方法:

  • 使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。
  • 设置定长消息,服务端每次读取既定长度的内容作为一条完整消息。
  • 设置消息边界,服务端从网络流中按消息编辑分离出消息内容。

如何获取完整应用数据报文

  • 使用带消息头的协议,头部写入包长度,然后在读取包内容
  • 设置定长消息,每次读取定长内容,长度不够时空位补固定字符
  • 设置消息边界,服务端从网络流中按消息边界分离出消息内容,一般使用 ‘\n’
  • 更为复杂的协议:json,protobuf

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FNUamEZv-1648194571603)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647843018454.png)]

如何获取完整的数据报文

func main() {
	//类比接收缓冲区
	bytesBuffer := bytes.NewBuffer([]byte{})
	// 发送
	if err := Encode(bytesBuffer, "hello world 0 !!"); err != nil {
		panic(err)
	}
	if err := Encode(bytesBuffer, "hello world 1 !!"); err != nil {
		panic(err)
	}
	//读取
	for {
		if bt, err := Decode(bytesBuffer); err == nil {
			fmt.Println(string(bt))
			continue
		}
		break
	}
}

如何获取完整的数据报文

tcp_server

func main() {
	//simple tcp server
	//1.监听端口
	listener, err := net.Listen("tcp", "127.0.0.1:9090")
	if err != nil {
		fmt.Printf("tcp Listen fail,err: %v\n", err)
		return
	}
	//2.接受请求
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Printf("tcp Accept fail,err: %v\n", err)
			continue
		}
		//3.创建协程
		go process(conn)
	}
}

//4.创建的协程里面实现解码的功能
func process(conn net.Conn) {
	defer conn.Close()
	for {
		bt, err := unpack.Decode(conn)
		if err != nil {
			fmt.Printf("read from connect failed, err: %v\n", err)
			break
		}
		str := string(bt)
		fmt.Printf("receive from client, data: %v\n", str)
	}
}

tcp_client

func main() {
	//1.连接tcp服务器
	conn, err := net.Dial("tcp", "localhost:9090")
	defer conn.Close()
	if err != nil {
		fmt.Printf("connect failed, err : %v\n", err.Error())
		return
	}
	//2.实现编码
	unpack.Encode(conn, "hello world 0!!!")
}

**unpack ** : 实现编码(encode)和解码(docode)功能


const Msg_Header = "12345678"
// 编码
func Encode(bytesBuffer io.Writer, content string) error {
	//msg_header+content_len+content
	//8+4+content_len
	if err := binary.Write(bytesBuffer, binary.BigEndian, []byte(Msg_Header)); err != nil {
		return err
	}
	clen := int32(len([]byte(content)))
    //  binary.BigEndian 大端字节实现的加密 , 
	if err := binary.Write(bytesBuffer, binary.BigEndian, clen); err != nil {
		return err
	}
	if err := binary.Write(bytesBuffer, binary.BigEndian, []byte(content)); err != nil {
		return err
	}
	return nil
}
// 解码
func Decode(bytesBuffer io.Reader) (bodyBuf []byte, err error) {
	MagicBuf := make([]byte, len(Msg_Header))
    //先读取header的大小
	if _, err = io.ReadFull(bytesBuffer, MagicBuf); err != nil {
		return nil, err
	}
	//比较得到的header和实际的Msg_Header 是否相同
	if string(MagicBuf) != Msg_Header {
		return nil, errors.New("msg_header error")
	}

	lengthBuf := make([]byte, 4)
	if _, err = io.ReadFull(bytesBuffer, lengthBuf); err != nil {
		return nil, err
	}
    //  binary.BigEndian 大端字节实现的解密 ,得到实际数据的长度
	length := binary.BigEndian.Uint32(lengthBuf)
	bodyBuf = make([]byte, length)
	if _, err = io.ReadFull(bytesBuffer, bodyBuf); err != nil {
		return nil, err
	}
	return bodyBuf, err
}

基于golang 实现TCP,UDP,Http服务端与客户端

golang 实现UDP 服务端与客户端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LR962weH-1648194571604)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647852602546.png)]

UDP服务端:

func main() {
	//1.监听端口
	listen, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 9090,
	})
	if err != nil {
		fmt.Printf("listen udp failed ,err:%v\n", err)
		return
	}
	//2.循环读取消息内容
	for {
		var data [1024]byte
		n, addr, err := listen.ReadFromUDP(data[:])
		if err != nil {
			fmt.Printf("read failed from addr :%v,err%v\n", addr, err)
			break
		}
		go func() {
			//3.回复数据
			fmt.Printf("addr:%v data:%v count:%v\n", addr, string(data[:n]), n)
			_, err = listen.WriteToUDP([]byte("received success!"), addr)
			if err != nil {
				fmt.Printf("write failed,err :%v\n", err)
				return
			}
		}()
	}
}

udp客户端

func main() {
	//1. 连接udp服务器
	conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: 9090,
	})
	if err != nil {
		fmt.Printf("connect failed ,err %v\n", err)
		return
	}
	for i := 0; i < 100; i++ {
		// 2.发送数据
		_, err := conn.Write([]byte("hello " +
			"server"))
		if err != nil {
			fmt.Printf("send data failed,err: %v\n", err)
			return
		}
		// 3. 接收数据
		result := make([]byte, 1024)
		n, remoteAddr, err := conn.ReadFromUDP(result)
		if err != nil {
			fmt.Printf("read data failed,err:%v\n", err)
			return
		}
		fmt.Printf("receive from addr:%v data:%v\n", remoteAddr, string(result[:n]))
	}
}

golang实现tcp的服务端和客户端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vIA4cqIv-1648194571605)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647858500637.png)]

tcp 服务端

func main() {
	//1、监听端口
	listener, err := net.Listen("tcp", "0.0.0.0:9090")
	if err != nil {
		fmt.Printf("listen fail, err: %v\n", err)
		return
	}

	//2.建立套接字连接
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Printf("accept fail, err: %v\n", err)
			continue
		}

		//3. 创建处理协程
		go process(conn)
	}	
}

func process(conn net.Conn) {
	defer conn.Close()	//思考题:这里不填写会有啥问题?
	for {
		var buf [128]byte
		n, err := conn.Read(buf[:])

		if err != nil {
			fmt.Printf("read from connect failed, err: %v\n", err)
			break
		}
		str := string(buf[:n])
            fmt.Printf(" from client, data: %v\n", str)
	}
}

tcp客户端

golang实现Http的服务端和客户端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QUVM7LpJ-1648194571605)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647960142348.png)]

http服务端


var (
	Addr = ":8000"
)

// http的服务器
func main() {
	//1.创建路由器
	mux := http.NewServeMux()
	// 2. 设置路由规则
	mux.HandleFunc("/bye", sayBye)

	// 3.创建服务器
	server := &http.Server{
		Addr:         Addr,
		WriteTimeout: time.Second * 3,
		Handler:      mux,
	}
	// 4. 监听端口并提供服务
	log.Println("starting httpServer at" + Addr)
	log.Fatal(server.ListenAndServe())
}
func sayBye(w http.ResponseWriter, r *http.Request) {
	time.Sleep(1 * time.Second)
	w.Write([]byte("bye bye,this is httpserver"))

}

http客户端

func main() {
	//1. 创建连接池
	transport := &http.Transport{
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second, // 超时时间
			KeepAlive: 30 * time.Second, //长连接时间
		}).DialContext,
		MaxIdleConns:          100,              //最大空闲连接数
		IdleConnTimeout:       90 * time.Second, // 空闲超时时间
		TLSHandshakeTimeout:   10 * time.Second, // tls握手超时时间
		ExpectContinueTimeout: 1 * time.Second,  // 100-continue 状态码超时时间
	}
	//2. 创建客户端
	client := &http.Client{
		Timeout:   30 * time.Second,
		Transport: transport,
	}
	//3.请求数据
	resp, err := client.Get("http://127.0.0.1:8000/bye")
	if err != nil {
		fmt.Println("client get url failed ", err)
		return
	}
	defer resp.Body.Close()
	//4.读取内容
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Read body failed ", err)
		return
	}
	fmt.Println(string(b))

}

Http 服务器源码解读

  • 阅读源代码的原则:先整体在局部,先看脑图在逐一分析
  • 注册路由:理解函数是一等公民以及注册原理
  • 开启服务
  • 处理连接

函数是一等公民

type HandleFunc func(http.ResponseWriter, *http.Request)

func (f HandleFunc) ServerHTTP(w http.ResponseWriter, r *http.Request) {
	f(w, r)
}

//函数是一等公民
func main() {
	hf := HandleFunc(HelloHandler)
	resp := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/", bytes.NewBuffer([]byte("test")))

	hf.ServerHTTP(resp, req)
	b, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(b))
}
func HelloHandler(res http.ResponseWriter, req *http.Request) {
	res.Write([]byte("hello youMe "))
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MjVQl5gV-1648194571606)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1648017007628.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7JakmfrM-1648194571606)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1648017357816.png)]

到此这篇关于Go微服务网关的实现的文章就介绍到这了,更多相关Go微服务网关内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Go微服务网关的实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

百亿流量微服务网关的设计与实现

本文从百亿流量交易系统微服务网关(API Gateway)的现状和面临的问题出发,阐述微服务架构与 API 网关的关系,理顺流量网关与业务网关的脉络,分享API网关知识与经验。API网关概述“计算机科学领域的任何问题都可以通过增加一个间接的
2023-06-05

go实现服务优雅关闭的示例

本文主要介绍了go实现服务优雅关闭的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-02-09

如何使用Go语言实现的api网关

小编给大家分享一下如何使用Go语言实现的api网关,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!浏览器的请求去请求目标地址,然后获得结果它再发送给浏览器。对于Go语言来说,实现转发只需要简单的一行代码即可实现,如下所示:h
2023-06-21

基于Go语言的微服务架构设计与实现

随着云计算和容器化技术的快速发展,微服务架构已经成为了构建大型分布式系统的首选架构之一。微服务架构的核心理念是将复杂的单体应用拆分成一系列小而独立的服务,通过轻量级的通信方式进行交互,从而提高系统的可伸缩性、可靠性和可维护性。而Go语言作为
基于Go语言的微服务架构设计与实现
2023-11-20

SpringCloud微服务网关Zuul的作用是什么

这篇文章主要介绍“SpringCloud微服务网关Zuul的作用是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringCloud微服务网关Zuul的作用是什么”文章能帮助大家解决问题。网关
2023-07-02

微服务网关SIA-GateWay使用指南

一、DeskTopDeskTop展示当前注册的各个网关组,点击一个网关组将选中该网关组并跳转至首页。每个网关组包括如下四项:角色值:网关应用名前缀,用于区分网关组拥有实例:网关组实例个数网关状态:网关组当前状态网关描述:网关组描述信息,可修
2023-06-05

SpringCloud微服务网关Gateway怎么创建

这篇文章主要介绍“SpringCloud微服务网关Gateway怎么创建”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringCloud微服务网关Gateway怎么创建”文章能帮助大家解决问题。
2023-07-02

Java微服务Filter过滤器集成Sentinel实现网关限流过程详解

这篇文章主要介绍了Java微服务Filter过滤器集成Sentinel实现网关限流过程,首先Sentinel规则的存储默认是存储在内存的,应用重启之后规则会丢失。因此我们通过配置中心Nacos保存规则,然后通过定时拉取Nacos数据来获取规则配置,可以做到动态实时的刷新规则
2023-02-10

微服务的理想与现实

随着云原生微服务的日益火热,很多人都开始对微服务的相关知识内容感兴趣。本篇内容,旨在扫盲(意思是小白可入),希望能对大家有帮助。如有问题,欢迎大家一起讨论,共同学习进步。01 微服务从哪里来?--- 服务架构的演进史互联网初期, 2G还是个
2023-06-03

阿里云服务器作为内网网关的实现与优势

本文将介绍如何使用阿里云服务器作为内网网关,并分析其在企业网络架构中的优势和应用场景。阿里云服务器作为内网网关是一种将外部网络与内部网络连接起来的解决方案,通过将阿里云服务器作为内网网关,可以实现内外网数据的双向传输和访问控制,提高企业的网络安全性。首先,我们来了解一下阿里云服务器作为内网网关的实现方式。阿里云提
阿里云服务器作为内网网关的实现与优势
2023-12-27

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录