基于Rest服务实现的RPC
Posted lpeiNet
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Rest服务实现的RPC相关的知识,希望对你有一定的参考价值。
前言:现在RPC成熟的框架已经很多了,比喻Motan和Dubbo,但是今天我这里提供一种基于Rest服务的Rpc。和上一篇连着的http://www.cnblogs.com/LipeiNet/p/5856414.html
1:原理
首先我们要建立一个Rest服务,如果其他应用程序想要获取这个服务的资源就只需要一个URI就可以了。但是由于内部程序的调用我们在通过URI获取json然后在自己处理很不方便,也不是很合适,那么我们就需要利用一个中间层,把访问Rest服务返回的资源重新包装,然后其他工程只需要调用这个rpc工程即可。如下图
2:实现Rest服务
2.1:定义一个用于返回给消费者的实现对象(自己约定的)
public class ResponseBean implements Serializable{ private static final long serialVersionUID = -1L; public static final int SUCCESS = 10; public static final int FAILURE = 20; public static final int LOCKED = 30; public static final int EXCEPTION = 40; private int returnCode;//返回给消费者的编码(0表示调用成功,1表示调用失败) private String returnMsg;//返回给消费者错误信息 private int dataCount;//用于返回int类型 private String returnData;//用户返回json private Object returnObject;//用于返回对象 public ResponseBean() { } public ResponseBean(int returnCode, String returnMsg) { this.returnCode = returnCode; this.returnMsg = returnMsg; } public Object getReturnObject() { return this.returnObject; } public void setReturnObject(Object returnObject) { this.returnObject = returnObject; } public int getDataCount() { return this.dataCount; } public void setDataCount(int dataCount) { this.dataCount = dataCount; } public String getReturnData() { return this.returnData; } public void setReturnData(String returnData) { this.returnData = returnData; } public int getReturnCode() { return this.returnCode; } public void setReturnCode(int returnCode) { this.returnCode = returnCode; } public String getReturnMsg() { return this.returnMsg; } public void setReturnMsg(String returnMsg) { this.returnMsg = returnMsg; } }
2.2:定一个供外部请求的ApiService
public interface ApiService { String getToken(); ResponseBean add(String reqJson); }
public class ApiServiceImpl implements ApiService { private static final Log log = LogFactory.getLog(ApiServiceImpl.class); @Autowired private UserDao userDao; private String token;//供调用rpc校验使用 public String getToken() { return token; } public ResponseBean add(String reqJson) { ResponseBean responseBean = new ResponseBean(ResponseBean.SUCCESS, "调用成功"); try { Map map = JsonUtil.g.fromJson(reqJson, HashMap.class); String username = map.get("username").toString(); String password = map.get("password").toString(); String realname = map.get("realname").toString(); Long userroleid =Double.valueOf(map.get("userroleid").toString()).longValue() ; UserBean userBean = new UserBean(); userBean.setCreatedate(new Date()); userBean.setPassword(password); userBean.setUserroleid(userroleid); userBean.setRealname(realname); userBean.setUsername(username); int count = userDao.add(userBean); responseBean.setReturnData(JsonUtil.g.toJson(count)); responseBean.setReturnCode(10); } catch (Exception e) { log.error(e.getStackTrace()); responseBean.setReturnCode(11); responseBean.setReturnMsg("服务器异常"); } return responseBean; } public void setToken(String token) { this.token = token;//用于设置token(用来验证消费者的token是否是服务器的token) } }
2.3:定义http请求的入口,需要3个参数token(进行安全认证)、m(请求方法名)、reqJson(请求的参数)
@Controller @RequestMapping(value = "/api") public class ApiController { private static final Log log = LogFactory.getLog(ApiController.class); @Autowired private ApiService apiService; /** * 系统对外公开调用方法 * * @param m 接口方法名 * @param reqJson 请求参数 * @param token 请求token * @return */ @RequestMapping(value = "/exec", method = RequestMethod.POST) @ResponseBody public Object exec(@RequestParam(value = "m", required = true) String m, @RequestParam(value = "reqJson", required = true) String reqJson, @RequestParam(value = "token", required = true) String token) { log.info(String.format("m=%s,reqJson=%s,token=%s", m, reqJson, token)); Class c = apiService.getClass(); Method method = null; ResponseBean responseBean = null; if (!token.equals(apiService.getToken())) { log.error("token校验失败,token=" + token); responseBean = new ResponseBean(ResponseBean.FAILURE, "校验失败"); return responseBean; } try { method = c.getMethod(m, String.class);//利用反射找到对应的方法 } catch (Exception e) { log.error("m参数错误,m=" + m + ";req=" + reqJson, e); responseBean = new ResponseBean(ResponseBean.FAILURE, "m参数错误m=" + m); return responseBean; } if (StringUtils.isEmpty(reqJson)) { log.error("reqJson为空"); responseBean = new ResponseBean(ResponseBean.FAILURE, "reqJson为空"); return responseBean; } try { Object json = method.invoke(apiService, reqJson); return json; } catch (Exception e) { log.error("处理异常,m=" + m + ";req=" + reqJson, e); responseBean = new ResponseBean(ResponseBean.FAILURE, "服务器处理异常"); return responseBean; } } }
通过上面我们就实现了一个供消费者调用的rest服务。
3:RPC对rest服务进行包装
3.1:定义消费者需要的webService
public interface WebService { ResponseBean add(UserBean userBean); }
实现webService
public class WebserviceImpl implements WebService { private final static Log log = LogFactory.getLog(WebserviceImpl.class); private String url;//供消费者设置的url地址 private String token;//供消费者设置的url public void setUrl(String url) { this.url = url; } public void setToken(String token) { this.token = token; } @Override public ResponseBean add(UserBean userBean) { Map<String, String> map = new HashMap(); ResponseBean rb = null; map.put("token", token); map.put("reqJson", JsonUtil.g.toJson(userBean)); String reqUrl = url + "?m=add";//在这里进行设置你需要访问哪个方法 log.debug(reqUrl); try { String str = HttpClientUtil.executeHttpRequestUTF(reqUrl, map);//访问资源获取返回的json log.debug("add return data:" + str); rb = JsonUtil.g.fromJson(str, ResponseBean.class);//对json进行转换 log.debug("getPromInfo return Regions data" + reqUrl); return rb; } catch (Exception e) { rb = new ResponseBean(); rb.setReturnObject(e); log.debug(e.getStackTrace()); return rb; } } }
然后对rpc进行打包发布,其他应用程序就可以直接使用了。
4:配置
在applicationconfig中加入
<mvc:annotation-driven/>
<mvc:default-servlet-handler />
<mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean否则会出现异常
<bean id="ApiService" class="com.lp.rpc.impl.ApiServiceImpl">
<property name="token" value="41729ff3-3406-4fc5-aeca-04f98892999b"></property>
</bean>
消费者配置:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="webService" class="com.lp.rpc.impl.WebserviceImpl"> <property name="url" value="http://192.168.0.101:8088/api/exec"></property> <property name="token" value="41729ff3-3406-4fc5-aeca-04f98892999b"></property> </bean> </beans>
5:开启一个项目把rpc项目引进调用
public class AppMain { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("application-config.xml"); WebService webService = (WebService) context.getBean("webService"); UserBean userBean = new UserBean(); userBean.setUsername("lisi"); userBean.setPassword("123456"); userBean.setRealname("李四"); userBean.setUserroleid(2); userBean.setCreatedate(new Date()); ResponseBean result = webService.add(userBean); if (StringUtils.equals(result.getReturnData().toString(),"1")){ System.out.print("添加成功"); } } }
6:总结
以上级别能完成不同项目之间的调用了
优点:简单上手快,可以自己控制,效率也可以。
缺点:安全性低,需要维护url,有时候别人服务反复开启时候会出现调用不到的情况。
源码地址
以上是关于基于Rest服务实现的RPC的主要内容,如果未能解决你的问题,请参考以下文章