你的位置:首页 > Java教程

[Java教程]基于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" >
  <property name="token" value="41729ff3-3406-4fc5-aeca-04f98892999b"></property>
</bean>
消费者配置:
<beans     ="http://www.w3.org/2001/    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 ClassPath);    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,有时候别人服务反复开启时候会出现调用不到的情况。