博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
也来谈谈RPC
阅读量:5971 次
发布时间:2019-06-19

本文共 8102 字,大约阅读时间需要 27 分钟。

  hot3.png

问题描述

要想实现RPC,本质上是服务发布方与服务调用方的通信,主要集中在以下几个问题上:

  • 调用方首先需要知道如何找到对应服务所在的远程Server
  • 调用方与服务提供方能以特定的双方能够解析的数据格式封装自己的调用请求和调用结果
  • 对多个服务提供方Server提供的不同服务以及它们的状态应该予以有效的管理,比如新增服务需要注册该服务
  • 对于不同系统间的调用可能还需要一定的容错和调度

首先我们给出一个最简单的解决思路,就是直接利用HTTP协议,将调用方看做客户端,而服务提供方看做服务器,以JSON来做数据传输,默认一套key-value的含义解释,直接发起请求,然后等待响应并解析返回的JSON串。听起来似乎可行,但是却有许多弊端:

  • 每个调用者需要提前保有一份服务者以及它提供的相应服务的记录信息
  • 大量的Http请求与解析响应的代码混杂在原有的业务代码中,对于使用服务的开发者不透明,恶劣的设计
  • 对于服务变更和新增的情况不友好
  • 如果有多个提供者提供同一服务,不利于增加均衡策略,不利于扩展

。。。。

还有很多,这就是为什么许多RPC框架和标准存在的原因,下面我们就主要从RMI出发来继续探讨这一问题。

我们以Apache的CXF框架来说明RMI调用,首先介绍一下JAX-WS和JAX-RS的概念。

  • JAX-WS:JavaTM API forXML-Based Web Services,JAX-WS是面向消息的,每次请求的时候指定了请求的方法。J
  • JAX-RS:JavaTM API forRESTful Web Services,AX-RS是面向资源的。后则将网络上的东西当做一种资源,每次请求都是对该资源进行操作,比如对资源的增删查改。

SOAP和JAXRS分别代表了这两种模式。

JAXRS

首先关注服务提供方:

在ApplicatiionContext.xml中加入

Apache CXF的Restful Web Service配置

然后添加服务

/** * WebService服务端实现类. *  * 为演示方便,直接调用了Service层.客户端实现见功能测试用例. *  */@Path("/user")public class UserJaxRsService {	private static Logger logger = LoggerFactory.getLogger(UserJaxRsService.class);	@Autowired	private UserService userService;		@GET	@Path("/{id}.xml")	@Produces(MediaTypes.APPLICATION_XML_UTF_8)	public UserDTO getAsXml(@PathParam("id") Long id) {		Optional
user = userService.load(id); if (!user.isPresent()) { String message = "用户不存在(id:" + id + ")"; logger.warn(message); throw buildException(Status.NOT_FOUND, message); } return bindDTO(user.get()); } @GET @Path("/{id}.json") @Produces(MediaTypes.JSON_UTF_8) public UserDTO getAsJson(@PathParam("id") Long id) { Optional
user = userService.load(id); if (!user.isPresent()) { String message = "用户不存在(id:" + id + ")"; logger.warn(message); throw buildException(Status.NOT_FOUND, message); } return bindDTO(user.get()); } private UserDTO bindDTO(User user) { UserDTO dto = BeanMapper.map(user, UserDTO.class); // 补充Dozer不能自动绑定的属性 return dto; } private WebApplicationException buildException(Status status, String message) { return new WebApplicationException(Response.status(status).entity(message).type(MediaTypes.TEXT_PLAIN_UTF_8) .build()); } }

接下来看服务调用方

/** * 对基于JAX-RS的实现Restful的测试 *  * @author calvin */public class UserJaxRsFT extends BaseFunctionalTestCase {	private static String resourceUrl = baseUrl + "/cxf/jaxrs/user";	private RestTemplate restTemplate = new RestTemplate();	@Test	public void getUser() {		UserDTO user = restTemplate.getForObject(resourceUrl + "/{id}.xml", UserDTO.class, 1L);		assertThat(user.getLoginName()).isEqualTo("admin");		assertThat(user.getName()).isEqualTo("管理员");		assertThat(user.getTeamId()).isEqualTo(1);		try {			user = restTemplate.getForObject(resourceUrl + "/{id}.json", UserDTO.class, 1L);		} catch (HttpStatusCodeException e) {			fail(e.getMessage());		}		assertThat(user.getLoginName()).isEqualTo("admin");		assertThat(user.getName()).isEqualTo("管理员");		assertThat(user.getTeamId()).isEqualTo(1);	}}

SOAP

首先解释一下SOAP的概念:一种轻量的、简单的、基于XML的交换数据协议规范,也就是说它定义了Server之间通信的规范。SOAP的使用方式较为复杂,故我们只截取其中的核心部分阐述。

首先,看服务提供方:

配置文件

Apache CXF的 SOAP Web Service配置

然后是服务的实现:

/** * WebService服务端实现类. *  * 为演示方便,直接调用了Dao层.客户端实现见功能测试用例. *  */// serviceName指明WSDL中
元素的名称, endpointInterface属性指向Interface类全称.@WebService(serviceName = "AccountService", endpointInterface = "org.springside.examples.showcase.webservice.soap.AccountSoapService", targetNamespace = WsConstants.NS)// 增加inbound/outbound SOAP内容的日志@Features(features = "org.apache.cxf.feature.LoggingFeature")public class AccountSoapServiceImpl implements AccountSoapService { private static Logger logger = LoggerFactory.getLogger(AccountSoapServiceImpl.class); @Autowired private AccountService accountService; @Autowired private Validator validator; /** * @see AccountSoapService#getUser(Long) */ @Override public GetUserResult getUser(Long id) { GetUserResult result = new GetUserResult(); try { Validate.notNull(id, "id参数为空"); User user = accountService.getUser(id); Validate.notNull(user, "用户不存在(id:" + id + ")"); UserDTO dto = BeanMapper.map(user, UserDTO.class); result.setUser(dto); return result; } catch (IllegalArgumentException e) { return handleParameterError(result, e); } catch (RuntimeException e) { return handleGeneralError(result, e); } } /** * @see AccountSoapService#searchUser(String, String) */ @Override public SearchUserResult searchUser(String loginName, String name) { SearchUserResult result = new SearchUserResult(); try { List
userList = accountService.searchUser(loginName, name); List
dtoList = BeanMapper.mapList(userList, UserDTO.class); result.setUserList(dtoList); return result; } catch (RuntimeException e) { return handleGeneralError(result, e); } } /** * @see AccountSoapService#createUser(UserDTO) */ @Override public IdResult createUser(UserDTO user) { IdResult result = new IdResult(); try { Validate.notNull(user, "用户参数为空"); User userEntity = BeanMapper.map(user, User.class); BeanValidators.validateWithException(validator, userEntity); accountService.saveUser(userEntity); return new IdResult(userEntity.getId()); } catch (ConstraintViolationException e) { String message = StringUtils.join(BeanValidators.extractPropertyAndMessageAsList(e, " "), "\n"); return handleParameterError(result, e, message); } catch (RuntimeException e) { if (Exceptions.isCausedBy(e, DuplicateKeyException.class)) { String message = "新建用户参数存在唯一性冲突(用户:" + user + ")"; return handleParameterError(result, e, message); } else { return handleGeneralError(result, e); } } } private
T handleParameterError(T result, Exception e, String message) { logger.error(message, e.getMessage()); result.setError(WSResult.PARAMETER_ERROR, message); return result; } private
T handleParameterError(T result, Exception e) { logger.error(e.getMessage()); result.setError(WSResult.PARAMETER_ERROR, e.getMessage()); return result; } private
T handleGeneralError(T result, Exception e) { logger.error(e.getMessage()); result.setDefaultError(); return result; }}

