# 客户端虚拟路径映射

## 概述

Alluxio 支持将一个路径映射成另一个路径，所有的读写请求以及元数据操作请求将不会在原始路径上执行，而是会转发到映射后的路径。

当前客户端虚拟路径映射仅仅适用与 Alluxio Fuse 与 Alluxio Cli.

## 启用虚拟路径映射

```properties
alluxio.user.virtual.path.mapping.enabled=true
alluxio.user.virtual.path.mapping.rule.file.path=/opt/alluxio/conf/path_mapping.json
```

`path_mapping.json` 是一个 json 文件，用于描述路径之间的映射关系。 这里举一个简单的例子，比如将 `/a/b/c/xxx` 映射成 `/b/c/d/xxx`，则可以进行如下配置：

```json
{
  "rules": [
    {
      "src": "^/a/b/c/(.*)$",
      "dst": "/b/c/d/{{ var1 }}"
    }
  ]
}
```

这里 src 表示原始路径的正则表达式，括号用于取值，取到的值可以在 dst 中使用，用 var 来表示： var1 表示第一个括号里面的值，var2 表示第二个括号里面的值，以此类推，varN 表示第 N 个括号里的值。

下面我们举一个例子，比如我们想要实现以下路径之间的映射关系：

```
/foo/bar/[a-b].*  ------> /foo1/bar1/A/xxx
/foo/bar/[e-g].*  ------> /foo1/bar1/B/xxx
/foo/bar/[\d].*   ------> /foo1/bar1/C/xxx
/foo/bar/[acd].*  ------> /foo1/bar1/D/xxx
```

配置文件可以这样写，注意在 json 文件中写 `\` 符号时，需要写成 `\\`，因为 `\` 是 json 的转义字符：

```json
{
  "rules": [
    {
      "src": "^/foo/bar/([a-b].*)$",
      "dst": "/foo1/bar1/A/{{ var1 }}"
    },
    {
      "src": "^/foo/bar/([e-g].*)$",
      "dst": "/foo1/bar1/B/{{ var1 }}"
    },
    {
      "src": "^/foo/bar/([\\d].*)$",
      "dst": "/foo1/bar1/C/{{ var1 }}"
    },
    {
      "src": "^/foo/bar/([acd].*)$",
      "dst": "/foo1/bar1/D/{{ var1 }}"
    }
  ]
}
```

注意：**路径映射规则在匹配时，是从上到下逐条进行匹配，只有第一个匹配到的规则生效；如果路径没有匹配到任何规则，原始路径就是映射后的路径。**

## 路径映射测试工具

我们提供了命令行工具来校验路径映射规则是否符合预期，强烈建议在上线之前使用此工具进行验证。

```bash
bin/alluxio fs pathmapping /foo/bar/a.txt

-----------------------------------------------------------------
Input:  /foo/bar/a.txt
Output: /foo1/bar1/A/1.txt
Virtual Directory: false
-----------------------------------------------------------------
```

这里 Output 字段表示映射后的路径，如果输入路径没有匹配到规则，Output 的值将为 null。 Virtual Directory 字段的含义将在下面的章节解释。

## Fuse 路径映射（非常重要）

虚拟路径映射配置中，路径都是 Alluxio 的集群路径，对应到 Alluxio Fuse 的本地路径时，需要考虑到 Alluxio Fuse 在本地的挂载路径。

以如下规则为例：

```json
{
  "rules": [
    {
      "src": "^/a/b/(.*)$",
      "dst": "/a1/b1/{{ var1 }}"
    }
  ]
}
```

下面的命令是等价的，因为我们将 `/a/b/c` 映射成 `/a1/b1/c`

```bash
bin/alluxio fs ls /a/b/c
bin/alluxio fs ls /a1/b1/c
```

假设 Alluxio Fuse 在本地的挂载路径为 `/mnt/alluxio/alluxio-fuse`，以上规则实际上是将 `/mnt/alluxio/alluxio-fuse/a/b/c` 映射为 `/mnt/alluxio/alluxio-fuse/a1/b1/c`，因此下面的命令是才是等价的：

```bash
ls /mnt/alluxio/alluxio-fuse/a/b/c
ls /mnt/alluxio/alluxio-fuse/a1/b1/c
```

## 虚拟目录

在 Alluxio Fuse 里访问一个文件时，Alluxio Fuse 会检查这个文件的父路径是否存在，父路径不存在则会报错。

以如下规则为例：

```json
{
  "rules": [
    {
      "src": "^/a/b/(.*)$",
      "dst": "/a1/b1/{{ var1 }}"
    }
  ]
}
```

我们读取 `/a/b/c` 时，Alluxio Fuse 会检查父路径 `a/b` 是否存在，如果 `/a/b` 不存在，则会报错。 这个检查与映射后的路径 `/a1/b1` 是否存在没有任何关系。为了避免这种情况，我们可以配置虚拟目录：

```json
{
  "rules": [
    {
      "src": "^/a/b/(.*)$",
      "dst": "/a1/b1/{{ var1 }}"
    }
  ],
  "virtualDirectories": [
    "/a", "/a/b"
  ]
}
```

`virtualDirectories` 字段可以填写多个正则表达式，按填写的顺序进行匹配，匹配 `virtualDirectories` 的路径会始终认为它存在并且被当做一个目录。 配置的虚拟目录也能用命令行进行校验：

```bash
bin/alluxio fs pathmapping /a
-----------------------------------------------------------------
Input:  /a
Output: null
Virtual Directory: true
-----------------------------------------------------------------
```

其中输出的 `Virtual Directory` 代表路径是否匹配虚拟目录。

## 限制

### 规则数量限制

虚拟路径映射不适合有大量规则的场景，因为逐条匹配正则表达式将会影响性能。

### ls 限制

在 ls 一个目录时，不能看到所有匹配的子路径。

这里举例说明，对于这个映射关系：

```json
{
  "rules": [
    {
      "src": "^/foo/bar/([a-b].*)$",
      "dst": "/foo1/bar1/A/{{ var1 }}"
    },
    {
      "src": "^/foo/bar/([e-g].*)$",
      "dst": "/foo1/bar1/B/{{ var1 }}"
    },
    {
      "src": "^/foo/bar/([\\d].*)$",
      "dst": "/foo1/bar1/C/{{ var1 }}"
    },
    {
      "src": "^/foo/bar/([acd].*)$",
      "dst": "/foo1/bar1/D/{{ var1 }}"
    }
  ]
}
```

在 `ls /foo/bar` 时，看不到：

```bash
/foo1/bar1/A
/foo1/bar1/B
/foo1/bar1/C
/foo1/bar1/D
```

但是可以直接访问 `/foo/bar` 目录内的内容，比如可以 `cat /foo1/bar1/A/1.txt`。
