基于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的更多相关文章
- 基于Protobuf的分布式高性能RPC框架——Navi-Pbrpc
基于Protobuf的分布式高性能RPC框架——Navi-Pbrpc 二月 8, 2016 1 简介 Navi-pbrpc框架是一个高性能的远程调用RPC框架,使用netty4技术提供非阻塞.异步.全 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍
概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...
- 基于动态代理的WebAPI/RPC/webSocket框架,一套接口定义,多个通讯方式
API/RPC/webSocket三个看起来好像没啥相同的地方,在开发时,服务端,客户端实现代码也大不一样 最近整理了一下,通过动态代理的形式,整合了这些开发,都通过统一的接口约束,服务端实现和客户端 ...
- 服务治理与RPC · 跬步
以前写过Django中使用zerorpc的方法,但是由于我们的Django是运行在gevent下,而zeromq需要启动一个后台进程处理消息,与gevent使用的greenlet携程是冲突的. 在Ja ...
- 面试都在问的微服务、服务治理、RPC、下一代微服务框架... 一文带你彻底搞懂!
文章每周持续更新,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 单体式应用程序 与微服务相对的另一个概念是传统的单体式应用程序( ...
- 面试都在问的「微服务」「RPC」「服务治理」「下一代微服务」一文带你彻底搞懂!
❝ 文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) ❞ 单体式应用程序 与微服务相对的另一个概念是传统的「单体式应用程 ...
- 基于Kubernetes服务发现机制的探讨Non Service
服务注册 注册中⼼作为一般的RPC/Web服务中的底层设施提供了服务进程元数据(IP, Port, Interface, Group,Method等)存储,被Watch的功能,每个服务进程均需接⼊同⼀ ...
- Netty(四)基于Netty 的简易版RPC
3.1 RPC 概述 下面的这张图,大概很多小伙伴都见到过,这是 Dubbo 官网中的一张图描述了项目架构的演进过程 它描述了每一种架构需要的具体配置和组织形态.当网站流量很小时,只需一个应用,将所有 ...
- 基于RMI服务传输大文件的完整解决方案
基于RMI服务传输大文件,分为上传和下载两种操作,需要注意的技术点主要有三方面,第一,RMI服务中传输的数据必须是可序列化的.第二,在传输大文件的过程中应该有进度提醒机制,对于大文件传输来说,这点很重 ...
随机推荐
- 用 R 进行高频金融数据分析简介
作者:李洪成 摘自:http://cos.name/wp-content/uploads/2013/11/ChinaR2013SH_Nov03_04_LiHongcheng.pdf 高频数据 金融市场 ...
- 如何删除选中的checkbox
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 修改centos启动项
centos7下修改启动项在路径/etc/grub.d/文件路径下,修改完成之后需要运行命令 grub2-mkconfig --output=/boot/grub2/grub.cfg
- CentOS 7 安装 vmware-tools
[原创]标题:<CentOS 7 安装 vmware-tools>:作者:肖雪峰,QQ:35360657. 用 VMware Workstation 11 新安装了CentOS 7虚拟机, ...
- oracle迁移mysql数据库注意
oracle转mysql修改: . substr() substr( string , 0, 10) 这里测试 必须从 第一位获取 既是 substr(string , 1 , 10)2. to_ch ...
- JavaScript 基础第七天(DOM的开始)
一.引言 JavaScript的内容分为三个部分,这三个部分分别是ECMAScript.DOM.BOM三个部分组成.所谓ECMAScript就是JavaScript和核心基础语法,DOM是文档对象模型 ...
- 【摘】【网络】无线AP与无线路由器有什么区别?
参考网站: 1.无线上网百科 http://wifi.baike.com/article-290204.html 图片 1 今天我们从功能.应用.组网和成本四个方面为大家区分无线路由器和无线AP.当前 ...
- hdu 4698 - Counting(思路)
转:题意:给定一个二维平面,其中x取值为1-N,y取值为1-M,现给定K个点,问至少包括K个点中的一个的满足要求的<Xmin, Xmax, Ymin, Ymax>共有多少中取值情况.也就是 ...
- html+css实现简易下拉菜单
<!DOCTYPE html> <html> <head> <style> div { width:100px; height:40px; overfl ...
- 算法:POJ1006 三重峰值问题
这题有直接套公式的解法 这里提供一个O(n)的解法. package practice; import java.io.BufferedInputStream; import java.util.Sc ...