返回介绍

Channel Provider设计与代码实现 - 类ManagedChannelProvider

发布于 2020-09-14 13:14:41 字数 9103 浏览 1171 评论 0 收藏 0

ManagedChannelProvider 是 managed channel 的提供者,用于 transport 的不可知消费。

ManagedChannelProvider 的实现类不能抛出(异常)。如果抛出了,可能打断类装载。如果因为实现特有的原因会发生异常,实现应该优雅的处理异常并从 isAvailable() 方法返回 false 。

注:以上说明,来自 ManagedChannelProvider 的 Javadoc。

类定义

  1. package io.grpc;
  2. @Internal
  3. public abstract class ManagedChannelProvider {}

静态初始化

上面Javadoc的描述中提到的”类不能抛出异常,否则会打断类装载”,说的应该就是这个静态初始化的操作:

  1. private static final ManagedChannelProvider provider
  2. = load(getCorrectClassLoader());

getCorrectClassLoader()方法先获取正确的ClassLoader:

  1. private static ClassLoader getCorrectClassLoader() {
  2. if (isAndroid()) {
  3. //对于android平台,特殊一些
  4. return ManagedChannelProvider.class.getClassLoader();
  5. }
  6. //其他情况,默认都是取当前线程的Context ClassLoader
  7. return Thread.currentThread().getContextClassLoader();
  8. }

然后load()方法装载并选择ManagedChannelProvider的具体实现,这里有三个步骤:

  1. 装载所有可能的候选者
  2. 排除掉不可用的候选者
  3. 选择优先级最高的候选者

load()方法的具体代码:

  1. static ManagedChannelProvider load(ClassLoader classLoader) {
  2. Iterable<ManagedChannelProvider> candidates;
  3. if (isAndroid()) {
  4. // 对于android平台,直接hard code候选者
  5. candidates = getCandidatesViaHardCoded(classLoader);
  6. } else {
  7. // 其他情况,通过JDK的ServiceLoader装载候选者
  8. candidates = getCandidatesViaServiceLoader(classLoader);
  9. }
  10. List<ManagedChannelProvider> list = new ArrayList<ManagedChannelProvider>();
  11. for (ManagedChannelProvider current : candidates) {
  12. // 排除掉不可用的候选者
  13. if (!current.isAvailable()) {
  14. continue;
  15. }
  16. list.add(current);
  17. }
  18. if (list.isEmpty()) {
  19. // 如果为空返回null
  20. return null;
  21. } else {
  22. // 返回优先级最高的候选者
  23. return Collections.max(list, new Comparator<ManagedChannelProvider>() {
  24. @Override
  25. public int compare(ManagedChannelProvider f1, ManagedChannelProvider f2) {
  26. return f1.priority() - f2.priority();
  27. }
  28. });
  29. }
  30. }

逻辑很清晰,继续看其中的两个细节,看候选者是如何被装载出来的。

  1. android平台:hard code 两个可能的实现 okhttp 和 netty,如果类装载就只能忽略

    1. public static Iterable<ManagedChannelProvider> getCandidatesViaHardCoded(
    2. ClassLoader classLoader) {
    3. List<ManagedChannelProvider> list = new ArrayList<ManagedChannelProvider>();
    4. try {
    5. list.add(create(Class.forName("io.grpc.okhttp.OkHttpChannelProvider", true, classLoader)));
    6. } catch (ClassNotFoundException ex) {
    7. // ignore
    8. }
    9. try {
    10. list.add(create(Class.forName("io.grpc.netty.NettyChannelProvider", true, classLoader)));
    11. } catch (ClassNotFoundException ex) {
    12. // ignore
    13. }
    14. return list;
    15. }
  2. 普通平台:标准的JDK ServiceLoader 方式

  1. public static Iterable<ManagedChannelProvider> getCandidatesViaServiceLoader(
  2. ClassLoader classLoader) {
  3. return ServiceLoader.load(ManagedChannelProvider.class, classLoader);
  4. }

最重要的provider()方法

对于调用者来说,最重要的就是 provider()方法,因为通常调用者都是这样使用:

  1. ManagedChannelProvider provider = ManagedChannelProvider.provider();
  2. ......

provider()方法的实现很简单,判断一下静态变量 provider ,如果为空则抛出异常 ProviderNotFoundException,提示没有可用的 channel service provider:

  1. public static ManagedChannelProvider provider() {
  2. if (provider == null) {
  3. throw new ProviderNotFoundException("No functional channel service provider found. " + "Try adding a dependency on the grpc-okhttp or grpc-netty artifact");
  4. }
  5. return provider;
  6. }

和装载相关的抽象方法

在load()方法中,每个装载到的候选者,都需要实现这两个方法以便调用。

  1. isAvailable()

    用来判断这个provider是否可用,需要考虑当前环境。如果返回 false,则其他任何方法都不安全。

    1. protected abstract boolean isAvailable();

    实际在 load() 方法中,所有isAvailable()返回false的候选者都被直接排除。

  2. priority()

    优先级,每个provider可用的范围是从1到10,需要考虑当前环境。5被视为默认值,然后根据环境检测调整。优先级0 并不是暗示这个provider不工作,只是会排在最后。

    1. protected abstract int priority();

创建ChannelBuilder的抽象方法

ManagedChannelProvider 的主要功能,还是在于创建适当的 ManagedChannelBuilder,在 ManagedChannelBuilder 中定义了两个抽象方法要求子类做具体实现:

  1. builderForAddress(): 使用给定的host和端口创建新的builder

    1. protected abstract ManagedChannelBuilder< ?> builderForAddress(String name, int port);
  2. builderForTarget(): 使用给定的target URI来创建新的builder

    1. protected abstract ManagedChannelBuilder< ?> builderForTarget(String target);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文