龙空技术网

HTML框架分层设计

极速星空4DO 181

前言:

目前各位老铁们对“htmludp”大约比较关注,咱们都需要知道一些“htmludp”的相关知识。那么小编在网络上网罗了一些有关“htmludp””的相关内容,希望咱们能喜欢,兄弟们快快来学习一下吧!

计算机网络中的OSI七层模型

计算机网络中的OSI(Open Systems Interconnection)七层模型是一种理论框架,用于描述计算机网络中数据通信的过程。OSI模型将计算机网络通信过程划分为七个层次,每个层次都有其特定的功能和协议。这种分层结构有助于研究和理解计算机网络中的通信原理。以下是OSI七层模型的各个层次及其主要功能:

应用层是OSI模型的第七层,也是网络应用程序和网络协议之间的接口。应用层主要负责为用户提供各类应用服务,如文件传输、电子邮件、Web浏览等。

表示层是OSI模型的第六层,主要负责处理在网络中传输的数据的表示方式,如数据加密、解密、压缩、解压缩等。表示层确保了不同系统之间的数据兼容性。

会话层是OSI模型的第五层,主要负责建立、维护和终止应用程序之间的通信会话。会话层提供了数据交换的同步和确认机制。

传输层是OSI模型的第四层,主要负责在源主机和目标主机之间提供可靠的、端到端的数据传输服务。传输层通过分段、封装和重组数据来实现可靠的数据传输。常见的传输层协议包括TCP(传输控制协议)和UDP(用户数据报协议)。

网络层是OSI模型的第三层,主要负责将数据包从源主机路由到目标主机。网络层主要负责逻辑寻址、路由选择和分组转发。常见的网络层协议包括IP(互联网协议)和ICMP(互联网控制报文协议)。

数据链路层是OSI模型的第二层,主要负责将网络层传来的数据包封装成帧(Frame),并在同一局域网内进行传输。数据链路层主要负责物理寻址、数据成帧、错误检测和流量控制。常见的数据链路层协议包括以太网(Ethernet)、令牌环(Token Ring)和无线局域网(Wi-Fi)等。

物理层是OSI模型的第一层,主要负责在物理介质上实现比特流的透明传输。物理层主要关注硬件接口、电气特性、光纤、无线传输等方面的问题。

OSI七层模型提供了一个通用的框架,帮助研究和理解计算机网络中的通信原理。实际应用中,我们通常使用TCP/IP四层模型,它包括了应用层、传输层、网络层和链路层,与OSI模型有一定的对应关系。

HTML框架的必要性

HTML框架进行分层设计的主要原因是为了提高代码的可读性、可维护性和可重用性。将HTML框架分层可以提高整体项目的结构和逻辑,便于开发者更好地理解和修改代码。分层设计具有以下优点:

提高可读性:通过将HTML框架划分为不同的层次,可以使代码结构更清晰,有助于开发者快速理解代码的功能。便于维护:分层设计有助于将功能模块化,这样可以方便地修改或替换某个模块,而不会影响其他部分的代码。这有助于提高项目的可维护性。可重用性:将HTML框架分层可以将公共部分提取为可重用的组件,这样可以在不同项目中重复使用这些组件,提高开发效率。适应性:分层设计可以让HTML框架更容易适应不同的设备和屏幕尺寸,提高项目的兼容性。便于协作:在大型项目中,通常会有多个开发者参与。通过分层设计,开发者可以专注于自己的模块,减少代码冲突和沟通成本。HTML框架的组成

HTML框架包括Application层``middleware层``route层``codec层``transport层 Application层 应用层通常包括与业务逻辑相关的代码,如Web应用程序的控制器(Controller)、视图(View)和模型(Model)。应用层的主要作用是处理用户请求并返回相应的响应。

Middleware层 中间件层是介于应用层和底层框架之间的一层,负责处理一些通用的功能,如身份验证、授权、缓存、日志记录等。中间件层有助于将业务逻辑与通用功能分离,使得应用层更加简洁和易于维护。

Route层 路由层负责处理HTTP请求的URL和HTTP方法(如GET、POST等),将请求分发到相应的控制器和方法。路由层的主要作用是根据URL映射来定位具体的功能代码。

Codec层 编解码层负责处理数据的编码和解码。在Web开发中,编码和解码通常涉及到HTML、CSS、JavaScript等前端技术的处理,以及JSON、XML等数据交换格式的处理。编解码层的主要作用是将数据转换为特定的格式,以便在不同层之间进行传输和处理。

