dubbo exposes HTTP services

dubbo exposes HTTP services

   Don't get lost. Welcome to visit again!		

Catalog

Preface

Generally speaking, a dubbo service is called internally, but it is also possible that a service needs to be provided externally, and it cannot have the limitation of using language. This chapter is not so complicated. It just provides a dubbo service that does not need all kinds of permission verification as an HTTP service.

Preparation

Here are some of the knowledge points involved in this article:

Spring related knowledge
Java reflection knowledge
Spring MVC related knowledge

In fact, the idea is very simple, using spring MVC to provide an HTTP interface. In this interface, the concrete dubbo service implementation is found through the reflection of the input parameter.

HttpProviderConf configuration class

First of all, we need to define an HttpProviderConf class to save the package name of the declared external service. After all, we need to use a class's fully qualified name when reflecting:

public class HttpProviderConf {
    /**
     * Packages that provide http access
     */
    private List<String> usePackage ;

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

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

Request response input and output parameters

HttpRequest input

public class HttpRequest {
	
	private String param ;//Participation
    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;
	}
}

param is used to store the input parameters when the dubbo service is actually called, and the incoming json is parsed into specific parameter objects when it is called.
Service the package name of the interface API where the dubbo service declaration is stored.
Method is the name of the method that is actually called.

HttpResponse response

public class HttpResponse implements Serializable{
	
	private static final long serialVersionUID = -6296842759601736401L;	
	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;
	}
}

The response data of the commonly used HTTP service is just encapsulated here.

Exposure service controller

@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.GET)
	public String api(HttpRequest httpRequest, HttpServletRequest request, @PathVariable String service,
			@PathVariable String method) {
		logger.info("ip:{}-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) {
				// Calling unconfigured package
				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 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);
	}

	/**
	 * Get 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;
	}
}
  • First, a DubboController is defined, and the spring MVC annotation is used to expose HTTP services.

  • The org.springframework.context.ApplicationContextAware class is implemented, and the setApplicationContext() method is implemented to initialize the Spring context object. After that, the corresponding object in the container can be obtained.

  • The core's invoke() method.

  • When called: http://127.0.0.1:8080/dubbo-provider/dubboAPI/com.dubbo.service.IDubboService/getUser.

  • See the call instance above for details. First, assign com.dubbo.service.IDubboService and getUser to the httpRequest parameter.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
	<!-- Provider application information is defined to calculate dependencies. stay dubbo-admin or dubbo-monitor This name will be displayed for easy identification -->
	<dubbo:application name="admin-provider" owner="admin" organization="dubbox"/>
	<!-- Use zookeeper To expose the service in the registration center, please open it first zookeeper -->
	<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
	<!-- use dubbo Agreement in20880Port exposure service -->
	<dubbo:protocol name="dubbo" port="20880"/>
	<!-- use dubbo Protocol implementation defined api Interface -->
	<dubbo:service interface="com.dubbo.service.IDubboService" ref="dubboService" protocol="dubbo"/>
	<dubbo:service interface="com.dubbo.service.IUserService" ref="userService" protocol="dubbo"/>
	<!--dubbo Service exposure is http service-->
    <bean class="com.crossoverJie.dubbo.http.conf.HttpProviderConf">
        <property name="usePackage">
            <list>
            	   <!--Need to expose the interface package name of the service, multiple-->
                <value>com.dubbo.service</value>
            </list>
        </property>
    </bean>
</beans>

Among them, com.dubbo.service is the package name you need to expose, which can be multiple.

  • Then take out the interface class type obtained by reflection in the cache map. If not, get it by reflection, and set the value to the cache map. In this way, you can save system overhead (reflection consumes system resources).
  • Then it is to determine whether there is the incoming getUser method in the interface.
  • Take out the parameter list of the method, and call directly if there is no parameter.
  • If there are parameters, judge the number. There is at most one parameter to run here. That is to say, only one BO type can be passed during the real dubbo call. The specific parameter list can be written to BO. Because if there are multiple parameters that cannot be assigned to two parameter objects during json parsing.
  • After the call, return the data returned by the call.
102 original articles published, 33 praised, 20000 visitors+
Private letter follow

Tags: Dubbo JSON Spring Zookeeper

Posted on Mon, 13 Jan 2020 10:12:57 -0500 by carylson