Iris - 子域

Iris 2020-01-18 7119 字 1261 浏览 点赞

起步

子域一词语是我学 flask 首次接触到的,当时书上说了这么个功能,没有细讲,于是我长久不知道它是做啥的。再后来一路磕磕碰碰,像收集龙珠似的找到些子域的意义,今天就和着 Iris 的 Subdomain 来说说吧。

什么是子域

常见如 baidu.com 就是一个域名。域名等级是从右往左,com 是顶级域名,baidu 是二级域名。baidu.com 的子域名就可以是 a.baidu.com、b.baidu.com 之类。百度百科的域名就是子域应用的典型案例:baike.baidu.com。

子域:破除浏览器限制

事实上,不同浏览器都会有一个限制,就是对同一个域名并发请求数的限制。我们可以通过实验来理解这一现象。

准备图片素材

创建目录 img,随意放入一张 jpg 格式的图片,建议图片大小不低于 500k,这样有利于后面的观察。我还写了一个简单脚本,用来批量生成图片:

file=$1

for i in {1..30}; do
    if [ $i -lt 10 ]; then
        i="0$i"
    fi
    cp $file "$i.jpg"
done

用法:

# generate.sh 是脚本名
# origin.jpg 是 img 目录下的图片名
$ sh generate.sh origin.jpg

脚本成功运行后应该会有:01.jpg、02.jpg ……30.jpg 这样的图片产生。

准备 html 文件

在项目根目录下创建目录 html,在 html 目录下创建 index.html 文件。写入内容如下:

<!-- index.html -->
...
<body>
  <img src="http://ying.com:8080/static/01.jpg" />
  <img src="http://ying.com:8080/static/02.jpg" />
  <img src="http://ying.com:8080/static/03.jpg" />
  ...
  <img src="http://ying.com:8080/static/29.jpg" />
  <img src="http://ying.com:8080/static/30.jpg" />
</body>

利用 Iris 构建后台代码:

func main() {
    app := iris.Default()

    // 注册 html 文件
    htmlEng := iris.HTML("html", ".html")
    htmlEng.Reload(true) // 开启自动加载
    app.RegisterView(htmlEng)

    app.Get("/", func(ctx iris.Context) {
        ctx.View("index.html")
    })

    // 静态文件的视图函数
    app.Get("/static/{file}", func(ctx iris.Context) {
        file := ctx.Params().Get("file")
        // 构建图片路径
        filepath := strings.Join(
            []string{".", "img", file}, 
            string(os.PathSeparator),
        )
        fd, _ := os.Open(filepath)
        defer fd.Close()
        // 读取图片所有内容
        stream, _ := ioutil.ReadAll(fd)
        ctx.Write(stream)
    })

    app.Run(iris.Addr(":8080"))
}

由于是本地测试,还需要修改 hosts 文件。我现在手上的是 mac 电脑,hosts 文件在 /etc 目录下(win 电脑不同,请自行百度)。在 hosts 文件中追加:

127.0.0.1 ying.com

设置 chrome 浏览器

请根据下图对 chrome 浏览器进行配置。
Alt text

  • Fast 3G 的意思是将环境设置为 3g 网速,这样有利于我们观察实验现象。

观察现象

现在浏览器访问:http://ying.com:8080

在 network 中可以观察到,图片的请求总是 6 个为一组。你可以刷新浏览器多尝试几次。
Alt text

现在在 waterfall 右边右键鼠标,开启 Domain,也就是显示每次浏览器请求的域名。
Alt text

修改 index.html 文件。修改内容如下:

<body>
  <img src="http://z.ying.com:8080/static/01.jpg" />
  ...
  <img src="http://z.ying.com:8080/static/10.jpg" />
  
  <img src="http://t.ying.com:8080/static/11.jpg" />
  ...
  <img src="http://t.ying.com:8080/static/20.jpg" />
  
  <img src="http://y.ying.com:8080/static/21.jpg" />
  ...
  <img src="http://y.ying.com:8080/static/30.jpg" />
</body>
  • 01.jpg - 10.jpg 对应域名 z.ying.com
  • 11.jpg - 20.jpg 对应域名 t.ying.com
  • 21.jpg - 30.jpg 对应域名 y.ying.com

在 hosts 文件中追加内容如下:

127.0.0.1 z.ying.com
127.0.0.1 t.ying.com
127.0.0.1 y.ying.com

浏览器继续访问:http://ying.com:8080。可以观察到的现象是,每个域名下都会有 6 个请求。
Alt text

现在结论就出来了,尽管访问的是同一 IP,但通过不同域名,解决了浏览器最大并发请求数的限制问题。诚然,我们没有真正做到破除浏览器的限制,但实现了曲线救国。

子域:url 模块化

