重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
最近在学习Go语言,利用之前的项目作为案例进行重构。项目背景:php提供TCP服务,硬件连接服务器需要处理信息,然后将处理的信息在发送客户端,客户端拿到信息展示给用户。第一种方案客户端对发送信息的接口进行轮询,检查硬件是否有信息返回,这样不好太浪费资源;第二种方案采用websocket将信息主动发送给客户端,客户端做后续的展示和处理工作;所以最终采用websocket。PHP实现websocket采用GatewayWorker,经过商业论证还是很稳定的。
创新互联公司主营哈密网站建设的网络公司,主营网站建设方案,app软件开发公司,哈密h5微信平台小程序开发搭建,哈密网站营销推广欢迎哈密等地区企业咨询
在用Go语言重构项目的时候,需要用Go重新搭建Websocket,去网上查了一些资料,利用 gorilla/websocket 在嵌套web框架就可以实现websocket,目前采用Go语言的iris Web框架,接下来就说说我是怎么实现的。
1.用{{}}包围的是变量,如 {{testName}} ,这表示把给定变量的值插入, {%%}这是块元素 在faygo里叫tag,常见的有 for , if 等
2.如何在模板中定义变量, 平常我们在使用的模板的时候的常会有这样的需要,在模板中要定义一个变量以方便前端逻辑的实现,在faygo模板中定义变量需要用到标签{%set%}
使用方法
{#定义变量 newName #}
{% set newName = "hello faygo" %}
{#获取变量newName的值#}
{{newName}}
定义用 tag set 取值就是上文所提到的{{}}取值
3.在模板中调用方法
这也是一个非常常见和有用的方法,在faygo中调用方法有两种方式 , 一是在渲染模板时在faygo.Map在加入你要调用的方法 , 二是注册一个全局的方法 (在faygo里叫filter过滤器),我们分别来看一下每个方法的实现
1) 在渲染模板时加入方法(render)
//在后端render时加入方法 testFunc
rErr := ctx.Render(200, switchDir+"index.html", faygo.Map{
"TITLE": title,
"testMap": map[string]string{"aaa": "111111"},
"testFunc": func(s string) string {
return s + " this is test func"
},
})
{#前端模板中调用#}
{{ testFunc("hello") }}
结果如下
hello this is test func
这种方法适合只用于此模板一个特殊方法 , 在其它功能中不通用 ,那么如果想定义一个方法全局都可以使用怎么办,这里就需要注册全局方法了(见下文)
2)注册全局方法(过滤器)
如果想定义一个方法全局都可以使用怎么办 ,这里就需要注册一个方法
// pongo2 注册一个全局过滤器,一般在程序启动时init中注册
//这里注册了一个名叫testFilter的过滤器,指向TestFilterFunc方法
pongo2.RegisterFilter("testFilter", TestFilterFunc)
func TestFilterFunc(in, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
a := in.String() + " this is global filter"
return pongo2.AsValue(a), nil
}
在这里我们看到TestFilterFunc方法里接收参数和返回参数的类型是pongo2.Value和pongo2.Error
在注册过滤器里方法的接收参数和返回参数是固定的这两个不能改变
官网的话:
All functions’ parameters types must be of either your own type or of type *pongo2.Value(no matter how many) and functions must return one value of either type *Value or your own one.
那么我们返回数据时怎么返回? 在上面例子在我们看到了 AsValue 这个方法可以将我们数据返回,我们可以返回struct,map,array,string 等
在前端调用
{{ "hello" | testFilter }}
结果:
hello this is global filter
返回结构体:
type LoginUserInfo struct {
Username string `json:"username"`
Telephone string `json:"telephone"`
Email string `json:"email"`
Level int `json:"level"`
}
func TestFilterFunc(in, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
userInfo := LoginUserInfo{
Username: "userA",
Telephone: "123456",
Email: "123456@test.com",
Level: 1,
}
return pongo2.AsValue(userInfo), nil
}
前端使用:
{#定义一个变量接收struct数据 #}
{% set uinfo = "" | testFilter %}
{#取用户名字#}
{{ uinfo.Username }}
注意,如是 uinfo 只是一个struct 不是struct数组([]uinfo)时 在模板中不能使用{% for %} 使用也不会得到任何数据
如果uinfo是struct数组 在模板中for循环时不要使用 key,val in uinfo
如果uinfo是struct数组 uinfo = []userInfo{}
{#错误示例#}
{% for key,val in uinfo %}
{{val.Username}}
{% endfor %}
struct数据不能使用key,否则循环会执行,但取不到任何数据
{# 正确示例 #}
{% for val in uinfo %}
{{val.Username}}
{% endfor %}
说一下返回map时 用for循环的情况,无论是否是map数组都可以用for key,val in uinfo 来遍历数据
4. 在模板中字符串的连接和宏标签的使用
在模板中有时我们会碰到这样的需要:在模板中有几个变量 ,我们想把这几个变量连接在一起赋值给另一个变量以做其它操作
例: 在模板中有三个变量 host是域名,route是路由地址,param是参数 ,要把这三个变量连接起来赋值给另一个新的变量做urlencode操作。这应该怎么办
因为在模板中使用 + 号连接变量时,程序会认为是数学运算,两个字符串的连接值为0, 如果用内置的filter: join来连接需要传入一个slice,但这三个只是字符串变量。
这个时候我们可能就要用到宏标签了% macro %% endmacro %.
思路是这样的,在宏标签中定义一个宏(可以理解为一个方法),这个宏接收三个参数(参数个数看需求而定),在宏内返回连接的字符串
代码:
{#定义三个变量#}
{% set host="" %}
{% set route="/aaa/bbb" %}
{% set param= "?id=123" %}
{#定义一个宏标签接收三个参数,并返回。注意在宏标签内如果换行,输出的结果中也会有换行,在urlencode的时候也会把换行符进行转义#}
{% macro joinUrl(paramA,paramB,paramC) %}{{paramA}}{{paramB}}{{paramC}}{% endmacro %}
hr
{#定义一个新变量调用宏方法,并将三个参数传入#}
{% set newurl = joinUrl(host,route,param) %}
{#输出newurl的值#}
{{newurl}}br
{#输入出urlencode后的字符串#}
{{newurl|urlencode}}br
结果:
http%3A%2F%2F
在宏标签在也可加入自定义的一些字符串如在上面的宏标签返回结果中要加一个固定字符可以这样写:
{% macro joinUrl(paramA,paramB,paramC) %}{{paramA}}{{paramB}}{{paramC}}from=macro{% endmacro %}
http请求的处理,相较于基本的net/http包,iris框架将http请求(w,*r)及其它上下文,封装成ctx,逐个调用handler闭包进行处理,最后分发函数返回值(反射)以及响应。请求响应相当于流水线上的一个商品,被一组当中的每个handler处理。路由注册的过程,在完成之前的方法解析,path解析之后,就需要组装一个handler链到路由了。
每一步先由api.relativePath和方法传入的参数构成fullPath。
第二步然后添加handler,顺序为:
第三步构建Route:
Name格式为defaultName := method + subdomain + 未注值的路径格式。其中路径有三个相关变量:Subdomain Path FormattedPath。FormattedPath是将path中的:变量替换为%v。构建路由Router时将路径解析的macroHandler,添加在最前面。
最后将路由注册到app.APIBuilder.routes,类型为[]Route。
iris框架定义了handler闭包的操作对象context.context。不是指TCP的context,也不是net/http中的context。数据结构如下:
前文已经提过,由于反射字段的不可复用性,造成go的反射效率下降。如果处理每次的请求都要在运行时重新构建context,就会降低性能。iris用c.pool保存ctx,实现ctx的可复用。c.pool的类型是sync.pool。id确定ctx的唯一性,方便保存在c.pool集合中。request writer都是net/http标准库的类型,直接传递。
params RequestParams 是一个专用的KV存储,类型为[string]interface{}。用来保存path param。values用来保存其它KV信息。
app引用自iris实例,可以获取全部的实例字段。其中路由信息单独拿出:currentRouteName路由名,对应c.routes的键。路由handlers,currentHandlerIndex。
ctx是可复用的,需要构建ctx时,不是初始化而是直接从池中拿一个实例。空实例仅配置了app字段。第一步包装net/http包的(w,*r)。第二步根据request确定路由信息,并赋到ctx相应字段。第三步会执行流水线式地Do(handlers),反射调用得到响应信息,并写入到ctx中。最后一步将针对特定http请求的信息清掉,并放回c.pool。
标准库net/http Response
IRIS中文文档
现在市面上的翻译都是老版本的,所以翻译了这个 github 上的 wiki 文档!