Golang中基于HTTP协议的网络服务

目录
  • 一、HTTP协议的网络服务
    • 1.1 使用http.Get函数访问HTTP协议的网络服务
    • 1.2 使用缺省客户端DefaultClient(类型为*http.Client )
    • 1.3 使用http.Client访问HTTP协议的网络服务
  • 二、http.Client中的Transport字段
    • (1)http.Transport类型中的DialContext字段
    • (2)http.Transport类型中的其它字段
  • 三、为什么会出现空闲的连接
    • 3.1 空闲连接的产生
    • 3.2 杜绝空闲连接的产生
  • 四、http.Server
    • 4.1 http.Server类型的ListenAndServe方法
    • 4.2 ListenAndServe方法主要做的事情
    • 4.3 (衍生问题)net.Listen 函数都做了哪些事情
    • 4.4 (衍生问题)http.Server类型的Serve方法是怎么接受和处理HTTP请求的
  • 五、思考:怎么优雅地停止基于HTTP协议的网络服务程序?

一、HTTP协议的网络服务

HTTP协议是基于TCP/IP协议栈的,并且它也是一个面向普通文本的协议。

只要搞清楚了HTTP请求的报文(报文的头部(header)和主体(body))应该包含的内容,使用任何一个文本编译器,就饿可以编写一个完整的HTTP请求报文。

在这种情况下,直接使用net.Dial函数,就可以。

使用net/http代码包中的程序实体,可以更便捷的访问基于HTTP协议的网络服务。其中最便捷的是使用http.Get函数。

1.1 使用http.Get函数访问HTTP协议的网络服务

package main

import (
	"fmt"
	"net/http"
)

func main() {
	url1 := "http://www.google.cn/"
	fmt.Printf("Send request to %q with method GET ... \n", url1)
	response1, err := http.Get(url1)
	if err != nil {
		fmt.Printf("request sending error: %v\n", err)
	}
	defer response1.Body.Close()
	line1 := response1.Proto + " " + response1.Status
	fmt.Printf("The first line of response: \n %s \n", line1)
}

http.Get函数会返回两个结果值:

  • 第一个结果值的类型是*http.Response,它是网络服务给我们传回来的响应内容的结构化表示。
  • 第二个结果值是error类型。它代表了在创建和发送HTTP请求,以及接受和解析HTTP响应的过程中可能发生的错误。

http.Get函数内部会使用缺省的HTTP客户端,并调用它的Get方法以完成功能。缺省客户端类型是*http.Client,由公开变量DefaultClient代表。

1.2 使用缺省客户端DefaultClient(类型为*http.Client )

package main

import (
	"fmt"
	"net/http"
)

func main() {
	url1 := "http://www.google.cn/"
	fmt.Printf("Send request to %q with method GET ... \n", url1)
	// response1, err := http.Get(url1)
	response1, err := http.DefaultClient.Get(url1)
	if err != nil {
		fmt.Printf("request sending error: %v\n", err)
	}
	defer response1.Body.Close()
	line1 := response1.Proto + " " + response1.Status
	fmt.Printf("The first line of response: \n %s \n", line1)
}

它的基本类型(http.Client)可以开箱即用。

1.3 使用http.Client访问HTTP协议的网络服务

package main

import (
	"fmt"
	"net/http"
)

func main() {
	url1 := "http://www.google.cn/"
	fmt.Printf("Send request to %q with method GET ... \n", url1)
	// response1, err := http.Get(url1)
	// response1, err := http.DefaultClient.Get(url1)
	var oneClient http.Client
	response1, err := oneClient.Get(url1)
	if err != nil {
		fmt.Printf("request sending error: %v\n", err)
	}
	defer response1.Body.Close()
	line1 := response1.Proto + " " + response1.Status
	fmt.Printf("The first line of response: \n %s \n", line1)
}

http.Client是一个结构体类型,并且它包含的字段是公开的。之所以该类型的零值仍然可以使用,是因为它的这些字段要么存在着响应的缺省值,要么其零值直接可以使用,且代表着特定的含义。

二、http.Client中的Transport字段

http.Client类型中的Transport字段代表着:向网络服务发送HTTP请求,并从网络服务接收HTTP响应的操作过程。