当系统庞大到需要模块化时,一般会用 url path 对 url 进行模块划分。假设域名是 ying.com,告警模块相关 url 可以是 ying.com:8080/alert/...,用户中心相关 url 可以是 ying.com:8080/user/...。虽说没什么问题,但似乎不太优雅(个人主观意识),如果 url 划分方式是 alert.ying.com,user.ying.com 感觉会好许多。另一方面,借助二级域名,实现模块化多机部署要简单许多。

静态子域

静态子域表示知道子域的具体名称,如 baike.baidu.com 子域部门就是 baike。Iris 实现子域的方式有两种,第一种是 Subdomain 方法。

Subdomain(subdomain string, middleware ...Handler) Party
  • subdomain 表示子域名称。
  • middleware 表示子域匹配成功后的操作,每个 middleware 中必须有 ctx.Next(),否则不能正常匹配之后的路由。可以省略。

现在实现 alert.ying.com 与 user.ying.com 子域访问。

func main() {
    app := iris.Default()

    alertSubdomain := app.Subdomain("alert")
    userSubdomain := app.Subdomain("user")
    // 系统首页
    app.Get("/", func(ctx iris.Context) {
        ctx.HTML("ying index")
    })
    // 告警模块首页
    alertSubdomain.Get("/", func(ctx iris.Context) {
        ctx.HTML("alert module")
    })
    // 用户模块首页
    userSubdomain.Get("/", func(ctx iris.Context) {
        ctx.HTML("user module")
    })

    app.Run(iris.Addr(":8080"))
}

hosts 文件追加以下内容:

127.0.0.1 ying.com
127.0.0.1 alert.ying.com
127.0.0.1 user.ying.com

现在,你可以通过访问以下链接来感受区别:

第二种实现子域方式需要借助 Party。Party 方法在讲 分组路由 时提到过,用它来实现子域仅需要 path + "." 即可。上述代码可修改成如下:

...
alertSubdomain := app.Party("alert.")
userSubdomain := app.Party("user.")
...

事实上,在 Iris 源码中可以看到,Subdomain 方法也是借助 Party 实现的。

// Iris 源码
func (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler) Party {
    ...
    if l := len(subdomain); l < 1 {
        return api
    } else if subdomain[l-1] != '.' {
        // "user" -> "user."
        subdomain += "."
    }

    return api.Party(subdomain, middleware...)
}

动态子域

又叫做通配符子域。例如前面说到的 alert.ying.com 与 user.ying.com,对静态子域来讲它们不是一类域名,但对动态子域来讲它们是一类域名。最常见的应用就是 github 上挂载静态博客,在我的账号上自动生成域名 youguanxinqing.github.io,一位朋友则是 tflins.github.io 。

借助 Iris 我们可以实现这一特性。

func main() {
    app := iris.Default()

    userSubdomain := app.WildcardSubdomain()
    // *.ying.com:8080
    userSubdomain.Get("/", func(ctx iris.Context) {
        // 获取子域名称
        subdomain := ctx.Subdomain()
        html := fmt.Sprintf("hello, %v", subdomain)
        ctx.HTML(html)
    })
    app.Run(iris.Addr(":8080"))
}

hosts 文件追加内容如下:

127.0.0.1 tflins.ying.com
127.0.0.1 youguanxinqing.ying.com
  • 子域名必须在 hosts 文件中配置过才能正常访问。

Party 依然是可以替代 WildcardSubdomian() 的存在。

// = app.WildcardSubdomain()
userSubdomain := app.Party("*.")

尽管 WildcardSubdomain 也是通过 Party 实现功能,从性能上说,直接调用 Party 也会更快,但不论是 Subdomain 还是 WildcardSubdomain 中都有一些校验逻辑用来保护程序。因此最好是该静态就用 Subdomain,该动态就用 WildcardSubdomain。

域名重定向

在访问百度时,我们发现不论是 baidu.com 还是 www.baidu.com 跳转到的都是同一个界面,这对 Iris 框架来说 so easy。

现在我们把所有请求 http://ying.com:8080/path?query 都重定向到 http://www.ying.com:8080/path?query。代码如下:

app := iris.Default()
www := app.Subdomain("www")
// http://ying.com:8080 -> http://www.ying.com:8080
www.Get("/", func(ctx iris.Context) {
    ctx.HTML("www index")
})
// http://ying.com:8080/user -> http://www.ying.com:8080/user
www.Get("/user", func(ctx iris.Context) {
    ctx.HTML("www user")
})
// 重定向
app.SubdomainRedirect(app, www)
// 必须显示绑定域名
app.Run(iris.Addr("ying.com:8080"))
  • 函数签名:SubdomainRedirect(from, to Party) Party
  • from 可以是动态域,但 to 不可以(app.SubdomainRedirect(app.WildcardSubdomain(), www))。
  • to 不是根域(app)时,from 可以是根域。反之依然。

hosts 文件追加如下内容:

127.0.0.1 ying.com
127.0.0.1 www.ying.com

感谢

Note:此次实验中修改 hosts 文件,当不用时应该还原,避免真实存在的网站不能正常访问。



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论