资讯专栏INFORMATION COLUMN

也来谈谈RPC

NSFish / 2706人阅读

摘要:前言,顾名思义即远程过程调用,可以说是分部式应用的基础,也是概念中的核心部分。对于来说,可以分为之间的调用与和其他平台之间的调用。和分别代表了这两种模式。

前言

RPC,顾名思义即远程过程调用,可以说是分部式WEB应用的基础,也是SOA概念中的核心部分。对于J2EE来说,可以分为JVM之间的调用与和其他平台之间的调用。前者主要是RMI,而后者则五花八门,比如Apache的Thrift框架就支持多语言系统之间的调用.今天就让我们来初窥以下这大名鼎鼎的RPC。

问题描述

要想实现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, " "), "
");
            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://www.ucloud.cn/yun/64714.html

相关文章

  • 谈谈为什么需要服务治理(Dubbo)

    摘要:服务治理主要针对于当前分布式架构下多服务微服务等。随着业务的增长,服务不能一味地随之增长,需要管理治理。服务设计期主要针对于服务的设计评审以及标准的制定。服务治理后期的重点放在消除冗余。 服务治理主要针对于当前分布式架构下多服务、微服务等。 服务是分布式系统下的一个不大不小的部分,有了服务的组成,整个系统才能活起来。 随着业务的增长,服务不能一味地随之增长,需要管理、治理。没有服务治理...

    yunhao 评论0 收藏0
  • Spring Cloud Alibaba到底坑不坑?

    摘要:我没有能力去控制那些自媒体发布这些不实的内容,但是在我了解的范围内,还是尽力输出一些我的理解。 之前我发过一篇《说说我为什么看好Spring Cloud Alibaba》,然后这两天有网友给我转了这篇文章《坑爹项目spring-cloud-alibaba,我们也来一个》,问我的看法是怎么样的,聊天时候简单说了一下。今天在家休息,抽空整理一下内容,逐点说一下我的看法,主要还是觉得这篇文章...

    娣辩孩 评论0 收藏0
  • 服务器部署工具 - 收藏集 - 掘金

    摘要:基本入门前端掘金作者本文属于翻译文章,原文链接为。如果如何把应用放在容器中运行掘金本文适合零基础,且希望使用运行应用的人士。后端掘金使用构建网站。 nginx 基本入门 - 前端 - 掘金作者:villainthr 本文属于翻译文章,原文链接为 nginx Beginner’s Guide。是至今为止见过最好的 nginx 入门文章。额。。。没有之一。 这篇教程简单介绍了 nginx ...

    Shonim 评论0 收藏0
  • RabbitMQ快速入门

    摘要:就是交换机生产者发送消息给交换机,然后由交换机将消息转发给队列。对应于中则是发送一个,处理完成之后将其返回给。这样来说一个是级别而不是级别的了。当然这些也都是官网的入门例子,后续有机会的话再深入研究。 一、前言 RabbitMQ其实是我最早接触的一个MQ框架,我记得当时是在大学的时候跑到图书馆一个人去看,由于RabbitMQ官网的英文还不算太难,因此也是参考官网学习的,一共有6章,当时...

    Moxmi 评论0 收藏0
  • 架构 - 收藏集 - 掘金

    摘要:浅谈秒杀系统架构设计后端掘金秒杀是电子商务网站常见的一种营销手段。这两个项目白话网站架构演进后端掘金这是白话系列的文章。 浅谈秒杀系统架构设计 - 后端 - 掘金秒杀是电子商务网站常见的一种营销手段。 不要整个系统宕机。 即使系统故障,也不要将错误数据展示出来。 尽量保持公平公正。 实现效果 秒杀开始前,抢购按钮为活动未开始。 秒杀开始时,抢购按钮可以点击下单。 秒杀结束后,按钮按钮变...

    Riddler 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<