Transport字段的RoundTrip方法实现单次HTTP事务(或者说基于HTTP协议的单词交互)需要的所有步骤。

Transport 字段是http.RoundTrip接口类型,它有一个缺省值,这个缺省值的变量名为DefaultTransport。DefaultTransport的实际类型为*http.Transport*http.Transport可以被复用,并且是线程安全的。

如果没有显式的为http.Client中的Transport字段赋值,这个Client就会直接使DefaultTransport。

http.Client中的Timeout字段,代表前面所说的单词HTTP事务的超时时间,它time.Duration类型,它的零值是可用的,用于表示没有设置超时时间。

(1)http.Transport类型中的DialContext字段

http.Transport类型,在内部使用一个net.Dialer类型的值,并且会把该值的Timeout字段的值,设定为30秒。

也就是说,这个Dialer值如果在30秒内还没有建立好网络连接,那么就会被判定为操作超时。

在DefaultTransport的值被初始化的时候,这样的Dialer值的DialContext方法会被赋给前者DialContext字段:

var DefaultTransport RoundTripper = &Transport{
	Proxy: ProxyFromEnvironment,
	DialContext: defaultTransportDialContext(&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}),
	ForceAttemptHTTP2:     true,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

func defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) {
	return dialer.DialContext
}

KeepAlive的背后是一种针对网络连接(更确切地说,是TCP连接)的存活探测机制。它的值用于表示每隔多长时间发送一次探测包。当该值不大于0时,则表示不开启这种机制。

DefaultTransport会把这个字段的值设定为30秒。

(2)http.Transport类型中的其它字段

一些是关于超时操作

  • IdleConnTimeout:含义是空闲的连接在多久之后就应该关闭。

DefaultTransport 会把该字段的值设定为90秒。

如果该值为0,那么就表示不关闭空闲连接。注意,这样可能会造成资源的泄露。

  • ResponseHeaderTimeout:含义是,从客户端把请求完全递交给操作系统到从操作系统那里接收到响应报文头到最长时长。

DefaultTransport并没有设定该字段的值。

  • ExpectContinueTimeout:含义是,在客户端提交了请求报文头之后,等待接收第一个响应报文头的最长时间。

DefaultTransport 把该字段的值设定为1秒。

在客户端想要使用HTTP的“POST”方法把一个很大的报文体发送给服务端的时候,它可以先通过发送一个包含了“Expect: 100-continue”的请求报文头,来询问服务端是否愿意接受这个大报文体。这个字段就是用于设定在这种情况下的超时时间的。

注意,如果该字段的值不大于0,那么无论多大的请求报文体都将会被立即发送出去。

TLSHandshakeTimeout:TLS是Transport Layer Security 的缩写,可以被翻译为传输层安全。这个字段代表了基于TLS协议的连接在被建立时的握手阶段的超时时间。

DefaultTransport 把该字段的值设置为10秒。

若该值为0,则表示对这个值不设限。

一些与IdleConnTimeout相关的字段值

  • MaxIdleConns:用于控制访问所有主机的最大空闲连接。如果为0,不做限制。

DefaultTransport 把MaxIdleConns设定为100。

MaxIdleConns字段只会对空闲连接的总数做出限定。

  • MaxIdleConnsPerHost: 控制Transport值访问每一个网络服务的最大空闲连接数。如果为0,将使用缺省值2, 这个缺省值由DefaultMaxIdleConnsPerHost所代表。

也就是说,默认情况下,对于某一个Transport值访问的每一个网络服务,它的空闲连接数都最多只能由两个。

  • MaxConnsPerHost:针对某一个Transport值访问的每一个网络服务的最大连接数,不论这些连接是否是空闲的。

该字段没有缺省值,零值表示不限定。

MaxIdleConns和MaxIdleConnsPerHost两个与空闲连接数有关的字段的值应该是联动的,所以,有时需要根据实际情况定制它们,可以参考DefaultTransport变量的声明。

三、为什么会出现空闲的连接

3.1 空闲连接的产生

HTTP协议有一个请求报文头,叫做“Connection”。在HTTP协议的1.1 版本中,这个报文头的值默认是“keep-alive”。