下面我们再来看服务调用者:

配置文件

Apache CXF Web Service Client端配置

调用流程

/** *  * 以用JAXWS的API, 根据AccountWebService接口自行创建. * 使用在Spring applicaitonContext.xml中用
,根据AccountWebService接口创建的Client. * */public class AccountWebServiceWithDynamicCreateClientFT extends BaseFunctionalTestCase { //predefine client @Autowired private AccountSoapService accountWebServiceClient; //dynamic client public AccountSoapService creatClient() { String address = baseUrl + "/cxf/soap/accountservice"; JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean(); proxyFactory.setAddress(address); proxyFactory.setServiceClass(AccountSoapService.class); AccountSoapService accountWebServiceProxy = (AccountSoapService) proxyFactory.create(); // (可选)演示重新设定endpoint address. ((BindingProvider) accountWebServiceProxy).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address); // (可选)演示重新设定Timeout时间 Client client = ClientProxy.getClient(accountWebServiceProxy); HTTPConduit conduit = (HTTPConduit) client.getConduit(); HTTPClientPolicy policy = conduit.getClient(); policy.setReceiveTimeout(600000); return accountWebServiceProxy; } public void getUser() { GetUserResult response = accountWebServiceClient.getUser(1L); return response; } /** * 测试搜索用户 */ @Test public void searchUser() { SearchUserResult response = accountWebServiceClient.searchUser(null, null); return response; } /** * 测试创建用户. */ @Test public void createUser() { User user = UserData.randomUser(); UserDTO userDTO = BeanMapper.map(user, UserDTO.class); IdResult response = accountWebServiceClient.createUser(userDTO); assertThat(response.getId()).isNotNull(); GetUserResult response2 = accountWebServiceClient.getUser(response.getId()); }}

转载于:https://my.oschina.net/suemi/blog/697189

你可能感兴趣的文章
web安全类
查看>>
Vue项目中使用基于pdf.js的vue-pdf插件在pc浏览器下阅览PDF文件
查看>>
编译VIM
查看>>
玩转Elasticsearch源码-一图看懂ES启动流程
查看>>
自动注册appleid
查看>>
OpenTracing Registry登记了129个仪器插件和追踪器
查看>>
使用 Gatsby.js 搭建静态博客 7 文章目录
查看>>
logback.xml日志写入数据库改造,重写源码手工读取yml参数作为数据源参数的方法...
查看>>
一个完整的增删改查模块(以我们的项目‘危化品库管理’模块为例)
查看>>
github GPG 配置
查看>>
javascript 多物体运动
查看>>
JavaScript 之 DOM [ Document对象 ]
查看>>
koa源码阅读[3]-koa-send与它的衍生(static)
查看>>
【spring 注解】第2篇:@ComponentScan
查看>>
从零开始实现一个IDL+RPC框架
查看>>
Docker 容器监控系统初探
查看>>
【跃迁之路】【496天】刻意练习系列255(2018.06.16)
查看>>
CentOS 7服务器下Nginx安装配置
查看>>
前端笔记——JS基础(原型&&原型链)
查看>>
初探正则表达式
查看>>