在对hadoop进行任何操作之前,我应该调用ugi.checkTGTAndReloginFromKeytab()吗? - java

在服务器应用程序中,我正在从Java应用程序连接到Kerberos安全的Hadoop群集。我正在使用HDFS文件系统,Oozie,Hive等各种组件。在应用程序启动时,我确实打电话给

UserGroupInformation.loginUserFromKeytabAndReturnUGI( ... );

这将返回我UserGroupInformation实例,并在应用程序生存期内保留它。当执行特权操作时,我使用ugi.doAs(action)启动它们。

这可以正常工作,但是我想知道是否以及何时应该更新UserGroupInformation中的kerberos票?我找到了一种方法UserGroupInformation.checkTGTAndReloginFromKeytab(),该方法似乎在快要到期时都会进行票证更新。我还发现,例如WebHdfsFileSystem之类的各种Hadoop工具正在调用此方法。

现在,如果我希望我的服务器应用程序(可能运行数月甚至数年)从未经历过票证到期,那么最佳方法是什么?提供具体问题:

是否可以在需要时依赖它们调用checkTGTAndReloginFromKeytab的各种Hadoop客户端?
我应该在代码中称呼自己checkTGTAndReloginFromKeytab吗?
如果是这样,我应该在每次调用ugi.doAs(...)之前执行此操作,还是设置一个计时器并定期(多长时间一次)调用一次?

参考方案

Hadoop提交者在这里!这是一个很好的问题。

不幸的是,如果不深入研究应用程序的特定使用模式,很难给出明确的答案。相反,我可以提供一般准则,并描述Hadoop何时为您自动处理票证更新或从keytab重新登录,何时不这样做。

Hadoop生态系统中Kerberos身份验证的主要用例是Hadoop的RPC框架,该框架使用SASL进行身份验证。 Hadoop生态系统中的大多数守护进程都通过在进程启动时对UserGroupInformation#loginUserFromKeytab进行一次一次性调用来处理此问题。这样的示例包括HDFS DataNode和YARN NodeManager,它们必须对对NameNode的RPC调用进行身份验证,而YARN NodeManager必须对对ResourceManager的调用进行身份验证。像DataNode这样的守护程序如何在进程启动时进行一次登录,然后继续运行数月,这比典型的票证到期时间还长呢?

由于这是一个常见的用例,因此Hadoop直接在RPC客户端层内部实现自动重新登录机制。此代码在RPC Client#handleSaslConnectionFailure方法中可见:

          // try re-login
          if (UserGroupInformation.isLoginKeytabBased()) {
            UserGroupInformation.getLoginUser().reloginFromKeytab();
          } else if (UserGroupInformation.isLoginTicketBased()) {
            UserGroupInformation.getLoginUser().reloginFromTicketCache();
          }

您可以将其视为重新登录的“惰性评估”。它仅在尝试进行RPC连接时由于身份验证失败而重新执行登录。

知道这一点,我们可以给出部分答案。如果您的应用程序使用模式是从密钥表登录,然后执行典型的Hadoop RPC调用,那么您可能不需要滚动自己的重新登录代码。 RPC客户端层将为您完成此任务。 “典型的Hadoop RPC”表示用于与Hadoop交互的绝大多数Java API,包括HDFS FileSystem API,YarnClient和MapReduce Job提交。

但是,某些应用程序使用模式根本不涉及Hadoop RPC。例如,仅与Hadoop的REST API交互的应用程序,例如WebHDFS或YARN REST APIs。在这种情况下,身份验证模型将通过Hadoop HTTP Authentication文档中所述的SPNEGO使用Kerberos。

知道这一点,我们可以在答案中添加更多内容。如果您的应用程序的使用模式根本不使用Hadoop RPC,而是仅使用REST API,那么您必须滚动自己的重新登录逻辑。正如您所注意到的,这正是WebHdfsFileSystem calls UserGroupInformation#checkTGTAndReloginFromkeytab的原因。 WebHdfsFileSystem选择在每次操作前进行呼叫。这是一个很好的策略,因为UserGroupInformation#checkTGTAndReloginFromkeytab only renews the ticket if it's "close" to expiration.否则,该呼叫为空操作。

