Skip to content

Latest commit

 

History

History
505 lines (400 loc) · 16.9 KB

README.md

File metadata and controls

505 lines (400 loc) · 16.9 KB

part1: introduce about go web app

go

  • simple
  • efficient
  • back end system

for web app and *-as-a-server system

large-scale web app features:

  • scalable
    • vertical scaling: increasing cpu goroutine
    • horizontal scaling: increasing machines layer: add a proxy layer
  • modular easy to add/remove/modify feature easy to reusing modular components
  • maintainable
  • high performance

web app and web server

app: software program

web app:

  • return html,(client render html and display to user)
  • transport data by http

web service:

  • not return html
  • server for other program

http

app-level communication protocol

  • stateless
  • text-based
  • request-response
  • c/s

request

a request consists of a few line:

  • request-line
  • zero or more request headers
  • an empty line
  • the message body (optional)

GET /Protocols/rfc2616/rfc2616.html HTTP/1.1 Host: www.w3.org User-Agent: Mozilla/5.0 (empty line) abc

request-line = request method + uri + http version request headers = a:b c:d

request method

  • get: Tells the server to return the specified resource.
  • head: The same as GET except that the server must not return a message body.
  • post: Tells the server that the data in the message body should be passed to the resource identified by the URI .
  • put: Tells the server that the data in the message body should be the resource at the given URI .
  • delete: Tells the server to remove the resource identified by the URI .
  • trace: Tells the server to return the request
  • options: Tells the server to return a list of HTTP methods that the server supports.
  • connect: Tells the server to set up a network connection with the client.
  • patch: Tells the server that the data in the message body modifies the resource identified by the URI .

idempotent
the result of 1 call = the result of 1000 times call

request_header

request header consists:

  • info of request
  • info of client

if request have a message, Content-Length and Transfer-Encoding is need

common http request headers:

  • Accept: Content types that are acceptable by the client as part of the HTTP response.
  • Accept-Charset: The character sets required from the server.
  • Authorization: This is used to send Basic Authentication credentials to the server.
  • Cookie: The client should send back cookies that were set by the calling server.
  • Content-Length: The length of the request body in octets.
  • Content-Type: The content type of the request body.
  • Host: The name of the server, along with the port number.
  • Referrer: The address of the previous page that linked to the requested page.
  • User-Agent: Describes the calling client.

response

a response consists of a few line:

  • a status line
  • zero ro more response headers
  • an empty line
  • the message body (optional)

status line = status code + reason phrase

status_code

  • 1xx: Informational. This tells the client that the server has already received the request and is processing it.
  • 2xx: Success. This is what clients want; the server has received the request and has processed it successfully. The standard response in this class is 200 OK.
  • 3xx: Redirection. This tells the client that the request is received and processed but the client needs to do more to complete the action.
  • 4xx: Client Error. This tells the client that there’s something wrong with the request.
  • 5xx: Server Error. This tells the client that there’s something wrong with the request but it’s the server’s fault.

response_header

common http response headers:

  • Allow: Tells the client which request methods are supported by the server.
  • Content-Length: The length of the response body in octets
  • Content-Type: The content type of the response body
  • Date: Tells the current time
  • Location: This header is used with redirection, to tell the client where to request the next URL.
  • Server: Domain name of the server that’s returning the response.
  • Set-Cookie: Sets a cookie at the client.
  • WWW-Authenticate: Tells header the client what type of authorization clients should supply in their Authorization request header.

  • rui: name of resource
  • rul: location of resource