在这种情况下,网络连接都是持久连接,它们会在当前的HTTP事务完成后仍然保持着连通性,因此是可以被复用的。

连接的可复用,带来两种可能:

  • 一种可能是,针对同一个网络服务,有新的HTTP请求被提交,该连接被再次使用。
  • 另一种可能是,不再有对该网络服务的HTTP请求,该连接被闲置。(产生空闲的连接)

后一种情况就产生了空闲连接。另外,如果分配给某一个网络服务的连接过多的话,也可能会导致空闲连接的产生。因为每一个新递交的HTTP请求,都只会征用一个空闲的连接。所以,为空闲连接设定限制,在大多数情况下都是很有必要的,也是需要斟酌的。

3.2 杜绝空闲连接的产生

如果想彻底杜绝空闲连接的产生,那么可以在初始化的时候,把它的DisableKeepAlives字段的值设定为true。这时,HTTP请求的“Connection”报文头的值就会被设置为“close”。这会告诉网络服务,这个网络连接不必保持,当前的HTTP事务完成后就可以断开它。

如此一来,每当一个HTTP请求被递交时,就会产生一个新的网络连接。这样做会明显地加重网络服务以及客户端的负载。所以,在一般情况下,我们都不要去设置这个DisableKeepAlive字段。

在net.Dialer类型中,也有一个看起来很相似的字段KeepAlive。不过,它与前面所说的HTTP 持久连接不是一个概念,KeepAlive是直接作用在底层的socket上的。

KeepAlive的背后是一种针对网络连接(更确切地说,是TCP连接)的存活探测机制。它的值用于表示每隔多长时间发送一次探测包。当该值不大于0时,则表示不开启这种机制。DefaultTransport会把这个字段的值设定为30秒。

四、http.Server

http.Server类型与http.Client相对应。http.Server代表的是基于HTTP协议的服务端,或者网络服务。

4.1 http.Server类型的ListenAndServe方法

http.Server类型的ListenAndServe方法的功能是:监听一个基于TCP协议的网络地址,并对接收到的HTTP请求进行处理。

  • 这个方法默认会开启针对网络连接的存活探测机制,以保证连接是持久的。
  • 同时,该方法会一直执行,直到有严重的错误发生或被外界关掉。

当被外界关掉时,它会返回一个由http.ErrServerClosed变量代表的错误值。

4.2 ListenAndServe方法主要做的事情

func (srv *Server) ListenAndServe() error {
	if srv.shuttingDown() {
		return ErrServerClosed
	}
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(ln)
}

ListenAndServe方法主要会做下面的事情:

  • 检查当前的http.Server类型的值的Addr字段。

该字段的值代表了当前的网络服务需要使用的网络地址。即:IP地址和端口号。如果这个字段的值为空字符串,那么就用":http"代替。

也就是说,使用任何可以代表本机的域名和IP地址,并且端口号为80.

  • 通过调用net.Listen函数在已确定的网络地址上启动基于TCP协议的监听。
  • 检查net.Listen 函数返回的错误值。

如果该错误值不为nil,那么就直接返回该值。否则,通过调用当前值的Serve方法准备接受和处理将要到来的HTTP请求。

4.3 (衍生问题)net.Listen 函数都做了哪些事情

net.Listen函数做的事情:

  • 解析参数值中包含的网络地址隐含的IP地址和端口号;
  • 根据给定的网络协议,确定监听的方法,并开始进行监听;

这里还可以延伸到net.socket函数,以及socket相关的知识。

4.4 (衍生问题)http.Server类型的Serve方法是怎么接受和处理HTTP请求的

在一个for循环中,网络监听的Accept方法会被不断的调用,

	for {
		rw, err := l.Accept()
  }

该方法会返回两个结果值:

  • 第一个结果值是net.Conn 类型,代表包含了新到来的HTTP请求的网络连接;
  • 第二个结果值是error类型值,代表可能发生的错误。

如果错误不为nil,除非它代表了一个暂时性的错误,否则循环都会被终止。如果是暂时性的错误,那么循环的下一次迭代将会在一段时间之后开始执行。

如果这里的Accept方法没有返回非nil的错误值,那么这里的程序将会把它的第一个结果值包装成一个*http.conn类型的值,然后通过在新的goroutine中调用这个*http.conn 类型值的serve方法,来对当前的HTTP请求进行处理。