作为最终用例,让我们考虑一个交互式过程,而不是从密钥表登录,而是要求用户在启动应用程序之前在外部运行kinit。在大多数情况下,这些应用程序将是短期运行的应用程序,例如Hadoop CLI命令。但是,在某些情况下,这些过程可能是运行时间更长的过程。为了支持运行时间更长的流程,Hadoop启动了一个后台线程来将Kerberos票证“关闭”更新为到期。此逻辑在UserGroupInformation#spawnAutoRenewalThreadForUserCreds中可见。与RPC层中提供的自动重新登录逻辑相比,这里有一个重要的区别。在这种情况下,Hadoop仅具有续签和延长其寿命的功能。根据Kerberos基础结构的规定,票证具有最大的可更新生存期。之后,该票将不再可用。在这种情况下,重新登录实际上是不可能的,因为这将暗示重新提示用户输入密码,并且他们很可能已离开终端。这意味着,如果该进程持续运行到票证到期之后,它将无法再进行身份验证。

同样,我们可以使用此信息来告知我们的总体答案。如果您在启动应用程序之前依靠用户通过kinit进行交互式登录,并且您确信应用程序的运行时间不会超过Kerberos票证的最大可再生寿命,那么您可以依靠Hadoop内部构件来进行定期更新为了你。

如果您使用的是基于keytab的登录,并且只是不确定应用程序的使用模式是否可以依赖Hadoop RPC层的自动重新登录,那么保守的方法是自行滚动。 @SamsonScharfrichter在这里给自己一个很好的答案。

HBase Kerberos connection renewal strategy

最后,我应该添加有关API稳定性的注释。 Apache Hadoop Compatibility指南详细讨论了Hadoop开发社区对向后兼容的承诺。 UserGroupInformation的界面带有LimitedPrivateEvolving注释。从技术上讲,这意味着UserGroupInformation的API不被认为是公共的,并且可能以向后不兼容的方式发展。实际上,已经有很多代码取决于UserGroupInformation的接口,因此对我们来说,进行重大更改根本不可行。当然,在当前的2.x发行版中,我不会担心方法签名会从您的下面更改并破坏您的代码。

现在,我们已经掌握了所有这些背景信息,让我们重新讨论您的具体问题。

是否可以在需要时依赖它们调用checkTGTAndReloginFromKeytab的各种Hadoop客户端?

如果您的应用程序的使用模式是调用Hadoop客户端,那么您可以依靠它,而Hadoop客户端又利用Hadoop的RPC框架。如果您的应用程序的使用模式仅调用Hadoop REST API,则您不能依靠它。

我应该在自己的代码中自称checkTGTAndReloginFromKeytab吗?

如果您的应用程序的使用模式仅是调用Hadoop REST API而不是Hadoop RPC调用,则可能需要执行此操作。您将无法从Hadoop的RPC客户端内部实现的自动重新登录中受益。

如果是这样,我应该在每次调用ugi.doAs(...)之前执行此操作,还是设置一个计时器并定期(多长时间一次)调用一次?

可以在需要验证的每个操作之前立即调用UserGroupInformation#checkTGTAndReloginFromKeytab。如果票证即将到期,则该方法将为无操作。如果您怀疑Kerberos基础结构缓慢,并且您不希望客户端操作支付重新登录的延迟成本,那么这就是在单独的后台线程中进行此操作的原因。只要确保在票证的实际到期时间之前提前一点时间即可。您可以借用UserGroupInformation中的逻辑来确定故障单是否“接近”到期。在实践中,我从未亲自看到重新登录的延迟有问题。

您可以在不映射到字符串的情况下收集(joining())吗? - java

我正在阅读实际使用的Java 8,作者说,如果您有一个覆盖toString方法的类,则在执行collect(joining())时无需将流映射到String。一个例子: public static void main(String... args) { List<Person> people = Arrays.asList( new Person…

Java:线程池如何将线程映射到可运行对象 - java

试图绕过Java并发问题,并且很难理解线程池,线程以及它们正在执行的可运行“任务”之间的关系。如果我创建一个有10个线程的线程池,那么我是否必须将相同的任务传递给池中的每个线程,或者池化的线程实际上只是与任务无关的“工人无人机”可用于执行任何任务?无论哪种方式,Executor / ExecutorService如何将正确的任务分配给正确的线程? 参考方案 …

JAVA:字节码和二进制有什么区别? - java

java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java:BigInteger,如何通过OutputStream编写它 - java

我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…