RUI form:
< scheme name> : < hierarchical part> [ ? < query> ][ # < fragment> ]"

http://user:[email protected]/doc/file?name=test&ip=123#sum

http2

  • focuse on performance
  • based on spdy/2
  • binary protocol (http1.x base on text)
  • full multiplexed
  • compress the header
  • allow server to push response to client

in go1.6 and after, http2 is default

define web app

  • get a http request form client
  • process the requet, do some work
  • generate html and return it in an http response message

web app = handlers + template engine

handlers

  • receives and process the http request

  • call the template engine to generate the html and something about response

    mvc(model-view-controller pattern) divide a arogram into three parts: model, view, controller model - underlaying data view - visualization of the model for the user controller - use input(from user) to modify the model model change --> view update automatically

mvc is a good way for web app, but is not the only way

template engine

come form ssi technology

there are two type of template with different design philosophies:

  • static template or logic-less template use placeholder tokens no logic
  • active template placehoder tokens + other programming language eg: jsp asp erb

example - forums


part2: details in go web program

handling requests

货物崇拜编程:在不理解需求痛点的情况下,复制一份可运行的代码,
对代码也不很了解,最后导致扩展很困难。
换句话说就是使用了不理解的解决方案,导致无法明确预期。
在编程中,可能是使用了一个强大的框架,但不知道正确的使用规则。

为什么client要持久化cookie,server要持久化session信息:
因为http是无状态协议,而且每次请求,不会带上上次请求的相关信息

web app的框架首推标准库中的net/htpp + html/template,其次是其他三方库, 为了避免货物崇拜编程,需要了解一下标准库中的一些规则(就像上面的cookie和session)

  func ListenAndServe(addr string, handler Handler) error
  // addr 网络地址,空字符串表示使用80端口
  // handler 为nil,默认处理器(handler)就是默认复用器,DefaultServeMux

  http.ListenAndServe(":80", nil);  // 这个server未做配置
  server := http.Server{Addr: ":80", Handler: nil,}
  server.ListenAndServe()  // 这个是通过Server结构体来配置server

监听tcp端口,处理request,

在web app中(golang中),handler和handler function并不是一个, handler是一个接口:

    type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
    }

任何实现这个这个方法的,都可以被称为一个handler

接下来看看DefaultServeMux:

// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}
// 用两个参数去初始化一个Server结构体,然后调用其中的方法

type Server struct {
    Addr    string  // TCP address to listen on, ":http" if empty
    Handler Handler // handler to invoke, http.DefaultServeMux if nil
    ...
}
// 如果Handler未指定,就默认取http.DefaultServeMux

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux
// 结构是ServeMux

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
// 绕了一圈,DefaultServeMux是实现了Handler接口的

这个默认Handler的来历是理顺了,那她的作用是:
根据不同的rul,将请求丢给不同的handler,说白了就是一个默认的复用器。

为什么需要一个复用器?
如果不用复用器,请求全都会被一个handler处理, 显然从设计上讲,分层是必须的,至少扩展是非常方便的

尽量避免一个handler处理全部的request

    http.Handle("/hello", &hello)

用这个来指定对固定uri的处理,除了/是匹配所有,其他的都是完全匹配, 有一点差别都会报404, /hello/ 多了一个/也是404, 为啥设计时不考虑将/hello 和/hello/兼容呢? 最小惊讶原则

再总结一下Handler:

  • 是一个接口
  • 有一个方法来实现这个接口
  • http.Handle(uri, &handler)来指定如何处理

什么是handler function:

  • 和handler的功能类似
  • 不是方法,不是接口,只是一个函数,参数和ServeHTTP一致
  • http.HandleFunc(uri, handler function)来指定如何处理
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
// 这个直接是使用了DefaultServeMux默认复用器

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}
// 实际上调用了ServeMux的方法
// 这个HandlerFunc,是一个函数类似,T(a)这种写法是类型转换
// 用处是将handler function 转换成Handler

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

	if pattern == "" {
		panic("http: invalid pattern")
	}
	if handler == nil {
		panic("http: nil handler")
	}
	if _, exist := mux.m[pattern]; exist {
		panic("http: multiple registrations for " + pattern)
	}

	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e
	if pattern[len(pattern)-1] == '/' {
		mux.es = appendSorted(mux.es, e)
	}

	if pattern[0] != '/' {
		mux.hosts = true
	}
}
// 这个就是将uri和handler成对保存起来
// 说白了就是一个handler注册过程,后面遇到对应的uri,调用不同的handler来处理

handler 和 handler function, 一个是接口,一个是函数, handler = HandleFunc(handler function),可使用类型转换直接转换

为什么要提供功能相同,只是写法上有所区别的两种概念:
实际上使用的还是handler,但写法上,handler function简单很多,所以这就是原因

