zoukankan      html  css  js  c++  java
  • Dubbo扩展点SPI简单实例

      dubbo采用微内核+插件机制方便框架使用者自行扩展,这个插件机制的实现就是JDK的SPI(参见Java的SPI简单实例)。dubbo扩展了JDK的SPI,加入了注解和Spring容器的支持,给配置文件中的全限定实现类添加了自定义名称映射,支持按不同的映射参数加载不同的实现类等。按dubbo官方说法,进化后的SPI叫扩展点SPI,ServiceLoader变成了ExtensionLoader。接下来看一个例子,使用扩展点SPI来自定义一个LoadBalance。

      先看dubbo源码中的LoadBalance接口:

    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package org.apache.dubbo.rpc.cluster;
    
    import org.apache.dubbo.common.URL;
    import org.apache.dubbo.common.extension.Adaptive;
    import org.apache.dubbo.common.extension.SPI;
    import org.apache.dubbo.rpc.Invocation;
    import org.apache.dubbo.rpc.Invoker;
    import org.apache.dubbo.rpc.RpcException;
    import org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
    
    import java.util.List;
    
    /**
     * LoadBalance. (SPI, Singleton, ThreadSafe)
     * <p>
     * <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
     *
     * @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
     */
    @SPI(RandomLoadBalance.NAME)
    public interface LoadBalance {
    
        /**
         * select one invoker in list.
         *
         * @param invokers   invokers.
         * @param url        refer url
         * @param invocation invocation.
         * @return selected invoker.
         */
        @Adaptive("loadbalance")
        <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
    
    }

      我们看到该接口使用了@SPI和@Adaptive注解,而且它们都有参数。@SPI用来标记扩展接口,@Adaptive使ExtesionLoader知道应该加载哪个扩展实现类。同ServiceLoader一样,ExtesionLoader也是主要的实现类:

        /**
         * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
         * will be thrown.
         */
        @SuppressWarnings("unchecked")
        public T getExtension(String name) {
            if (StringUtils.isEmpty(name)) {
                throw new IllegalArgumentException("Extension name == null");
            }
            if ("true".equals(name)) {
                return getDefaultExtension();
            }
            Holder<Object> holder = getOrCreateHolder(name);
            Object instance = holder.get();
            if (instance == null) {
                synchronized (holder) {
                    instance = holder.get();
                    if (instance == null) {
                        instance = createExtension(name);
                        holder.set(instance);
                    }
                }
            }
            return (T) instance;
        }
        @SuppressWarnings("unchecked")
        private T createExtension(String name) {
            Class<?> clazz = getExtensionClasses().get(name);
            if (clazz == null) {
                throw findException(name);
            }
            try {
                T instance = (T) EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                    instance = (T) EXTENSION_INSTANCES.get(clazz);
                }
                injectExtension(instance);
                Set<Class<?>> wrapperClasses = cachedWrapperClasses;
                if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                    for (Class<?> wrapperClass : wrapperClasses) {
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                    }
                }
                return instance;
            } catch (Throwable t) {
                throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                        type + ") couldn't be instantiated: " + t.getMessage(), t);
            }
        }

      下面自定义一个LoadBalance实现,扩展点实现起来非常简单。项目原代码参见一个spring boot集成dubbo的小例子,只需修改消费者,服务提供者不用动。下面重点把修改点列出来:

      1、消费者项目新增配置文件:resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance

    demo=com.example.dubbo.democonsumer.loadbalance.DemoLoadBalance

      2、消费者项目新增自定义LoadBalance实现:

    package com.example.dubbo.democonsumer.loadbalance;
    
    import org.apache.dubbo.common.URL;
    import org.apache.dubbo.rpc.Invocation;
    import org.apache.dubbo.rpc.Invoker;
    import org.apache.dubbo.rpc.RpcException;
    import org.apache.dubbo.rpc.cluster.LoadBalance;
    
    import java.util.List;
    
    public class DemoLoadBalance implements LoadBalance {
        @Override
        public <T> Invoker<T> select(List<Invoker<T>> list, URL url, Invocation invocation) throws RpcException {
            System.out.printf("DemoLoadBalance coming, url %s", url.toFullString());
            return list.get(0);
        }
    }

      3、消费者项目的Controller类新增LoadBalance注解:

    package com.example.dubbo.democonsumer.controller;
    
    import com.example.dubbo.demo.domain.DemoBean;
    import com.example.dubbo.demo.service.DemoService;
    import org.apache.dubbo.config.annotation.Reference;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * 服务消费者
     */
    @RestController
    public class ConsumerController {
    
        // 引入API
        @Reference(check = false, loadbalance = "demo")
        DemoService demoService;
    
        @ResponseBody
        @RequestMapping("/hello")
        public String sayHelo(@RequestParam(value = "msg") String msg) {
            return demoService.sayHelo(msg);
        }
    
        @ResponseBody
        @RequestMapping(value = "/login", method = RequestMethod.POST)
        public String login(DemoBean demoBean) {
            return demoService.login(demoBean);
        }
    }

      完事了。启动消费者和两个服务提供者(IDEA启动多实例参见IDEA同一项目启动多个实例),注意两个provider配置的rpc端口不能相同:

      浏览器输入消费者hello接口跑一次:

      

      从消费者日志可以看到进入我们自定义的LoadBalance:

  • 相关阅读:
    laravel tymon/jwt-auth header 发送token
    laravel migrate时报错:Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes
    laravel 报错 class not exist
    HTTP 状态码详解大全 | HTTP Status Codes
    JS,jquery 无法获取到动态插入的元素
    关键字
    PHP 配置默认SSL CA证书
    phpunit 配置
    plantuml 手册
    Windows使用RunHiddenConsole一键启动nginx,php-cgi服务
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/13286911.html
Copyright © 2011-2022 走看看