Backend Development 10 min read

How to Bridge Domestic and Overseas Dubbo Services with Generic HTTP Proxies

This article presents a one‑stop solution that uses dynamic proxies, Spring extensions, and Dubbo generic invocation to let overseas applications call domestic Dubbo services via HTTP without refactoring existing callers, reducing workload and eliminating previous drawbacks.

Weimob Technology Center
Weimob Technology Center
Weimob Technology Center
How to Bridge Domestic and Overseas Dubbo Services with Generic HTTP Proxies

Project Background

The new e‑commerce system serves domestic merchants and overseas buyers; servers are deployed in an East‑US data center and connected via a dedicated line, but the domestic and overseas Dubbo clusters are isolated and cannot communicate directly.

Current Solution and Drawbacks

Create a service‑wrapping application in China to expose domestic Dubbo services as HTTP endpoints for overseas calls.

Overseas applications invoke the wrapped HTTP services.

Drawbacks: callers must be refactored; wrapping Dubbo to HTTP adds extra work and each iteration may require new interfaces.

Idea: Generic Proxy for Cross‑Region Calls

Dubbo consumers are essentially proxy objects that use TCP; we can generate a custom proxy that accesses domestic services via HTTP.

Generic invocation allows calling a service by name and parameters without depending on its jar.

Consumer Side Implementation

Define

@RemoteReference

annotation on fields to indicate a remote proxy.

<code>@Component
public class LogisticsHandler {
    @RemoteReference
    private LogisticsTrackAdaptExportService logisticsTrackAdaptExportService;

    @RemoteReference
    private OrderAdapterExportService orderAdapterExportService;
}
</code>

Use CGLIB to create the proxy:

<code>@Slf4j
public class InvocationProxy implements MethodInterceptor {
    private final Class<?> interfaceClass;
    private String remoteReferenceUrl;
    public InvocationProxy(Class<?> interfaceClass, String remoteReferenceUrl) throws Exception {
        this.interfaceClass = interfaceClass;
        if (StringUtils.isEmpty(remoteReferenceUrl)) {
            throw new InvalidParameterException("remoteReferenceUrl is null");
        }
        this.remoteReferenceUrl = remoteReferenceUrl;
    }
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        if (args == null || args.length == 0) {
            throw new IllegalArgumentException("invalid param");
        }
        HttpPost post = new HttpPost(remoteReferenceUrl);
        Object request = args[0];
        HttpClient httpClient = HttpClientUtils.getHttpClient("common-proxy");
        List<NameValuePair> nvps = new ArrayList<>();
        nvps.add(new BasicNameValuePair("interface", interfaceClass.getName()));
        nvps.add(new BasicNameValuePair("method", method.getName()));
        nvps.add(new BasicNameValuePair("parameter", JSON.toJSONString(request)));
        post.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
        post.setHeader("Content-type", "application/x-www-form-urlencoded");
        HttpResponse response = httpClient.execute(post);
        String responseContent = EntityUtils.toString(response.getEntity(), "UTF-8");
        return PojoUtils.realize(JSON.parseObject(responseContent), SoaResponse.class);
    }
}
</code>

Inject the proxy into Spring IOC via a custom

BeanPostProcessor

that registers a bean definition for the proxy.

<code>@Slf4j
public class RemoteReferenceInjectedBeanPostProcessor extends AbstractAnnotationBeanPostProcessor {
    public RemoteReferenceInjectedBeanPostProcessor() {
        super(RemoteReference.class);
    }
    @Override
    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName,
                                       Class injectedType, InjectionMetadata.InjectedElement injectedElement) throws Exception {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(injectedType, () -> {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(injectedType);
            enhancer.setCallback(new InvocationProxy(injectedType, this.getEnvironment().getProperty("remoteReferenceUrl")));
            return injectedType.cast(enhancer.create());
        });
        String newBeanName = injectedType.getName() + "$proxy";
        BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        registry.registerBeanDefinition(newBeanName, beanDefinition);
        return beanFactory.getBean(newBeanName);
    }
    @Override
    protected String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
                                                    Class<?> injectedType, InjectionMetadata.InjectedElement injectedElement) {
        return injectedType.getName();
    }
}
</code>

Provider Side Implementation

Expose a generic HTTP endpoint that forwards calls to Dubbo via generic service.

<code>public static GenericService getGenericService(String clazz) {
    if (dubboServiceInstance.containsKey(clazz)) {
        return dubboServiceInstance.get(clazz);
    }
    synchronized (dubboServiceInstance) {
        if (dubboServiceInstance.containsKey(clazz)) {
            return dubboServiceInstance.get(clazz);
        }
        GenericService genericService = generatorReferenceConfig(clazz);
        dubboServiceInstance.put(clazz, genericService);
        return genericService;
    }
}
private static GenericService generatorReferenceConfig(String clazz) {
    ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
    reference.setApplication(applicationContext.getBean(ApplicationConfig.class));
    reference.setRegistry(applicationContext.getBean(RegistryConfig.class));
    reference.setInterface(clazz);
    reference.setGeneric(true);
    reference.setTimeout(10000);
    return ReferenceConfigCache.getCache().get(reference);
}
</code>
<code>@RequestMapping(value = "invoke", method = RequestMethod.POST)
@ResponseBody
public Object invoke(@RequestParam("interface") String interfaceName,
                     @RequestParam("method") String method,
                     @RequestParam("parameter") String parameter,
                     HttpServletRequest request) {
    Map<String, String> paramMap = new HashMap<>();
    paramMap.put("interface", interfaceName + "." + method);
    paramMap.put("parameter", parameter);
    long start = System.nanoTime();
    String resp;
    try {
        GenericService genericService = ServiceManager.getGenericService(interfaceName);
        Object result = genericService.$invoke(method, null, new Object[]{JSON.parseObject(parameter)});
        resp = JSON.toJSONString(result);
    } catch (Exception ex) {
        SoaResponse error = new SoaResponse();
        error.setReturnCode("500");
        error.setReturnMsg(ex.getMessage());
        resp = JSON.toJSONString(error);
    } finally {
        // logging omitted for brevity
    }
    return resp;
}
</code>

The flow diagram below illustrates the call process after transformation.

Conclusion

The generic invocation capability bridges domestic Dubbo services and overseas consumers without refactoring existing callers, reducing workload and avoiding the drawbacks of the previous HTTP‑wrapper approach. The solution leverages dynamic proxies, Spring extension points, and Dubbo generic calls.

Backend IntegrationmicroservicesDubboSpringGeneric InvocationCGLIB
Weimob Technology Center
Written by

Weimob Technology Center

Official platform of the Weimob Technology Center

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.