HTTP请求相关的,更多的衍生问题:

  • 这个*http.conn类型值的状态有几种,分别代表着处理的哪个阶段?
  • 处理的过程中会用到哪些读取器和写入器,它们的作用分别是什么?
  • 这里的程序是怎么调用我们自定义的处理函数的?

五、思考:怎么优雅地停止基于HTTP协议的网络服务程序?

srv.Shutdown(context.Background()) 的方式停止服务,通过RegisterOnShutdown可添加服务停止时的调用。

以上就是Golang中基于HTTP协议的网络服务的详细内容,更多关于Golang HTTP协议的资料请关注我们其它相关文章!

(0)

相关推荐

  • golang语言http协议get拼接参数操作

    我就废话不多说了,大家还是直接看代码吧~ package main import ( "fmt" "net/url" ) // Manage the HTTP GET request parameters type GetRequest struct { urls url.Values } // Initializer func (p *GetRequest) Init() *GetRequest { p.urls = url.Values{} return p }

  • golang简单tls协议用法完整示例

    本文实例讲述了golang简单tls协议用法.分享给大家供大家参考,具体如下: 生成私钥: openssl genrsa -out key.pem 2048 生成证书: openssl req -new -x509 -key key.pem -out cert.pem -days 3650 https: 复制代码 代码如下: package main import (     "io"     "net/http"     "log" ) fun

  • Golang gRPC HTTP协议转换示例

    gRPC HTTP协议转换 正当有这个需求的时候,就看到了这个实现姿势.源自coreos的一篇博客,转载到了grpc官方博客gRPC with REST and Open APIs. etcd3改用grpc后为了兼容原来的api,同时要提供http/json方式的API,为了满足这个需求,要么开发两套API,要么实现一种转换机制,他们选择了后者,而我们选择跟随他们的脚步. 他们实现了一个协议转换的网关,对应github上的项目grpc-gateway,这个网关负责接收客户端请求,然后决定直接转发

  • golang实现简单的udp协议服务端与客户端示例

    本文实例讲述了golang实现简单的udp协议服务端与客户端.分享给大家供大家参考,具体如下: 其实udp没有什么服务端和客户端的概念了,只是一个发一个收而已,只是这样较方便能识别和理解. 服务端: 复制代码 代码如下: package main import (     "fmt"     "net" ) func main() {     // 创建监听     socket, err := net.ListenUDP("udp4", &am

  • Golang中基于HTTP协议的网络服务

    目录 一.HTTP协议的网络服务 1.1 使用http.Get函数访问HTTP协议的网络服务 1.2 使用缺省客户端DefaultClient(类型为*http.Client ) 1.3 使用http.Client访问HTTP协议的网络服务 二.http.Client中的Transport字段 (1)http.Transport类型中的DialContext字段 (2)http.Transport类型中的其它字段 三.为什么会出现空闲的连接 3.1 空闲连接的产生 3.2 杜绝空闲连接的产生 四

  • 在VMware+centOS 8上基于http协议搭建Git服务的方法

    一.起因 一定要看 本文最终目的是实现Android终端访问虚拟机中git服务,所以需要搭建http协议的git服务器,而如何搭建http协议的git服务器,前人之述备矣,笔者遂借鉴前人之作这里 二.设备信息 windows10家庭中文版(1903) VMware 15Pro(15.5.0 build-14665864) centOS 8(1905已关闭GUI,VMware采用NAT模式) 三.准备工作 (一)windows防火墙开放80端口控制面板 -> 系统和安全 -> Windows D

  • 对python中基于tcp协议的通信(数据传输)实例讲解

    阅读目录 tcp协议:流式协议(以数据流的形式通信传输).安全协议(收发信息都需收到确认信息才能完成收发,是一种双向通道的通信) tcp协议在OSI七层协议中属于传输层,它上承用户层的数据收发,下启网络层.数据链路层.物理层.可以说很多安全数据的传输通信都是基于tcp协议进行的. 为了让tcp通信更加方便需要引入一个socket模块(将网络层.数据链路层.物理层封装的模块),我们只要调用模块中的相关接口就能实现传输层下面的繁琐操作. 简单的tcp协议通信模板:(需要一个服务端和一个客户端) 服务

  • Java基于TCP协议socket网络编程的文件传送的实现

    先了解一下socket基本概念 socket也叫套接字: 是指在网路中不同主机上的应用进程之间,进行双向通信的端点的抽象. 简单理解就是: 两个主机之间要通信,就需要知道彼此的ip,端口号等信息,而一台主机这些信息的集合: 就可以理解为一个端点,即为套接字 双方通过套接字作为一种坐标,建立信息通道,形成连接(两点连接一条直线) 简单理解了套接字的概念后,来看看如何通过java socket编程来实现 两台主机文件的接收与发送: 代码如下: 发送方: import java.io.*; impor

  • 基于epoll的多线程网络服务程序设计

    基于epoll的多线程网络服务程序设计——C语言​ 采用C语言设计了一个基于epoll的多线程网络服务程序.每个线程都有一个epoll来捕获处于这个线程的socket事件.当子线程数量为0,即只有一个线程,则网络监听服务与socket消息处理处于同一个epoll.当子线程数量大于0时,主线程监听socket连接,当有新的连接到来时将其加入到活跃socket数量最小的子线程的epoll中. server.h #ifndef EPOLL_C_SERVER_H #define EPOLL_C_SERV

  • Android中基于XMPP协议实现IM聊天程序与多人聊天室

    简单的IM聊天程序 由于项目需要做一个基于XMPP协议的Android通讯软件.故开始研究XMPP. XMPP协议采用的是客户端-服务器架构,所有从一个客户端发到另一个客户端的消息和数据都必须经过XMPP服务器转发,而且支持服务器间DNS的路由,也就是说可以构建服务器集群,使不同的 服务器下的客户端也可以通信,XMPP的前身是一个开源组织制定的网络通信协议--Jabber,XMPP的核心是在网络上分片段发送XML流的协议,这个协议是XMPP的即时通讯指令的传递手段.       为了防止服务器间

  • golang中使用proto3协议导致的空值字段不显示的问题处理方案

    最近在使用grpc协议的时候,由于采用的是Proto3协议,在查找记录信息的时候,由于某些字段会有默认空值,导致在通过协议调用后,返回的json结构中并没有这些字段,虽然作为前端使用没有太大的问题,但是在更多的使用场景中,我们更需要知道该服务返回的确切字段,以便于能够做相应处理,尤其是编译型语言 具体的使用出现场景如下 type MemberResponse struct { Id int32 `json "id"` Phone string `json "phone&quo

  • python爬取基于m3u8协议的ts文件并合并

    前言 简单学习过网络爬虫,只是之前都是照着书上做并发,大概能理解,却还是无法自己用到自己项目中,这里自己研究实现一个网页嗅探HTML5播放控件中基于m3u8协议ts格式视频资源的项目,并未考虑过复杂情况,毕竟只是练练手. 源码 # coding=utf-8 import asyncio import multiprocessing import os import re import time from math import floor from multiprocessing import

  • 使用php来实现网络服务

    作者:samisa 以下文中的翻译名称对照表 : payload: 交谈内容 object: 实例 function: 函数 使用 php来实现网络服务 使用框架: WSO2 WSF/PHP 安装环境: windows 或者 linux (厌恶于眼下计算机文章夹杂无数难懂的翻译以及术语,此处尽量使用口语以及汉语.) WSMessages 类: 在调用网络服务的过程中,需要两个消息,发送的消息和接受的消息,又来有往方能来往不是. WSMessages 这个类就是在 Web services fra

  • Android基于TCP和URL协议的网络编程示例【附demo源码下载】

    本文实例讲述了Android基于TCP和URL协议的网络编程.分享给大家供大家参考,具体如下: 手机本身是作为手机终端使用的,因此它的计算能力,存储能力都是有限的.它的主要优势是携带方便,可以随时打开,而且手机通常总是处于联网状态.因此网络支持对于手机应用非常重要. Android完全支持JDK本身的TCP,UDP网络通信API,也可以使用ServerSocket,Socket来建立基于TCP/IP协议的网络通信,也可以使用DatagramSocket,Datagrampacket来建立基于UD

随机推荐