【golang】海量数据去重-布隆过滤器

Python012

【golang】海量数据去重-布隆过滤器,第1张

在做域名爆破中,遇到了把一个300G的子域名json文件进行去重,一开始是考虑使用字典进行去重,但是数据量大了,会造成内存泄露。看网上资料介绍了一种方案,就是使用布隆过滤器。

布隆过滤器是一种数据结构,概率型数据结构,特定是高效插入和查询,可以用来告诉你“某一值一定不存在或者kennel存在”。

相比于传统的map、set等数据结构,占用空间更少,但其返回结果是概率型的,不确定。

布隆过滤器内部维护一个bitArray(位数组),开始所有数据为0,当一个元素过来时,能过多个哈希函数(hash1、hash2、hash3)计算不同的hash值,并通过hash值找到bitArray的下标,将里面的值改为由0变为1。布隆过滤器有一个误判率,误判率越低,数组越长,所在空间越大,误判率越高,数组越小,所占空间越小。

这里贴上一个技术大牛的博客地址,里面对布隆过滤器用法以及在redis里面处理缓存穿透问题的详细介绍。

https://www.cnblogs.com/yscl/p/12003359.html

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: "[email protected]",

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="http://www.baidu.com" %}

{% 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://www.baidu.com/aaa/bbb?id=123

http%3A%2F%2Fwww.baidu.com%2Faaa%2Fbbb%3Fid%3D123

在宏标签在也可加入自定义的一些字符串如在上面的宏标签返回结果中要加一个固定字符可以这样写:

{% macro joinUrl(paramA,paramB,paramC) %}{{paramA}}{{paramB}}{{paramC}}&from=macro{% endmacro %}