current position:Home>Expose Dubbo to HTTP service

Expose Dubbo to HTTP service

2022-04-29 10:00:47xiaoweiwei99

Usually one dubbo The service is internal , That is, for internal calls , But it is also possible that a service needs to be provided for external use , And can not have the limitations of using language .

The standard practice is to provide a unified service for external services openAPI, Such callers need to provide corresponding services according to the standard appID And the key for signature verification . This is certainly more standardized and safe , But the complexity is no less than developing a separate system .

What is said here is not so complicated , It's just a that doesn't need all kinds of permission verification dubbo The service provided externally is HTTP service .

Realize the idea :

1、 Definition HttpProviderConf Configuration class

It is mainly used to save the package name that declares that it needs to provide external services

package com.bsk.dubbo.http.conf;


import java.util.List;


public class HttpProviderConf {

    /**
     *  Provide http Package accessed 
     */
    private List<String> usePackage ;

    public List<String> getUsePackage() {
        return usePackage;
    }

    public void setUsePackage(List<String> usePackage) {
        this.usePackage = usePackage;
    }
}

2、 Encapsulate the request response class

package com.bsk.dubbo.http.req;
/**
 *  request 
 */
public class HttpRequest {
    private String param ;// Enter the reference 
    private String service ;// request service
    private String method ;// Request method 

    public String getParam() {
        return param;
    }

    public void setParam(String param) {
        this.param = param;
    }

    public String getService() {
        return service;
    }

    public void setService(String service) {
        this.service = service;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }
}

among param It is used to store the real call dubbo Service input , Pass in json When called, it is parsed into a specific parameter object .

service Deposit dubbo Of service declaration interface API The package name .

method Is the name of the method actually called .

package com.bsk.dubbo.http.rsp;

import java.io.Serializable;

/**
 *  Respond to 
 */
public class HttpResponse implements Serializable{

    private static final long serialVersionUID = -552828440320737814L;

    private boolean success;// Sign of success 

    private String code;// Information code 

    private String description;// describe 

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

3、 Write exposure Services Controller Interface

utilize SpringMVC Provide a HTTP Interface .
In this interface, reflection is carried out through input parameters to find specific dubbo Call the service implementation .

package com.bsk.dubbo.http.controller;

import com.alibaba.fastjson.JSON;
import com.bsk.dubbo.http.conf.HttpProviderConf;
import com.bsk.dubbo.http.req.HttpRequest;
import com.bsk.dubbo.http.rsp.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.*;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;


@Controller
@RequestMapping("/dubboAPI")
public class DubboController implements ApplicationContextAware {

    private final static Logger logger = LoggerFactory.getLogger(DubboController.class);

    @Autowired
    private HttpProviderConf httpProviderConf;

    /**
     *  cache  Map
     */
    private final Map<String, Class<?>> cacheMap = new HashMap<String, Class<?>>();

    protected ApplicationContext applicationContext;


    @ResponseBody
    @RequestMapping(value = "/{service}/{method}", method = RequestMethod.POST)
    public String api(HttpRequest httpRequest, HttpServletRequest request,
                      @PathVariable String service,
                      @PathVariable String method) {
        logger.debug("ip:{}-httpRequest:{}", getIP(request), JSON.toJSONString(httpRequest));

        String invoke = invoke(httpRequest, service, method);
        logger.debug("callback :" + invoke);
        return invoke;

    }


    private String invoke(HttpRequest httpRequest, String service, String method) {
        httpRequest.setService(service);
        httpRequest.setMethod(method);

        HttpResponse response = new HttpResponse();

        logger.debug("input param:" + JSON.toJSONString(httpRequest));

        if (!CollectionUtils.isEmpty(httpProviderConf.getUsePackage())) {
            boolean isPac = false;
            for (String pac : httpProviderConf.getUsePackage()) {
                if (service.startsWith(pac)) {
                    isPac = true;
                    break;
                }
            }
            if (!isPac) {
                // An unconfigured package is called 
                logger.error("service is not correct,service=" + service);
                response.setCode("2");
                response.setSuccess(false);
                response.setDescription("service is not correct,service=" + service);
            }

        }
        try {
            Class<?> serviceCla = cacheMap.get(service);
            if (serviceCla == null) {
                serviceCla = Class.forName(service);
                logger.debug("serviceCla:" + JSON.toJSONString(serviceCla));

                // Set the cache 
                cacheMap.put(service, serviceCla);
            }
            Method[] methods = serviceCla.getMethods();
            Method targetMethod = null;
            for (Method m : methods) {
                if (m.getName().equals(method)) {
                    targetMethod = m;
                    break;
                }
            }

            if (method == null) {
                logger.error("method is not correct,method=" + method);
                response.setCode("2");
                response.setSuccess(false);
                response.setDescription("method is not correct,method=" + method);
            }

            Object bean = this.applicationContext.getBean(serviceCla);
            Object result = null;
            Class<?>[] parameterTypes = targetMethod.getParameterTypes();
            if (parameterTypes.length == 0) {
                // No parameters 
                result = targetMethod.invoke(bean);
            } else if (parameterTypes.length == 1) {
                Object json = JSON.parseObject(httpRequest.getParam(), parameterTypes[0]);
                result = targetMethod.invoke(bean, json);
            } else {
                logger.error("Can only have one parameter");
                response.setSuccess(false);
                response.setCode("2");
                response.setDescription("Can only have one parameter");
            }
            return JSON.toJSONString(result);

        } catch (ClassNotFoundException e) {
            logger.error("class not found", e);
            response.setSuccess(false);
            response.setCode("2");
            response.setDescription("class not found");
        } catch (InvocationTargetException e) {
            logger.error("InvocationTargetException", e);
            response.setSuccess(false);
            response.setCode("2");
            response.setDescription("InvocationTargetException");
        } catch (IllegalAccessException e) {
            logger.error("IllegalAccessException", e);
            response.setSuccess(false);
            response.setCode("2");
            response.setDescription("IllegalAccessException");
        }
        return JSON.toJSONString(response);
    }

    /**
     *  obtain IP
     *
     * @param request
     * @return
     */
    private String getIP(HttpServletRequest request) {
        if (request == null) {
            return null;
        }
        String s = request.getHeader("X-Forwarded-For");
        if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) {

            s = request.getHeader("Proxy-Client-IP");
        }
        if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) {

            s = request.getHeader("WL-Proxy-Client-IP");
        }
        if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) {
            s = request.getHeader("HTTP_CLIENT_IP");
        }
        if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) {

            s = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) {

            s = request.getRemoteAddr();
        }
        if ("127.0.0.1".equals(s) || "0:0:0:0:0:0:0:1".equals(s)) {
            try {
                s = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException unknownhostexception) {
                return "";
            }
        }
        return s;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

Encapsulate these implementations into a functional module

Please refer to the blog for specific use :https://crossoverjie.top/2017/05/07/SSM13/

4、 Test examples

Use Jmeter Tools to test :

test result :

5、 summary

Generally speaking, this provides HTTP Not many interfaces are used in practice , But it's easy to debug .

For example, I wrote a dubbo Query interface for , In the test environment or pre release environment, you can directly pass HTTP Simple testing by request , Or query data . Than in Java It is much faster to write a single test to test or query .

copyright notice
author[xiaoweiwei99],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/119/202204290825496818.html

Random recommended