Transport层 传输层负责处理底层的网络通信,如TCP、UDP等协议的使用。在Web开发中,传输层通常涉及到HTTP协议的处理,包括请求和响应的创建、发送和接收。传输层的主要作用是确保数据的可靠传输和在网络中的正确路由。

这些层次在实际应用中可能因框架和场景的不同而有所差异。但是,从您提供的描述来看,它们分别负责处理Web应用程序中的不同功能,共同构成了一个完整的Web开发框架。

HTML框架和服务端客户端之间的通信对比Application层应用层设计

应用层设计主要是设置各种接口,用于路由使用。

例如在字节后端进阶版中的大项目中的注册接口。

/douyin/user/register/ - 用户注册接口

新用户注册时提供用户名,密码,昵称即可,用户名需要保证唯一。创建成功后返回用户 id 和权限token.

接口类型

POST

接口定义

go复制代码syntax = "proto2";package douyin.core;message douyin_user_register_request {  required string username = 1; // 注册用户名,最长32个字符  required string password = 2; // 密码,最长32个字符}message douyin_user_register_response {  required int32 status_code = 1; // 状态码,0-成功,其他值-失败  optional string status_msg = 2; // 返回状态描述  required int64 user_id = 3; // 用户id  required string token = 4; // 用户鉴权token}
go复制代码func Register(username, password string) (id int64, token int64, err error) {   if len(username) > 32 {      return 0, 0, errors.New("用户名过长,不可超过32位")   }   if len(password) > 32 {      return 0, 0, errors.New("密码过长,不可超过32位")   }   // 先查布隆过滤器,不存在直接返回错误,降低数据库的压力   if userNameFilter.TestString(username) {      return 0, 0, errors.New("用户名已经存在!")   }   //雪花算法生成token   node, err := snowflake.NewNode(1) //这里的userIdInt64就是 User.Id(主键)   if err != nil {      log.Println("雪花算法生成id错误!")      log.Println(err)   }   token1 := node.Generate().Int64()   tokenStr := strconv.FormatInt(token1, 10)   user := domain.User{}   // 再查缓存   data, err := dao.RedisClient.Get(context.Background(), tokenStr).Result()   if err == redis.Nil {      fmt.Println("token does not exist")   } else if err != nil {      fmt.Println("Error:", err)   } else {      num, err := strconv.ParseInt(data, 10, 64)      if err != nil {         fmt.Println("Error:", err)         return num, 0, err      }      return num, token1, nil   }   //在查数据库   user = domain.User{}   dao.DB.Model(&domain.User{}).Where("name = ?", username).Find(&user)   if user.Id != 0 {      return 0, 0, errors.New("用户已存在")   }   user.Name = username   // 加密存储用户密码   user.Salt = randSalt()   buf := bytes.Buffer{}   buf.WriteString(username)   buf.WriteString(password)   buf.WriteString(user.Salt)   pwd, err1 := bcrypt.GenerateFromPassword(buf.Bytes(), bcrypt.MinCost)   if err1 != nil {      return 0, 0, err   }   user.Pwd = string(pwd)   //存在mysql里边   dao.DB.Model(&domain.User{}).Create(&user)   //再把用户id作为键 用户的所有信息作为值存在其中   //用户信息的缓存是 保存在redis中 一个以id为键 user json为值   jsonuser, err1 := MarshalUser(user)   if err1 != nil {      fmt.Println("err1", err1)      return 0, 0, err1   }   err = dao.RedisClient.Set(context.Background(), strconv.FormatInt(user.Id, 10), jsonuser, 0).Err()   if err != nil {      fmt.Println("err", err)      return 0, 0, err   }   // 布隆过滤器中加入新用户   userIdFilter.AddString(strconv.FormatInt(user.Id, 10))   userNameFilter.AddString(username)   return user.Id, token1, nil}

本接口注册功能实现:把所有信息存在mysql里边当然redis里边也存在这些信息,当然username也存在了布容过滤器中去,当接收到用户的username的时候我们现在布容过滤器中先查询是否存在如果存在则直接返回err,不存在然后再在redis里边查询,因为redis相比于mysql是更为轻量级的所以我们要先在redis里边进行查,如果查不到再进mysql里边查去,查不到说明没有注册过,可以注册。

命名规范

遵循命名规范原则。

Middleware层中间件

gin框架里的中间件分为全局中间件,局部中间件。那么什么是中间件?中间件是为应用提供通用服务和功能的软件。数据管理、应用服务、消息传递、身份验证和 API 管理通常都要通过中间件。在gin框架里,就是我们的所有API接口都要经过我们的中间件,我们可以在中间件做一些拦截处理。

中间件常用模型全局中间件

这个是在服务启动就开始注册,全局意味着所有API接口都会经过这里。Gin的中间件是通过Use方法设置的,它接收一个可变参数,所以我们同时可以设置多个中间件。

首先定义如下

go复制代码// 1.创建路由r := gin.Default()  //默认带Logger(), Recovery()这两个内置中间件r:= gin.New()      //不带任何中间件// 注册中间件  r.Use(MiddleWare())r.Use(MiddleWare2())

注意的是

gin.Default()默认使用了Logger和Recovery中间件,其中:Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

go复制代码// 定义中间func MiddleWare() gin.HandlerFunc {    return func(c *gin.Context) {        t := time.Now()        fmt.Println("中间件开始执行了")        // 设置变量到Context的key中,可以通过Get()取        c.Set("request", "这是中间件设置的值")        status := c.Writer.Status()        fmt.Println("中间件执行完毕", status)                t2 := time.Since(t)        fmt.Println("time:", t2)    }}

然后启动我们的服务,访问任意一个接口可以看到输出如下

这是请求先到了中间件,然后在到我们的API接口。在中间件里可以设置变量到Context的key中,然后在我们的API接口取值。

go复制代码        r.GET("/", func(c *gin.Context) {            // 取值            req, _ := c.Get("request")            fmt.Println("request:", req)            // 页面接收            c.JSON(200, gin.H{"request": req})        })

这时候在访问就可以看到中间件设置的值是

next方法是在中间件里面使用,这个是执行后续中间件请求处理的意思(含没有执行的中间件和我们定义的GET方法处理,如果连续注册几个中间件则会是按照顺序先进后出的执行,遇到next就去执行下一个中间件里的next前面方法。

go复制代码        // 执行函数        c.Next()        // 中间件执行完后续的一些事情
局部中间件

局部中间件意味着部分接口才会生效,只在局部使用,这时候访问http:127.0.0.1:8000/ 才会看到中间件的日志打印,其他API接口则不会出现。

go复制代码   //局部中间件使用    r.GET("/", MiddleWare(), func(c *gin.Context) {        // 取值        req, _ := c.Get("request")        fmt.Println("request:", req)        // 页面接收        c.JSON(200, gin.H{"request": req})    })
gin内置中间件
go复制代码func BasicAuth(accounts Accounts) HandlerFuncfunc BasicAuthForRealm(accounts Accounts, realm string) HandlerFuncfunc Bind(val interface{}) HandlerFuncfunc ErrorLogger() HandlerFuncfunc ErrorLoggerT(typ ErrorType) HandlerFuncfunc Logger() HandlerFuncfunc LoggerWithConfig(conf LoggerConfig) HandlerFuncfunc LoggerWithFormatter(f LogFormatter) HandlerFuncfunc LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFuncfunc Recovery() HandlerFuncfunc RecoveryWithWriter(out io.Writer) HandlerFuncfunc WrapF(f http.HandlerFunc) HandlerFuncfunc WrapH(h http.Handler) HandlerFunc
总结

通过自定义中间件,我们可以很方便的拦截请求,来做一些我们需要做的事情,比如日志记录、授权校验、各种过滤等等。

route层路由层

Gin 是一个标准的 Web 服务框架,遵循 Restful API 接口规范,其路由库是基于 httproute 实现的。

本节将从 Gin 路由开始,详细讲述各种路由场景下,如何通过 Gin 来实现。

基本路由GET:用于处理从客户端发起的HTTP GET请求。GET请求用于从服务器获取数据,不应对服务器上的数据进行更改。例如,获取用户信息、获取文章列表等。POST:用于处理从客户端发起的HTTP POST请求。POST请求通常用于向服务器发送数据,用于创建新的资源或更新已有的资源。例如,用户注册、发布文章、更新用户信息等。PUT:用于处理从客户端发起的HTTP PUT请求。PUT请求通常用于更新服务器上的资源。例如,更新用户信息、更新文章内容等。DELETE:用于处理从客户端发起的HTTP DELETE请求。DELETE请求通常用于从服务器删除资源。例如,删除用户账户、删除文章等。PATCH:用于处理从客户端发起的HTTP PATCH请求。PATCH请求通常用于对服务器上的资源进行部分更新。例如,更新用户的部分信息、更新文章标题等。OPTIONS:用于处理从客户端发起的HTTP OPTIONS请求。OPTIONS请求用于获取服务器支持的HTTP方法。例如,跨域资源共享(CORS)场景。HEAD:用于处理从客户端发起的HTTP HEAD请求。HEAD请求类似于GET请求,但不返回响应体。主要用于获取响应头信息。例如,检查资源是否存在,但不需要获取资源内容。ANY:用于处理任何HTTP方法(GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD)的请求。适用于处理多种HTTP方法的情况。CONNECT:用于处理从客户端发起的HTTP CONNECT请求。CONNECT请求通常用于建立客户端与服务器之间的隧道,用于代理或其他场景。TRACE:用于处理从客户端发起的HTTP TRACE请求。TRACE请求用于测试或诊断网络连接。服务器应当返回原始的请求信息,以便客户端可以检查中间代理或防火墙是否进行了修改。

示例

字节大项目注册接口

go复制代码syntax = "proto2";package douyin.core;message douyin_user_register_request {  required string username = 1; // 注册用户名,最长32个字符  required string password = 2; // 密码,最长32个字符}message douyin_user_register_response {  required int32 status_code = 1; // 状态码,0-成功,其他值-失败  optional string status_msg = 2; // 返回状态描述  required int64 user_id = 3; // 用户id  required string token = 4; // 用户鉴权token}
go复制代码func Register(c *gin.Context) {   username := c.Query("username")   password := c.Query("password")   id, token, err := service.Register(username, password)   if err != nil {      c.JSON(http.StatusOK, domain.Response{StatusCode: 1, StatusMsg: err.Error()})   } else {      c.JSON(http.StatusOK, domain.UserLoginResponse{         //可以直接去掉         Response: domain.Response{StatusCode: 0},         Id:       id,         Token:    token,      })   }}
go复制代码package mainimport (   "github.com/gin-gonic/gin"   "github.com/goTouch/TicTok_SimpleVersion/controller")func initRouter(r *gin.Engine) {   // public directory is used to serve static resources   r.Static("/static", "./public")   apiRouter := r.Group("/douyin")   // basic apis   //controller.VerifyToken,   apiRouter.POST("/user/", controller.UserInfo)   apiRouter.POST("/user/register/", controller.LoginLimit, controller.Register)   apiRouter.POST("/user/login/", controller.LoginLimit, controller.Login)   }
codec层

在Web开发中,编码和解码通常涉及到HTML、CSS、JavaScript等前端技术的处理,以及JSON、XML等数据交换格式的处理。编解码层的主要作用是将数据转换为特定的格式,以便在不同层之间进行传输和处理。

示例

在postman中的示例 json

xml

html复制代码{"status_code":1,"status_msg":"redis: nil"}{"status_code":2,"status_msg":"no multipart boundary param in Content-Type"}
Text复制代码{"status_code":1,"status_msg":"redis: nil"}{"status_code":2,"status_msg":"no multipart boundary param in Content-Type"}
Auto复制代码{    "status_code": 1,    "status_msg": "redis: nil"}{    "status_code": 2,    "status_msg": "no multipart boundary param in Content-Type"}
transport层传输层

传输层负责处理底层的网络通信,如TCP、UDP等协议的使用。在Web开发中,传输层通常涉及到HTTP协议的处理,包括请求和响应的创建、发送和接收。传输层的主要作用是确保数据的可靠传输和在网络中的正确路由。

golang语言中net/http这个库中的conn 他是BIO自带阻塞

1. BIO (Blocking I/O)

同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。

1.1 传统 BIO

BIO通信(一请求一应答)模型图如下(图源网络,原出处不明):

采用 BIO 通信模型 的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接。我们一般通过在 while(true) 循环中服务端会调用 accept() 方法等待接收客户端的连接的方式监听请求,请求一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成, 不过可以通过多线程来支持多个客户端的连接,如上图所示。

如果要让 BIO 通信模型 能够同时处理多个客户端请求,就必须使用多线程(主要原因是 socket.accept()、 socket.read()、 socket.write() 涉及的三个主要函数都是同步阻塞的),也就是说它在接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的 一请求一应答通信模型 。我们可以设想一下如果这个连接不做任何事情的话就会造成不必要的线程开销,不过可以通过 线程池机制 改善,线程池还可以让线程的创建和回收成本相对较低。使用FixedThreadPool 可以有效的控制了线程的最大数量,保证了系统有限的资源的控制,实现了N(客户端请求数量):M(处理客户端请求的线程数量)的伪异步I/O模型(N 可以远远大于 M),下面一节"伪异步 BIO"中会详细介绍到。

我们再设想一下当客户端并发访问量增加后这种模型会出现什么问题?

程是宝贵的资源,线程的创建和销毁成本很高,除此之外,线程的切换成本也是很高的。尤其在 Linux 这样的操作系统中,线程本质上就是一个进程,创建和销毁线程都是重量级的系统函数。如果并发访问量增加会导致线程数急剧膨胀可能会导致线程堆栈溢出、创建新线程失败等问题,最终导致进程宕机或者僵死,不能对外提供服务。 golang实现BIO

NIO

NIO: NIO是一种同步非阻塞IO, 基于Reactor模型来实现的。其实相当于就是一个线程处理大量的客户端的请求,通过一个线程轮询大量的channel,每次就获取一批有事件的channel,然后对每个请求启动一个线程处理即可。这里的核心就是非阻塞,就那个selector一个线程就可以不停轮询channel,所有客户端请求都不会阻塞,直接就会进来,大不了就是等待一下排着队而已。这里面优化BIO的核心就是,一个客户端并不是时时刻刻都有数据进行交互,没有必要死耗着一个线程不放,所以客户端选择了让线程歇一歇,只有客户端有相应的操作的时候才发起通知,创建一个线程来处理请求。

————————————————

NIO:模型图

Reactor模型:

NIO核心组件详细讲解

学习NIO先来搞清楚一些相关的概念,NIO通讯有哪些相关组件,对应的作用都是什么,之间有哪些联系?

多路复用机制实现Selector

首先我们来了解下传统的Socket网络通讯模型。

传统Socket通讯原理图

为什么传统的socket不支持海量连接

每次一个客户端接入,都是要在服务端创建一个线程来服务这个客户端的,这会导致大量的客户端的时候,服务端的线程数量可能达到几千甚至几万,几十万,这会导致服务器端程序负载过高,不堪重负,最终系统崩溃死掉。

接着来看下NIO是如何基于Selector实现多路复用机制支持的海量连接。

NIO原理图

多路复用机制是如何支持海量连接

NIO的线程模型 对Socket发起的连接不需要每个都创建一个线程,完全可以使用一个Selector来多路复用监听N多个Channel是否有请求,该请求是对应的连接请求,还是发送数据的请求,这里面是基于操作系统底层的Select通知机制的,一个Selector不断的轮询多个Channel,这样避免了创建多个线程,只有当莫个Channel有对应的请求的时候才会创建线程,可能说1000个请求, 只有100个请求是有数据交互的, 这个时候可能server端就提供10个线程就能够处理这些请求。这样的话就可以避免了创建大量的线程。

NIO如何通过Buffer来缓冲数据的

NIO中的Buffer是个什么东西 ?

学习NIO,首当其冲就是要了解所谓的Buffer缓冲区,这个东西是NIO里比较核心的一个部分,一般来说,如果你要通过NIO写数据到文件或者网络,或者是从文件和网络读取数据出来此时就需要通过Buffer缓冲区来进行。Buffer的使用一般有如下几个步骤:

写入数据到Buffer,调用flip()方法,从Buffer中读取数据,调用clear()方法或者compact()方法。

Buffer中对应的Position, Mark, Capacity,Limit都啥?

capacity: 缓冲区容量的大小,就是里面包含的数据大小。

limit: 对buffer缓冲区使用的一个限制,从这个index开始就不能读取数据了。

position: 代表着数组中可以开始读写的index, 不能大于limit。

mark: 是类似路标的东西,在某个position的时候,设置一下mark,此时就可以设置一个标记,后续调用reset()方法可以把position复位到当时设置的那个mark上去,把position或limit调整为小于mark的值时,就丢弃这个mark。如果使用的是Direct模式创建的Buffer的话,就会减少中间缓冲直接使用的是DirectorBuffer来进行数据的存储。

————————————————

如何通过Channel和FileChannel读取Buffer数据写入磁盘的

NIO中,Channel是什么?

Channel是NIO中的数据通道,类似流,但是又有些不同,Channel即可从中读取数据,又可以从写数据到通道中,但是流的读写通常是单向的。Channel可以异步的读写。Channel中的数据总是要先读到一个Buffer中,或者从缓冲区中将数据写到通道中。

FileChannel的作用是什么 Buffer有不同的类型,同样Channel也有好几个类型。 FileChannel,DatagramChannel,SocketChannel,ServerSocketChannel。这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。而FileChannel就是文件IO对应的管道, 在读取文件的时候会用到这个管道。 golang的NIO

标签: #htmludp #框架隐藏css #html框架布局网页代码左右上中下 #html框架大小怎么调 #html默认极速模式