• Spring Cloud 之 Zuul网关集成Dubbo泛化调用(十八)


    PS:源码已上传Github, 欢迎指教。https://github.com/shileishmily/spring-cloud-x.git

    之前已经讲过zuul网关搭建和网关Filter使用,参考下面两篇文章。
     
    本篇主要Zuul和Dubbo如何集成,因为笔者所在的公司既有Dubbo服务,又有SpringCloud服务。对外(公网)提供服务的时候,不可能把dubbo服务再改造成Http协议,一是成本大,二是风险高。所以我们采用Zuul网关实现SpringCloud和Dubbo服务分发。在Zuul网关之上再挂Nginx对外提供服务。
     
     
    本篇演示离不开zookeeper,zkui,Dubbo Admin。关于这三个应用的安装请参考下方链接,本文不做重点介绍。
    zookeeper,zkui安装参考:Zookeeper 注册中心安装
    Dubbo Admin安装参考:https://github.com/apache/dubbo-admin
     
    1、Dubbo接口定义
    在x-demo-service-api模块新建一个HelloDubboService接口
    package com.x.demo.api;
    
    public interface HelloDubboService {
        void sayHello();
    
        String sayHi(String name);
    }

    2、创建一个名称为x-demo-dubbo-provider的模块

    3、gradle依赖

    dependencies {
        compile project(":x-demo-service-api")
    
        compile(group: 'org.apache.dubbo', name: 'dubbo-spring-boot-starter', version: '2.7.8') {
            exclude(module: 'slf4j-log4j12')
        }
    
        compile(group: 'org.apache.dubbo', name: 'dubbo-dependencies-zookeeper', version: '2.7.8') {
            exclude(module: 'slf4j-log4j12')
        }
    }

    4、bootstrap.yml配置文件

    server:
      port: 6060
    
    dubbo:
      application:
        name: x-demo-dubbo-provider
      registry:
        address: zookeeper://localhost:2181
        protocol: zookeeper
      protocol:
        name: dubbo
        port: 20880
      scan:
        base-packages: com.x.dubbo.provider.service
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
        enabled: false

    5、创建HelloDubboService实现类,代码如下

    这里我们采用注解声明式配置,实现类上加@DubboService

    /**
     * @author Leo
     */
    @Slf4j
    @DubboService(version = "1.0.0", interfaceClass = HelloDubboService.class, methods = {
            @Method(name = "sayHello")
    })
    public class HelloDubboServiceImpl implements HelloDubboService {
        @Override
        public void sayHello() {
            log.info("Hello");
        }
    
        @Override
        public String sayHi(String name) {
            log.info("Hello: {}", name);
    
            return "Hello: " + name;
        }
    }

    6、创建启动类

    注意必须加@EnableDubbo注解,否则服务无法注册到zookeeper。

    /**
     * @author Leo
     */
    @EnableDubbo
    @SpringBootApplication
    public class DubboProviderApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DubboProviderApplication.class, args);
        }
    }

    到此dubbo服务提供者完成。下面开始改造spring-cloud-gateway模块

    7、创建路由Filter

    package com.x.gateway.zuul.filter;
    
    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.parser.Feature;
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import com.x.gateway.zuul.util.DubboCallbackUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StreamUtils;
    import org.springframework.util.StringUtils;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    import java.io.InputStream;
    import java.nio.charset.Charset;
    import java.util.LinkedHashMap;
    import java.util.List;
    
    import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ROUTE_TYPE;
    import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER;
    
    /**
     * Dubbo泛化调用
     *
     * @author Leo
     */
    @Slf4j
    @Component
    public class DubboForwardFilter extends ZuulFilter {
        @Override
        public String filterType() {
            return ROUTE_TYPE;
        }
    
        @Override
        public int filterOrder() {
            return SEND_RESPONSE_FILTER_ORDER - 2;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
            RequestContext requestContext = RequestContext.getCurrentContext();
            HttpServletRequest request = requestContext.getRequest();
    
            log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
    
            if ("POST".equals(request.getMethod())) {
                LinkedHashMap param = null;
                try (InputStream inputStream = request.getInputStream()) {
                    String body = StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"));
                    log.info("原始请求参数:{}", body);
                    param = JSONObject.parseObject(body, LinkedHashMap.class, Feature.OrderedField);
    
                    String interfaceName = (String) param.get("interfaceName");
                    String method = (String) param.get("method");
                    String version = (String) param.get("version");
                    String address = (String) param.get("address");
                    String json = param.get("param").toString();
                    List<Object> paramList = JSONObject.parseArray(json);
    
                    if (!StringUtils.isEmpty(interfaceName) && !StringUtils.isEmpty(method)) {
    
                        Object responseTxt = DubboCallbackUtil.invoke(interfaceName, method, paramList, address, version);
                        requestContext.setSendZuulResponse(true);
                        requestContext.setResponseStatusCode(HttpStatus.OK.value());
                        requestContext.setResponseBody((String) responseTxt);
                    }
                } catch (IOException e) {
                    log.error("dubbo泛化调动异常", e);
                }
            }
    
            return null;
        }
    }

    8、Dubbo泛化调用工具类

    /**
     * @author Leo
     */
    public class DubboCallbackUtil {
    
        private static Logger logger = LogManager.getLogger(DubboCallbackUtil.class);
    
        /**
         * 当前应用的信息
         */
        private static ApplicationConfig application = new ApplicationConfig();
    
        /**
         * 注册中心信息缓存
         */
        private static Map<String, RegistryConfig> registryConfigCache = new ConcurrentHashMap<>();
    
        /**
         * 各个业务方的ReferenceConfig缓存
         */
        private static Map<String, ReferenceConfig> referenceCache = new ConcurrentHashMap<>();
    
        static {
            application.setName("spring-cloud-gateway");
        }
    
        /**
         * 获取注册中心信息
         *
         * @param address zk注册地址
         * @param group   dubbo服务所在的组
         * @return
         */
        private static RegistryConfig getRegistryConfig(String address, String group, String version) {
            String key = address + "-" + group + "-" + version;
            RegistryConfig registryConfig = registryConfigCache.get(key);
            if (null == registryConfig) {
                registryConfig = new RegistryConfig();
                if (StringUtils.isNotEmpty(address)) {
                    registryConfig.setAddress(address);
                }
                if (StringUtils.isNotEmpty(version)) {
                    registryConfig.setVersion(version);
                }
                if (StringUtils.isNotEmpty(group)) {
                    registryConfig.setGroup(group);
                }
                registryConfigCache.put(key, registryConfig);
            }
            return registryConfig;
        }
    
        private static ReferenceConfig getReferenceConfig(String interfaceName, String address,
                                                          String group, String version) {
            String referenceKey = interfaceName;
    
            ReferenceConfig referenceConfig = referenceCache.get(referenceKey);
            if (null == referenceConfig) {
                try {
                    referenceConfig = new ReferenceConfig<>();
                    referenceConfig.setApplication(application);
                    referenceConfig.setRegistry(getRegistryConfig(address, group, version));
                    Class interfaceClass = forName(interfaceName);
                    referenceConfig.setInterface(interfaceClass);
                    if (StringUtils.isNotEmpty(version)) {
                        referenceConfig.setVersion(version);
                    }
                    referenceConfig.setGeneric(true);
                    //referenceConfig.setUrl("dubbo://10.1.50.167:20880/com.test.service.HelloService");
                    referenceCache.put(referenceKey, referenceConfig);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return referenceConfig;
        }
    
        public static Object invoke(String interfaceName, String methodName, List<Object> paramList, String address, String version) {
            ReferenceConfig reference = getReferenceConfig(interfaceName, address, null, version);
            if (null != reference) {
                GenericService genericService = (GenericService) reference.get();
                if (genericService == null) {
                    logger.debug("GenericService 不存在:{}", interfaceName);
                    return null;
                }
    
                Object[] paramObject = null;
                if (!CollectionUtils.isEmpty(paramList)) {
                    paramObject = new Object[paramList.size()];
                    for (int i = 0; i < paramList.size(); i++) {
                        paramObject[i] = paramList.get(i);
                    }
                }
    
    
                Object resultParam = genericService.$invoke(methodName, getMethodParamType(interfaceName, methodName), paramObject);
                return resultParam;
            }
            return null;
        }
    
    
        public static String[] getMethodParamType(String interfaceName, String methodName) {
            try {
                //创建类
                Class<?> class1 = Class.forName(interfaceName);
                //获取所有的公共的方法
                Method[] methods = class1.getMethods();
                for (Method method : methods) {
                    if (method.getName().equals(methodName)) {
                        Class[] paramClassList = method.getParameterTypes();
                        String[] paramTypeList = new String[paramClassList.length];
                        int i = 0;
                        for (Class className : paramClassList) {
                            paramTypeList[i] = className.getTypeName();
                            i++;
                        }
                        return paramTypeList;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
    
        }
    }

    9、bootstrap.yml配置修改

    注意标红的地方,加上这个配置,不管我们有多少个dubbo服务,都通过统一URLhttp://localhost:9001/dubbo请求到自己定路由DubboForwardFilter。

    spring:
      application:
        name: spring-cloud-gateway
    
    server:
      port: 9001
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/,http://localhost:8763/eureka/
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
    zuul:
      routes:
        x-demo-dubbo-provider: /dubbo/**

    10、依次启动zk,x-demo-dubbo-provider,spring-cloud-gateway

    通过PostMan访问Zuul网关,成功收到Dubbo服务返回:“Hello Leo”。

    请求地址:http://localhost:9001/dubbo

    请求参数:

    {
    
        "interfaceName":"com.x.demo.api.HelloDubboService",
        "method":"sayHi",
        "version":"1.0.0",
        "param":["Leo"],
        "address":"zookeeper://localhost:2181"
    }

    当然如果要应用到生产,还需要细化打磨代码。但是有一点可以肯定,此方案在性能上是没有问题,因为实践出真知!!!

  • 相关阅读:
    数组常用的几种方法
    Ajax、Flash优缺点
    Spring系列之Bean的生命周期
    小伙 zwfw-new.hunan.gov.cn.iname.damddos.com [222.240.80.52]
    scott 本月报将收录移动Web加速技术的主要进展,欢迎读者一起完善,投稿邮箱:openweb@baidu.com
    Java前后端依赖
    ifeve.com 南方《JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码》
    陕西柴油机--机械ip--------》QQ请求汇创
    某些站点内容的一个关键,有些网站(特别是论坛类)
    阿里
  • 原文地址:https://www.cnblogs.com/shileibrave/p/14476139.html
Copyright © 2020-2023  润新知