Istio Mixer Cache工作原理与源码分析part4-签名

本文转载自敖小剑的博客

接前文,继续分析Mixer Check Cache的源码,这次的重点是签名算法,也就是Referenced::Signature()方法。

前情回顾:

  • Referenced保存的是mixer adapter使用的引用属性的一个组合,也就是前面例子中的 “a,b,c”或者“a,b,c不存在”
  • Referenced中有两个数据结构: std::vector<AttributeRef> absence_keys_std::vector<AttributeRef> exact_keys_,exact_keys_保存的是一定要出现的属性, absence_keys_中保存的是没有出现的属性
  • 基本流程

    我们来看详细源代码,具体在文件src/istio/mixerclient/referenced.cc中,代码的基本流程非常清晰:

    bool Referenced::Signature(const Attributes &attributes,
                               const std::string &extra_key,
                               std::string *signature) const {
      // 第一步,先检查输入是否匹配保存的引用属性
      // 必须同时满足absent key和exact key的要求
      if (!CheckAbsentKeys(attributes) || !CheckExactKeys(attributes)) {
        return false;
      }
    
      // 发现匹配之后,才开始计算签名
      CalculateSignature(attributes, extra_key, signature);
      return true;
    }
    

    切记:请更新到 istio/proxy 仓库的最新代码,在master分支上才能看到这个版本。

    这里的代码在此之前是存在性能问题的,我为此提交了一个改进方案,由于0.8版本发布前锁了master分支,因此这个fix的代码是在0.8版本发布之后才进的master分支。

    详情请见:https://github.com/istio/proxy/issues/1531

    引用属性匹配

    先检查absent key,这里要求请求中的属性,不能出现 absencekeys 保存的属性,否则就是不匹配:

    bool Referenced::CheckAbsentKeys(const Attributes &attributes) const {
      const auto &attributes_map = attributes.attributes();
      for (std::size_t i = 0; i < absence_keys_.size(); ++i) {
        // 检查每个absence_key
        const auto &key = absence_keys_[i];
        const auto it = attributes_map.find(key.name);
        if (it == attributes_map.end()) {
          // 如果在输入的属性中没有找到,就继续下一个
          continue;
        }
    
        // 如果找到了,则直接返回不匹配
        return false;
        // 实际代码中还有特别的 StringMap 类型的属性需要额外处理,简单起见我们忽略它
      }
      // 只有absence_key都没有在输入的属性中出现,才表示匹配
      return true;
    }
    

    再检查exact keys,这里要求exact keys中保存的每一个属性,必须在请求中出现,否则就是不匹配:

    bool Referenced::CheckExactKeys(const Attributes &attributes) const {
      const auto &attributes_map = attributes.attributes();
      for (std::size_t i = 0; i < exact_keys_.size(); ++i) {
        // 检查每个exact_key
        const auto &key = exact_keys_[i];
        const auto it = attributes_map.find(key.name);
        // 如果没有在请求中出现就返回不匹配
        if (it == attributes_map.end()) {
          return false;
        }
    	// 实际代码中还有特别的 StringMap 类型的属性需要额外处理,简单起见我们忽略它
        }
      }
      // 只有exact_key都在输入的属性中出现,才表示匹配
      return true;
    }
    

    简单说,引用属性匹配的要求就是:exact key都必须出现,absence key都不能出现。

    输入 exact=“a,b,c”,absent=“” exact=“a,b”,absent=“c”
    “a=1,b=2,c=3,e=4,f=5” Yes No
    “a=1,b=2,e=4,f=5” No Yes

    计算签名

    在exact key和absent key检查通过之后,就意味着请求中的属性满足当前Referenced的匹配要求。

    下一步就可以进行签名计算了,CalculateSignature()方法的参数中attributes是输入的所有属性,extra_key这个参数目前没有使用,忽略即可:

    void Referenced::CalculateSignature(const Attributes &attributes,
                                        const std::string &extra_key,
                                        std::string *signature) const {
      const auto &attributes_map = attributes.attributes();
    
      utils::MD5 hasher;
      // 游历exact_keys_ 中的每个属性
      for (std::size_t i = 0; i < exact_keys_.size(); ++i) {
        const auto &key = exact_keys_[i];
        // 在输入的属性中通过属性名找到包含值的属性
        const auto it = attributes_map.find(key.name);
    
        hasher.Update(it->first);
        hasher.Update(kDelimiter, kDelimiterLength);
    
        // 根据属性值的不同类型,调用hasher.Update()方法进行计算
        const Attributes_AttributeValue &value = it->second;
        switch (value.value_case()) {
          case Attributes_AttributeValue::kStringValue:
            hasher.Update(value.string_value());
            break;
          ......// 忽略其他类型的处理代码
          case Attributes_AttributeValue::VALUE_NOT_SET:
            break;
        }
        hasher.Update(kDelimiter, kDelimiterLength);
      }
      hasher.Update(extra_key);
    
      // 完成签名计算的最后一步,得到签名
      *signature = hasher.Digest();
    }
    

    即CalculateSignature()方法会将exactkeys 指定的请求属性进行签名,注意只对 exactkeys 的属性进行签名,absent key反正没有出现自然无需也无法对它们进行计算。

    形象起见,以我们前面介绍基础概念和工作原理时的例子做讲解,假设 referenced_map 保存的引用属性组合为 {“k1”: “a,b,c”, “k2”: “a,b,c不存在” }

    请求 和请求匹配的引用属性 进行签名计算的实际属性值
    “a=1,b=2,c=3,e=4,f=5” exact=“a,b,c”, absent=“” a=1,b=2,c=3
    “a=1,b=2,e=4,f=5” exact=“a,b”, absent=“c” a=1,b=2

    总结

    签名算法的关键在于需要先匹配exact key和absent key,然后再计算。和主流程代码一样,只要理解了引用属性和absent key的概念,就容易理解了。