反过来,既然handler function很方便,为啥还要暴露出handler概念:
设计上的需求,可以提高模块化(实际上是为了更好的兼容性)

go 不是一个函数性的语言,但函数形语言的一些基本特征还是包括的:
函数类型,匿名函数, 闭包。

有个新的概念叫aop 和oop可以互补,aop 面向切面编程,属于设计模式的延伸, 其中有个概念叫cross-cutting concern,叫横切关注点, 映射到go中,就是日志handler,安全handler,等很多独立的功能handler, 只关注自己业务上的事,至于和其他业务模块联合起来,就是aop中的织网, 在go语言中,织网可以用chain(链式handler)来实现。

链式
假定我们的模块都已经做好了(各个功能性的handler已经完成,且高内聚低耦合),
下一步就是织网,在c++中,要考虑的是各个模块调用的参数问题,
在go中使用统一的格式即可:
func xxx(h http.HandlerFunc) http.HandlerFunc {}
这样织网时就不需要考虑模块之间参数的问题,
A(B(C)), A(C) 按业务进行组合即可

链式的handler,可以非常长,也被称为 pipeline processing

    func xxx(h http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            ...  // 做模块自己的事
            h(w, r)
        }
    }
    // 最后一个链式节点,使用方法(实现Handler的)

链式的handler和链式的handler function是一样的

除了默认的复用器,也有第三方的, ServeMux用来处理固定的uri是很合适的,处理动态uri就力有不怠

http2 需要使用https

process request

前面提到过:
RUI form:
< scheme name> : < hierarchical part> [ ? < query> ][ # < fragment> ]"

http://user:[email protected]/doc/file?name=test&ip=123#sum

RUL form:
scheme://[userinfo@]host/path[?query][#fragment]
eg: http://www.example.com/post?id=123&thread_id=456

query是k-v键值对,fragment在浏览器发送请求之前就被丢弃了,所以服务端不考虑这个, 但是非浏览器工具发送请求时,有可能有这个fragment,就需要做特殊处理了

http头,在库中用Header类型来表示

    type Header map[string][]string  // 是一个map类型

    // 主要4个基本方法,用于增删改查
    func (h Header) Add(key, value string)
    func (h Header) Del(key string)
    func (h Header) Get(key string) string
    func (h Header) Set(key, value string)

    // Add和Set的区别
    // Header的key是string, value是[]string 切片
    // Set是先创建一个空白切片,切片的第一个值就是value
    // Add是在切面后面追加

http消息体,用 Body io.ReadCloser来表示

    Body io.ReadCloser

    type ReadCloser interface {
      Reader
      Closer
    }

    type Reader interface {
        Read(p []byte) (n int, err error)
    }

    type Closer interface {
        Close() error
    }

消息体是一个接口,实际上用两个接口来表示:

  • Reader:用于读body
  • Close:

http GET 请求不带消息体,POST才带,浏览器地址栏的请求都是GET请求, 如果要发post请求,只能用其他工具

表单

http post一般会带一个表单,也就是form, 而request中message的组织有多种方式,都可由请求之前指定:

  • 简单的文本,使用url编码格式
  • 大数据(eg:file),使用multipart- MIME格式
  • 二进制,使用base64编码格式

http get是没有消息体,也就是没有body的,她的参数都是加在url后面的

new book

new lib

everyday

debug

net

blog

package(pkg)

标准库关系

未来 TODO

需要奋力追赶的方向,也是欠下的技术债

  • 规范 git流 github流 k8s流等,提升沟通效率,是主流社区交流的语言,也是对开发测试部署/项目工程管理的一些共同认识
  • 效能工具 高效能开发的基础,将目光集中在创新(coding),提高自己和团队的效率,包括源码管理,ci/cd
  • 技能 包括基础的语言和开发包,核心价值的基础,目前主要方向是go语言
  • 开发部署套件 和效能工具有所区别,效能工具侧重点在使用,而这里主要关注工具的深度学习,eg:docker git k8s vim等
  • 方向 包含的东西很多:架构(包括设计和系统部署) 微服务 性能架构
  • 团队管理 30岁的年纪,20岁的情商
  • CNCF