欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Service-computing-hw8 《Golang web 应用开发》阅读

程序员文章站 2022-06-20 14:26:04
...

这周的作业是阅读《Golang web 应用开发》,Golang web应用开发有很多相关内容,表单、访问数据库、web服务、安全加密、设计web框架等等,本文就其中的一个重点,web基础,用go搭建一个简单的web服务来谈谈自己学习到的知识和理解

1.1 http协议,web的运行原理

​ 想必学习过计算机网络的同学都会清楚web也就是浏览器的工作方式,浏览网页的时候,会打开浏览器,输入网址后按下回车键,然后就会显示出你想要浏览的内容

​ 这其中蕴含的过程简单回忆一下:输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP,然后通过IP地址找到IP对应的服务器后,要求建立TCP连接,等浏览器发送完HTTP Request(请求)包后,服务器接收到请求包之后才开始处理请求包,服务器调用自身服务,返回HTTP Response(响应)包;客户端收到来自服务器的响应后开始渲染这个Response包里的主体(body),等收到全部的内容随后断开与该服务器之间的TCP连接

​ 其中URL解析和DNS解析,URL按照规定的格式进行解析,集中式域名解析和分布式域名解析,一种是一个一个访问,一种是深度迭代访问DNS服务器,这些都不是重点

​ 当今网络的请求和响应数据传输,都是遵循最常用的http协议,http协议也是web工作的核心,HTTP是一种让Web服务器与浏览器(客户端)通过Internet发送与接收数据的协议,它建立在TCP协议之上,一般采用TCP的80端口。它是一个请求、响应协议–客户端发出一个请求,服务器响应这个请求。在HTTP中,客户端总是通过建立一个连接与发送一个HTTP请求来发起一个事务。服务器不能主动去与客户端联系,也不能给客户端发出一个回调连接。客户端与服务器端都可以提前中断一个连接。例如,当浏览器下载一个文件时,你可以通过点击“停止”键来中断文件的下载,关闭与服务器的HTTP连接。HTTP协议是无状态的。

1.2 如何用go搭建一个web服务器

​ 我们从最基础最简单的那种开始学起,Go语言里面提供了一个完善的net/http包,学会使用这个包就可以搭建起一个基础可运行的web服务器,同时可以对Web的路由,静态文件,模版,cookie等数据进行设置和操作。

package main

import (
	"fmt"
	"net/http"
	"strings"
	"log"
)

func sayhelloName(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()  //解析参数,默认是不会解析的
	fmt.Println(r.Form)  //这些信息是输出到服务器端的打印信息
	fmt.Println("path", r.URL.Path)
	fmt.Println("scheme", r.URL.Scheme)
	fmt.Println(r.Form["url_long"])
	for k, v := range r.Form {
		fmt.Println("key:", k)
		fmt.Println("val:", strings.Join(v, ""))
	}
	fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
}

func main() {
	http.HandleFunc("/", sayhelloName) //设置访问的路由
	err := http.ListenAndServe(":9090", nil) //设置监听的端口
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

build代码后,执行web.exe(已经在9090端口监听http链接请求),在浏览器输入http://localhost:9090查看,Hello astaxie!浏览器的页面输出,通过http的两个函数就可以写一个简单的web服务器了

1.3 接下来介绍底层原理,go如何让web服务器正常工作起来

​ 先来看服务器的几个概念,Request:是用户请求的信息 Response:服务器反馈给客户端的信息 Conn:用户的每次请求链接 Handler:处理请求和生成返回信息的方法

​ 先来看http包运行机制

Service-computing-hw8 《Golang web 应用开发》阅读

  1. 创建Listen Socket, 监听指定的端口, 等待客户端请求到来。
  2. Listen Socket接受客户端的请求, 得到Client Socket, 接下来通过Client Socket与客户端通信。
  3. 处理客户端的请求, 首先从Client Socket读取HTTP请求的协议头, 如果是POST方法, 还可能要读取客户端提交的数据, 然后交给相应的handler处理请求, handler处理完毕准备好客户端需要的数据, 通过Client Socket写给客户端。

​ Go是通过一个函数ListenAndServe来处理监听端口、接收客户端请求和分配handler这三件事情的,这个底层其实这样处理的:初始化一个server对象,然后调用了net.Listen("tcp", addr),也就是底层用TCP协议搭建了一个服务,然后监控我们设置的端口。内部源码摘录如下


func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		rw, e := l.Accept()
		if e != nil {
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return e
		}
		tempDelay = 0
		c, err := srv.newConn(rw)
		if err != nil {
			continue
		}
		go c.serve()
	}
}

我们仔细分析分析上面的代码

  • 监控之后如何接收客户端的请求呢?

    上面代码执行监控端口之调用srv.Serve(net.Listener),这个函数就是用来处理用户的请求信息的,函数里有个for{},首先通过Listener接受请求,然后创建一个Conn,最后单独开一个goroutine,把请求的参数扔给Conn服务,go c.serve(),go的高并发也是体现在这里,go的每一次请求都是一个新的goroutine服务

  • 如何分配到具体的函数来处理请求呢?

    conn首先会解析request:c.readRequest(),然后获取相应的handler:handler := c.server.Handler,也就是调用函数ListenAndServe时候的第二个参数,

    我们前面例子传递的是nil时,默认获取handler = DefaultServeMux,这个变量的作用是一个路由器,它用来匹配url跳转到其相应的handle函数,我们调用的代码里面第一句调用了http.HandleFunc("/", sayhelloName)这里就是有过这种设置。这个作用就是注册了请求/的路由规则,当请求uri为"/",路由就会转到函数sayhelloName,DefaultServeMux会调用ServeHTTP方法,这个方法内部其实就是调用sayhelloName本身,最后通过写入response的信息反馈到客户端。

整个函数的运行流程,具体每一块的作用如下图

Service-computing-hw8 《Golang web 应用开发》阅读
问题解答后,我们这样就大概知道go如何让web跑起来,至少是如何处理接受和处理请求信息的