dubbo轉http方式調用方式
更新時間:2025年10月22日 08:35:42 作者:安之若素^
當前項目計劃去掉網關層,前端將直接通過HTTP調用后端dubbo服務,為此,后端需新增controller層,實現(xiàn)請求兼容和統(tǒng)一轉發(fā),保證前端調用方式不變
業(yè)務背景
在當前項目下,所有前端請求均通過外層網關轉發(fā)到后端這邊的dubbo服務,現(xiàn)計劃去掉網關層,由前端直接http調用后端dubbo。
解決方案
在前端調用方式不變的前提下,后端服務新建controller層(原后端服務無任何controller),做統(tǒng)一請求兼容轉發(fā):
package cn.***********.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.utils.ReferenceConfigCache;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;
/**
* Dubbo調用controller
* @Version:1.0
* @Desc
*/
@Slf4j
@RestController
@RequestMapping("/api/test/")
public class GenericController {
@PostMapping("/{service}/{method}")
public Object invoke(HttpServletRequest request,
@PathVariable("service") String service,
@PathVariable("method") String method) {
Object result;
try {
GenericService tarService = this.getTargetService(service);
if(tarService == null){
throw new RuntimeException("獲取service失敗");
}
// 1.解析傳入?yún)?shù)
String paramStr = this.parseInputStream(request);
// 獲取目標接口的 Class 對象
Class<?> serviceInterface = Class.forName(service);
// 獲取方法的參數(shù)類型
String[] parameterTypes = this.getMethodParameterTypes(serviceInterface, method);
// 將 JSON 字符串轉換為目標參數(shù)值
Object[] parameters = this.convertParameters(paramStr, parameterTypes);
// 調用方法
result = tarService.$invoke(method, parameterTypes, parameters);
} catch (Exception e) {
log.error("服務調用失敗, service={},method={}",service, method, e);
throw new ErrorCodeException("000000", "系統(tǒng)異常,請稍后重試");
}
return result;
}
/**
* 獲取請求文本
*
* @return
*/
public String parseInputStream(HttpServletRequest request) {
try {
ServletInputStream inputStream = request.getInputStream();
StringBuilder content = new StringBuilder();
int size = request.getContentLength();
byte[] b = new byte[size];
int lens;
while ((lens = inputStream.read(b)) > 0) {
content.append(new String(b, 0, lens, "utf-8"));
}
return content.toString();
} catch (IOException e) {
log.error("請求報文解析失敗", e);
throw new RuntimeException("請求報文解析異常");
}
}
/**
* 獲取目標dubbo服務
* @params [service]
* @return org.apache.dubbo.rpc.service.GenericService
* @desc
*/
private GenericService getTargetService(String service){
// 應用信息
ApplicationConfig application = SpringBeanUtils.getBean(ApplicationConfig.class);
// 注冊中心發(fā)現(xiàn)
RegistryConfig registry = SpringBeanUtils.getBean(RegistryConfig.class);
// 引用遠程服務
ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
reference.setApplication(application);
reference.setRegistry(registry);
reference.setProtocol("dubbo");
reference.setInterface(service);
reference.setTimeout(10000);
reference.setRetries(0);
// 聲明為泛化接口
reference.setGeneric(true);
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
GenericService genericService = cache.get(reference);
if(genericService == null){
genericService = reference.get();
}
return genericService;
}
/**
* 將 JSON 字符串轉換為目標參數(shù)值
* @params [paramStr, parameterTypes]
* @return java.lang.Object[]
* @author wu.zeng
* @date 2025/2/21 13:44
* @desc
*/
private Object[] convertParameters(String paramStr, String[] parameterTypes) {
Object[] parameters = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
String paramType = parameterTypes[i];
switch (paramType) {
case "java.lang.String":
parameters[i] = paramStr;
break;
case "java.lang.Integer":
parameters[i] = Integer.parseInt(paramStr);
break;
case "java.lang.Long":
parameters[i] = Long.parseLong(paramStr);
break;
case "java.util.Map":
parameters[i] = JSON.parseObject(paramStr, new TypeReference<Map<String, Object>>() {});
break;
case "java.util.List":
parameters[i] = JSON.parseObject(paramStr, new TypeReference<List<Object>>() {});
break;
default:
// 如果是自定義類型,使用反射或 JSON 反序列化
try {
Class<?> clazz = Class.forName(paramType);
parameters[i] = JSON.parseObject(paramStr, clazz);
} catch (ClassNotFoundException e) {
throw new RuntimeException("不支持的類型: " + paramType, e);
}
}
}
return parameters;
}
/*
* 獲取目標方法的參數(shù)類型
* @params [serviceInterface, methodName]
* @return java.lang.String[]
* @desc
*/
public String[] getMethodParameterTypes(Class<?> serviceInterface, String methodName) {
for (Method method : serviceInterface.getMethods()) {
// 由于不支持重載,因此這里可以根據(jù) methodName 來匹配方法
if (method.getName().equals(methodName)) {
Parameter[] parameters = method.getParameters();
String[] parameterTypes = new String[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parameterTypes[i] = parameters[i].getType().getName();
}
return parameterTypes;
}
}
throw new RuntimeException("方法未找到: " + methodName);
}
}總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
mybatis如何在一個update標簽中寫多條update語句
這篇文章主要介紹了mybatis如何在一個update標簽中寫多條update語句問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
Java版C語言版簡單使用靜態(tài)語言實現(xiàn)動態(tài)數(shù)組的方法
本文給大家分享java版和C語言版簡單使用靜態(tài)語言實現(xiàn)動態(tài)數(shù)組的方法,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2017-10-10

