精华内容
下载资源
问答
  • 零点服务标准流程

    2020-12-20 15:22:01
    酒店管理一直遵循着为客户服务的准则,相信你也需要了解一下零点服务标准流程,赶快来下载零点服务标准流...该文档为零点服务标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 这是一款整理发布的商务中心服务标准流程,需要了解相关商务中心服务标准流程的你一定不容错过...该文档为商务中心服务标准流程,是一份很不错的参考资料,具有较高参考价值,感兴趣的可以下载看看
  • 酒店管理一直遵循着为客户服务的准则,相信你也需要了解一下中餐点菜服务标准流程,赶快来下载中餐点菜服...该文档为中餐点菜服务标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 接机服务标准流程

    2020-12-20 17:37:58
    随着全球经济快速发展,酒店行业也跟着崛起了,在这里,为大家带来了接机服务标准流程,欢迎大...该文档为接机服务标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 这一份入店行李服务标准流程适合大家用于学习、参考、借鉴,希望入店行李服务标准流程就是你所需要东西...该文档为入店行李服务标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 酒店管理一直遵循着为客户服务准则,相信你也需要了解一下酒吧管理——服务流程与工作标准,赶快来下载...该文档为酒吧管理——服务流程与工作标准,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载...
  • 酒店管理一直遵循着为客户服务的准则,相信你也需要了解一下休闲中心服务标准流程图,赶快来下载休闲中心...该文档为休闲中心服务标准流程图,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 这是一款整理发布的中、西餐点菜服务标准流程,需要了解相关中、西餐点菜服务标准流程的你一定...该文档为中、西餐点菜服务标准流程,是一份很不错的参考资料,具有较高参考价值,感兴趣的可以下载看看
  • 这一份团队离店行李服务标准流程适合大家用于学习、参考、借鉴,希望团队离店行李服务标准流程就是你所需...该文档为团队离店行李服务标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 这一份散客离店行李服务标准流程适合大家用于学习、参考、借鉴,希望散客离店行李服务标准流程就是你所需...该文档为散客离店行李服务标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 客户服务部退货操作标准流程以规范化构造端到端卓越业务流程为中心,致力于打造最强、最好客户服务部...该文档为客户服务部退货操作标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 客户服务部订单处理标准流程以规范化构造端到端卓越业务流程为中心,致力于打造最强、最好客户服务部...该文档为客户服务部订单处理标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 随着全球经济快速发展,酒店行业也跟着崛起了,在这里,为大家带来了美容美体中心服务标准流程...该文档为美容美体中心服务标准流程图,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 这是一款整理发布酒店管理之休闲中心服务标准流程图,需要了解相关酒店管理之休闲中心服务标...该文档为酒店管理之休闲中心服务标准流程图,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 俗话说流程决定绩效,而这一款整理发布某总公司客户服务部退货标准操作流程定能给你最好流...该文档为某总公司客户服务部退货标准操作流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 标准售后服务流程和培训模版请不愿意动手些同志们可以拿来参考了
  • 这是一款整理发布酒店行业客务部客房服务员工作标准流程,需要了解相关酒店行业客务部客房服...该文档为酒店行业客务部客房服务员工作标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 这是一款整理发布酒店行业客务部会议服务员工作标准流程,需要了解相关酒店行业客务部会议服...该文档为酒店行业客务部会议服务员工作标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • golang本身就提供了http标志库,在golang中可以轻松编写http服务,本文主要是因为在编写http服务的过程中,对整个处理流程不是很了解故想了解一下。 示例代码 package main import ( "fmt" "net/http" ) func ...

    http标准库

    golang本身就提供了http的标志库,在golang中可以轻松的编写http服务,本文主要是因为在编写http服务的过程中,对整个处理流程不是很了解故想了解一下。

    示例代码

    package main
    
    import (
    "fmt"
    "net/http"
    )
    
    func hello(w http.ResponseWriter, req *http.Request) {
    
    	fmt.Fprintf(w, "hello\n")
    }
    
    func headers(w http.ResponseWriter, req *http.Request) {
    
    	for name, headers := range req.Header {
    		for _, h := range headers {
    			fmt.Fprintf(w, "%v: %v\n", name, h)
    		}
    	}
    }
    
    func main() {
    	http.HandleFunc("/hello", hello)
    	http.HandleFunc("/headers", headers)
    	http.ListenAndServe(":7777", nil)
    }
    
    

    这是官网上面提供的一个简单的http服务的例子,从结构上看处理逻辑相对比较简单清晰,定义一个路由然后定义一个处理函数,许多go的web框架基本上都是在这个标准库的基础上封装而来,如果访问http://127.0.0.1:7777/hello就可以看见输出的内容为hello。

    http.ListenAndServe
    func ListenAndServe(addr string, handler Handler) error {
    	server := &Server{Addr: addr, Handler: handler}
    	return server.ListenAndServe()
    }
    

    从代码上来看,该函数很简单,直接通过初始化了一个Server,然后调用了ListenAndServe方法,从而开始处理请求。

    func (srv *Server) ListenAndServe() error {
    	if srv.shuttingDown() {  	// 检查是否关闭
    		return ErrServerClosed
    	}
    	addr := srv.Addr
    	if addr == "" {
    		addr = ":http"
    	}
    	ln, err := net.Listen("tcp", addr) 				// 使用tcp协议处理地址与端口的请求
    	if err != nil {
    		return err
    	}
    	return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})  // 开始循环处理
    }
    

    主要传入了tcpKeepAliveListener这个类型的监听对象;

    type tcpKeepAliveListener struct {
    	*net.TCPListener
    }
    
    func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
    	tc, err := ln.AcceptTCP() 								// 接受请求
    	if err != nil {
    		return nil, err
    	}
    	tc.SetKeepAlive(true) 										// 设置keepAlive
    	tc.SetKeepAlivePeriod(3 * time.Minute)    // 设置保存的时间
    	return tc, nil
    }
    

    Server函数处理;

    func (srv *Server) Serve(l net.Listener) error {
    	if fn := testHookServerServe; fn != nil {
    		fn(srv, l) // call hook with unwrapped listener
    	}
    
    	l = &onceCloseListener{Listener: l} 				// 当关闭时 只关闭一次该Listen
    	defer l.Close()
    
    	if err := srv.setupHTTP2_Serve(); err != nil {
    		return err
    	}
    
    	if !srv.trackListener(&l, true) {
    		return ErrServerClosed
    	}
    	defer srv.trackListener(&l, false)
    
    	var tempDelay time.Duration     // how long to sleep on accept failure
    	baseCtx := context.Background() // base is always background, per Issue 16220
    	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    	for {
    		rw, e := l.Accept() 											// 处理接受的请求
    		if e != nil {
    			select {
    			case <-srv.getDoneChan(): 						// 检查是否处理完成,如果关闭则退出
    				return ErrServerClosed
    			default:
    			}
    			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
    				}
    				srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
    				time.Sleep(tempDelay)
    				continue
    			}
    			return e
    		}
    		tempDelay = 0
    		c := srv.newConn(rw)                  	// 初始化一个新的连接
    		c.setState(c.rwc, StateNew) // before Serve can return  
    		go c.serve(ctx) 												// 开启协程来处理该请求
    	}
    }
    
    conn.server
    // Serve a new connection.
    func (c *conn) serve(ctx context.Context) {
    	c.remoteAddr = c.rwc.RemoteAddr().String() 								// 获取远端地址
    	ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
    	defer func() {
    		if err := recover(); err != nil && err != ErrAbortHandler {
    			const size = 64 << 10
    			buf := make([]byte, size)
    			buf = buf[:runtime.Stack(buf, false)]
    			c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
    		}
    		if !c.hijacked() {
    			c.close()
    			c.setState(c.rwc, StateClosed)
    		}
    	}() 																											// 处理错误
    
    	if tlsConn, ok := c.rwc.(*tls.Conn); ok { 								// 检查是否是https连接
    		if d := c.server.ReadTimeout; d != 0 {
    			c.rwc.SetReadDeadline(time.Now().Add(d))
    		}
    		if d := c.server.WriteTimeout; d != 0 {
    			c.rwc.SetWriteDeadline(time.Now().Add(d))
    		}
    		if err := tlsConn.Handshake(); err != nil {
    			// If the handshake failed due to the client not speaking
    			// TLS, assume they're speaking plaintext HTTP and write a
    			// 400 response on the TLS conn's underlying net.Conn.
    			if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) {
    				io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n")
    				re.Conn.Close()
    				return
    			}
    			c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
    			return
    		}
    		c.tlsState = new(tls.ConnectionState)
    		*c.tlsState = tlsConn.ConnectionState()
    		if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {
    			if fn := c.server.TLSNextProto[proto]; fn != nil {
    				h := initNPNRequest{tlsConn, serverHandler{c.server}}
    				fn(c.server, tlsConn, h)
    			}
    			return
    		}
    	}
    
    	// HTTP/1.x from here on.
    
    	ctx, cancelCtx := context.WithCancel(ctx)
    	c.cancelCtx = cancelCtx
    	defer cancelCtx()
    
    	c.r = &connReader{conn: c} 															// 初始化一个读连接
    	c.bufr = newBufioReader(c.r) 														//生成一个读buf
    	c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)  // 生成一个写buf,该bufw底层写入就是链接c
    
    	for {
    		w, err := c.readRequest(ctx) 														// 初始化一个response
    		if c.r.remain != c.server.initialReadLimitSize() {
    			// If we read any bytes off the wire, we're active.
    			c.setState(c.rwc, StateActive)
    		}
    		if err != nil {
    			const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"
    
    			if err == errTooLarge {
    				// Their HTTP client may or may not be
    				// able to read this if we're
    				// responding to them and hanging up
    				// while they're still writing their
    				// request. Undefined behavior.
    				const publicErr = "431 Request Header Fields Too Large"
    				fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
    				c.closeWriteAndWait()
    				return
    			}
    			if isCommonNetReadError(err) {
    				return // don't reply
    			}
    
    			publicErr := "400 Bad Request"
    			if v, ok := err.(badRequestError); ok {
    				publicErr = publicErr + ": " + string(v)
    			}
    
    			fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
    			return
    		}
    
    		// Expect 100 Continue support
    		req := w.req 																				// 获取解析后的request
    		if req.expectsContinue() {
    			if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
    				// Wrap the Body reader with one that replies on the connection
    				req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
    			}
    		} else if req.Header.get("Expect") != "" {
    			w.sendExpectationFailed()
    			return
    		}
    
    		c.curReq.Store(w) 
    
    		if requestBodyRemains(req.Body) {  					// 解析头部信息
    			registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
    		} else {
    			w.conn.r.startBackgroundRead() 						// 如果没有读出到则后台读客户端传入的数据
    		}
    
    		// HTTP cannot have multiple simultaneous active requests.[*]
    		// Until the server replies to this request, it can't read another,
    		// so we might as well run the handler in this goroutine.
    		// [*] Not strictly true: HTTP pipelining. We could let them all process
    		// in parallel even if their responses need to be serialized.
    		// But we're not going to implement HTTP pipelining because it
    		// was never deployed in the wild and the answer is HTTP/2.
    		serverHandler{c.server}.ServeHTTP(w, w.req)      // 通过server设置的handle来处理该请求
    		w.cancelCtx()
    		if c.hijacked() {
    			return
    		}
    		w.finishRequest() 															// 将处理的请求写入输出缓冲区将处理完成数据返回给客户端
    		if !w.shouldReuseConnection() { 								// 检查是否处理完该请求后关闭该链接
    			if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
    				c.closeWriteAndWait()
    			}
    			return
    		}
    		c.setState(c.rwc, StateIdle) 										// 设置为空闲
    		c.curReq.Store((*response)(nil))
    
    		if !w.conn.server.doKeepAlives() {
    			// We're in shutdown mode. We might've replied
    			// to the user without "Connection: close" and
    			// they might think they can send another
    			// request, but such is life with HTTP/1.1.
    			return
    		}
    
    		if d := c.server.idleTimeout(); d != 0 { 			
    			c.rwc.SetReadDeadline(time.Now().Add(d))
    			if _, err := c.bufr.Peek(4); err != nil {
    				return
    			}
    		}
    		c.rwc.SetReadDeadline(time.Time{})
    	}
    }
    
    
    conn.readRequest
    // Read next request from connection.
    func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
    	if c.hijacked() {
    		return nil, ErrHijacked
    	}
    
    	var (
    		wholeReqDeadline time.Time // or zero if none
    		hdrDeadline      time.Time // or zero if none
    	)
    	t0 := time.Now() 										// 获取当前时间
    	if d := c.server.readHeaderTimeout(); d != 0 { 		// 是否设置读头部信息超时
    		hdrDeadline = t0.Add(d)
    	}
    	if d := c.server.ReadTimeout; d != 0 {     // 是否设置读超时
    		wholeReqDeadline = t0.Add(d)
    	}
    	c.rwc.SetReadDeadline(hdrDeadline)
    	if d := c.server.WriteTimeout; d != 0 { 		// 是否设置写超时
    		defer func() {
    			c.rwc.SetWriteDeadline(time.Now().Add(d))
    		}()
    	}
    
    	c.r.setReadLimit(c.server.initialReadLimitSize())   // 设置读时间限制
    	if c.lastMethod == "POST" {
    		// RFC 7230 section 3 tolerance for old buggy clients.
    		peek, _ := c.bufr.Peek(4) // ReadRequest will get err below
    		c.bufr.Discard(numLeadingCRorLF(peek))
    	}
    	req, err := readRequest(c.bufr, keepHostHeader) 		// 读头部信息并解析头部协议
    	if err != nil {
    		if c.r.hitReadLimit() {
    			return nil, errTooLarge
    		}
    		return nil, err
    	}
    
    	if !http1ServerSupportsRequest(req) {             	// 检查是否支持
    		return nil, badRequestError("unsupported protocol version")
    	}
    
    	c.lastMethod = req.Method
    	c.r.setInfiniteReadLimit()
    
    	hosts, haveHost := req.Header["Host"] 							// 获取host信息
    	isH2Upgrade := req.isH2Upgrade()
    	if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade && req.Method != "CONNECT" {
    		return nil, badRequestError("missing required Host header")
    	}
    	if len(hosts) > 1 {
    		return nil, badRequestError("too many Host headers")
    	}
    	if len(hosts) == 1 && !httpguts.ValidHostHeader(hosts[0]) {
    		return nil, badRequestError("malformed Host header")
    	}
    	for k, vv := range req.Header {
    		if !httpguts.ValidHeaderFieldName(k) {
    			return nil, badRequestError("invalid header name")
    		}
    		for _, v := range vv {
    			if !httpguts.ValidHeaderFieldValue(v) {
    				return nil, badRequestError("invalid header value")
    			}
    		}
    	}
    	delete(req.Header, "Host")
    
    	ctx, cancelCtx := context.WithCancel(ctx)
    	req.ctx = ctx
    	req.RemoteAddr = c.remoteAddr
    	req.TLS = c.tlsState
    	if body, ok := req.Body.(*body); ok { 				// 获取请求body
    		body.doEarlyClose = true
    	}
    
    	// Adjust the read deadline if necessary.
    	if !hdrDeadline.Equal(wholeReqDeadline) {
    		c.rwc.SetReadDeadline(wholeReqDeadline)
    	}
    
    	w = &response{
    		conn:          c,
    		cancelCtx:     cancelCtx,
    		req:           req,
    		reqBody:       req.Body,
    		handlerHeader: make(Header),
    		contentLength: -1,
    		closeNotifyCh: make(chan bool, 1),
    
    		// We populate these ahead of time so we're not
    		// reading from req.Header after their Handler starts
    		// and maybe mutates it (Issue 14940)
    		wants10KeepAlive: req.wantsHttp10KeepAlive(),
    		wantsClose:       req.wantsClose(),
    	} 																			// 生成一个response 将连接信息都传入
    	if isH2Upgrade {
    		w.closeAfterReply = true
    	}
    	w.cw.res = w
    	w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)   // 生成一个写缓冲区
    	return w, nil
    }
    

    从readRequest执行流程来看,主要就是先检查是否设置超时时间,然后通过readRequest函数来读取并解析http头部内容,最后生成一个response,提供给Handle来处理,提供给serverHandler的ServerHttp函数来处理,ServeHTTP函数就是我们再示例代码中注册的两个func

    serverHandler.ServeHTTP

    在处理逻辑serverHandler{c.server}.ServeHTTP(w, w.req)这行代码中

    type serverHandler struct {
    	srv *Server
    }
    
    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    	handler := sh.srv.Handler
    	if handler == nil {
    		handler = DefaultServeMux 		// 默认的handle 本示例代码中就是该默认的
    	}
    	if req.RequestURI == "*" && req.Method == "OPTIONS" {
    		handler = globalOptionsHandler{}
    	}
    	handler.ServeHTTP(rw, req)      // 调用该handle来处理请求
    }
    

    此时调用的就是DefaultServeMux

    func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    	if r.RequestURI == "*" {
    		if r.ProtoAtLeast(1, 1) {
    			w.Header().Set("Connection", "close")
    		}
    		w.WriteHeader(StatusBadRequest)
    		return
    	}
    	h, _ := mux.Handler(r)    // 查找匹配的handle
    	h.ServeHTTP(w, r)         // 调用该handle的处理方式
    }
    ...
    func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    
    	// CONNECT requests are not canonicalized.
    	if r.Method == "CONNECT" {
    		// If r.URL.Path is /tree and its handler is not registered,
    		// the /tree -> /tree/ redirect applies to CONNECT requests
    		// but the path canonicalization does not.
    		if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
    			return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
    		}
    
    		return mux.handler(r.Host, r.URL.Path)
    	}
    
    	// All other requests have any port stripped and path cleaned
    	// before passing to mux.handler.
    	host := stripHostPort(r.Host)     // 获取host
    	path := cleanPath(r.URL.Path)     // 获取path
    
    	// If the given path is /tree and its handler is not registered,
    	// redirect for /tree/.
    	if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
    		return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
    	}
    
    	if path != r.URL.Path {
    		_, pattern = mux.handler(host, path)     // 通过host,path来比对
    		url := *r.URL
    		url.Path = path
    		return RedirectHandler(url.String(), StatusMovedPermanently), pattern
    	}
    
    	return mux.handler(host, r.URL.Path)     // 调用handler来匹配handler
    }
    
    func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    	mux.mu.RLock()
    	defer mux.mu.RUnlock()
    
    	// Host-specific pattern takes precedence over generic ones
    	if mux.hosts {
    		h, pattern = mux.match(host + path)   	// 匹配host加path
    	}
    	if h == nil {
    		h, pattern = mux.match(path)            // 如果只有path则匹配path
    	}
    	if h == nil {
    		h, pattern = NotFoundHandler(), ""     //返回没有找到
    	}
    	return
    }
    
    func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    	// Check for exact match first.
    	v, ok := mux.m[path] 										// 检查该path是否在路径中
    	if ok {
    		return v.h, v.pattern                 // 如果在则返回handle和pattern
    	}
    
    	// Check for longest valid match.  mux.es contains all patterns
    	// that end in / sorted from longest to shortest.
    	for _, e := range mux.es {
    		if strings.HasPrefix(path, e.pattern) {  	// 进行字符匹配
    			return e.h, e.pattern
    		}
    	}
    	return nil, ""
    }
    

    通过ServerHTTP的处理流程可以看出,先通过全路径匹配,然后找到对应自己编写的handle,通过编写的handle处理的流程。

    handle注册的流程如下,在本文示例中http.HandleFunc函数就是注册了处理对应url的handle

    func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    	DefaultServeMux.HandleFunc(pattern, handler)
    }
    
    ...
    // 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))
    }
    ...
    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}  // 设置handle与匹配方式
    	mux.m[pattern] = e
    	if pattern[len(pattern)-1] == '/' {
    		mux.es = appendSorted(mux.es, e)
    	}
    
    	if pattern[0] != '/' {
    		mux.hosts = true
    	}
    }
    ...
    

    此时完成了handle的匹配,并调用了该handle来处理具体的业务逻辑。

    response.finishRequest
    func (w *response) finishRequest() {
    	w.handlerDone.setTrue()    // 设置该handle被处理完成
    
    	if !w.wroteHeader {        // 如果没有头部则写入ok头部信息
    		w.WriteHeader(StatusOK)
    	}
    
    	w.w.Flush() 							// 刷新
    	putBufioWriter(w.w)       // 将处理的w刷入
    	w.cw.close()  						// 关闭
    	w.conn.bufw.Flush()       // 将业务处理所有的buff都发送给客户端
    
    	w.conn.r.abortPendingRead()
    
    	// Close the body (regardless of w.closeAfterReply) so we can
    	// re-use its bufio.Reader later safely.
    	w.reqBody.Close()
    
    	if w.req.MultipartForm != nil {
    		w.req.MultipartForm.RemoveAll()
    	}
    }
    

    w.conn.bufw就是c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

    通过注册该c,来控制写出的数据发送给客户端;

    func (b *Writer) Flush() error {
    	if b.err != nil {
    		return b.err
    	}
    	if b.n == 0 {
    		return nil
    	}
    	n, err := b.wr.Write(b.buf[0:b.n])  // 写入处理完成的数据,该wr就是前面初始化传入的c
    	if n < b.n && err == nil {
    		err = io.ErrShortWrite
    	}
    	if err != nil {
    		if n > 0 && n < b.n {
    			copy(b.buf[0:b.n-n], b.buf[n:b.n])
    		}
    		b.n -= n
    		b.err = err
    		return err
    	}
    	b.n = 0
    	return nil
    }
    

    至此一个业务逻辑的处理流程就基本完成。

    整个业务流程

    注册对应的url与handle
    添加到列表中
    添加完成之后启动Server
    监听端口
    启动服务端
    监听新接入的请求
    循环等待请求
    请求进来
    读客户端的数据
    处理请求
    如果处理完成或者异常
    http.HandleFunc
    DefaultServeMux.HandleFunc
    mux.Handle
    http.ListenAndServe
    net.Listen
    srv.Serve
    l.Accept
    c.serve
    readRequest
    serverHandler
    退出

    总结

    本文主要是简单的梳理了一下go标准库里面http服务器的一个基本的处理流程,大致处理流程清楚之后对go相关的web的框架的处理过程也都大致了解,方便开发过程中灵活运用。由于本人才疏学浅,如有错误请批评指正。

    展开全文
  • 随着全球经济快速发展,酒店行业也跟着崛起了,在这里,为大家带来了酒店行业客务部客房服务员...该文档为酒店行业客务部客房服务员工作标准流程-,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 随着全球经济快速发展,酒店行业也跟着崛起了,在这里,为大家带来了酒店行业客务部客房服务员...该文档为酒店行业客务部客房服务员工作标准流程1,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 本文详细介绍了标准的Windows蓝屏故障分析、处理流程。介绍了如何使用WinDbg工具来分析内核转储文件(Crashdump)以确定问题。在文末附带记录了VMware服务错误处理记录。 操作系统:Windows 7 Enterprise X64...

    本文详细的介绍了标准的Windows蓝屏故障的分析、处理流程。介绍了如何使用WinDbg工具来分析内核转储文件(Crashdump)以确定问题。在文末附带的记录了VMware服务错误的处理记录。

    操作系统:Windows 7 Enterprise X64 SP1 [Version 6.1.7601]。

    系统用途:这个机器安装的软件较多,一般用于软件测试使用。安装了很多Web服务,如SQL Server,IIS等和软件开发工具,如VS2010,DW和各种Studio、Kit等。

    可能的触发原因:由无线接入Internet方式切换到有线接入Internet方式(两者都是手动填写IP地址等,“等”是指子网掩码、默认网关、DNS服务器IP地址),配置IP后,检查了一下日志后,确定当前的状态在日志(事件)中没有问题,重启系统。在重启的过程中遇到蓝屏故障,系统自动重新启动,进入安全模式,登录前系统再次自动重新启动到正常模式。

    解决方案:首先使用Windows Driver Kit 7.1中的Windows Debugging Tool (WinDBG)分析内存转储文件

    image

    如果提示:

    image

    则用Administrator身份运行WinDbg。

    在查看了相关信息后(如下图的图c),可以点击或者在Console中输入 "!analyze-v”查看详细的分析结果,下图图d。

     

    image(图c)

    image(图d)

    查阅wdf的相关信息,先查看一下wdf01000.sys是不是系统文件。

    image

    找到文件位置,查看它的属性,发现是Windows的系统文件,而且还是内核模式驱动框架运行时程序(原文是Kernel Mode Driver Framework Runtime ),这个以前从没接触过,怀疑是系统bug,正如前面WinDbg说的,“

    This is a very common bugcheck.  Usually the exception address pinpoints 
    the driver/function that caused the problem.  Always note this address 
    as well as the link date of the driver/image that contains this address. 
    Some common problems are exception code 0x80000003.  This means a hard 
    coded breakpoint or assertion was hit, but this system was booted 
    /NODEBUG.  This is not supposed to happen as developers should never have 
    hardcoded breakpoints in retail code, but ... 
    If this happens, make sure a debugger gets connected, and the 
    system is booted /DEBUG.  This will let us see why this breakpoint is 
    happening.

    这个问题目前只能提交给微软的工程师来解决了。

    image

    再分析日志,特别是Error和Critical级别的日志。

    发现问题:

    image

    打开服务管理控制台,尝试手动启动VMwareHostd(服务名,显示名称为VMware Workstation Server)服务,结果遇到错误提示,如下图所示。

    图a是获取服务名,对以后的使用会有帮助。

    image(图a)

    图b是错误提示。

    image(图b)

    但是VMware里的虚拟机还是可以使用的。

    解决方案:通过Windows事件查看器的在线帮助查找是否有解决方案。

    网址为http://technet.microsoft.com/en-us/library/ab3f45e5-f0ec-49d0-b4f7-d4581984f420.aspx

    此页面提示访问Vendor(制造商、开发商)官方网站寻求办法。

    根据下图的提示搜索。

     

    image

     

    找到几个有用的:

    <The VMware Workstation Server service terminated with service-specific error %%-1.>

    http://communities.vmware.com/thread/334353?tstart=0




    本文转自 urey_pp 51CTO博客,原文链接:http://blog.51cto.com/dgd2010/998012,如需转载请自行联系原作者


    展开全文
  • 我们将这种方法应用于 Web 服务编排标准的历史,其中 500 多名参与者在 12 年时间里穿越了九个机构。 我们通过分析标准化思想运动模式来解释这段历史关键方面。 我们表明标准制定机构通过利用反映机构价值观...
  • 吧台工作标准流程

    2020-12-20 18:06:47
    酒店管理一直遵循着为客户服务的准则,相信你也需要了解一下吧台工作标准流程,赶快来下载吧台工作标准流...该文档为吧台工作标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 出入库标准流程

    2020-12-15 16:17:53
    物流采购若是能降低其过程成本,使其达到令人满意的服务水平那便是极为成功,而出入库标准流程可以给你...该文档为出入库标准流程,是一份很不错参考资料,具有较高参考价值,感兴趣可以下载看看
  • 一、服务器上架标准 因为我们服务器有专门人员在IDC进行上架,下面几项我们按照标准来进行提供。 要指定配置IP,子网掩码,网关;...系统初始化标准流程使用一键初始化脚本,主要包含一下几个参数设定: ...

    一、服务器上架标准

    因为我们的服务器有专门的人员在IDC进行上架,下面几项我们按照标准来进行提供。

    • 要指定配置的IP,子网掩码,网关;
    • 安装系统的版本(CentOS 7.6 mini),比如分区如下;
    目录 大小
    boot 200MB
    swap 8GB
    / 剩余大小

    二、系统初始化标准

    系统的初始化标准流程使用一键初始化脚本,主要包含一下几个参数的设定:

    1、配置服务器时区

    rm -f /etc/localtime
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

    2、配置国内yum源

    如果是在阿里云下载的镜像,yum已经自动配置好了。

    yum install wget
    rm -f /etc/yum.repos.d/*
    curl -o /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo
    curl -o /etc/yum.repos.d/epel-7.repo https://mirrors.aliyun.com/repo/epel-7.repo

    3、添加zabbix监控

    如果有特殊需求可以使用自己编译的安装包,通用的可以使用yum仓库中自带的客户端。

    4、安装软件包

    因为系统是最小化安装,所以工具包没有。

    yum install -y vim openssh-clients ntpdate man lrzsz net-tools

    5、配置定时任务

    echo "$((RANDOM%60)) $((RANDOM%24)) * * * /usr/sbin/ntpdate time1.aliyun.com" >> /var/spool/cron/root

    6、配置主机名

    每个公司都有自己的设定。
    用途-业务-区域-域名

    web[1-9]-pc-shanghai.example.com

    三、安全运维标准

    1、密码重置

    修改 root 密码复杂度,字母数字字符的组合。

    2、修改远程端口

    3、关闭SELinux

    sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
    setenforce 0

    4、关闭防火墙和NM

    systemctl stop firewalld.service
    systemctl disable firewalld.service
    systemctl stop NetworkManager
    systemctl disable NetworkManager

    5、录入跳板机系统

    转载于:https://www.cnblogs.com/wzlinux/p/10438784.html

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,239
精华内容 1,695
关键字:

服务流程的标准