百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

常用的RPC框架(rpc框架是什么意思)

ccwgpt 2024-09-17 12:49 128 浏览 0 评论

1. 为什么要使用RPC?

RPC(remote procedure call)是指远程过程调用,比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上的应用提供的接口,由于不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。

RPC(remote procedure call,远程过程调用):

首先,要解决通讯的问题,主要是通过客户端和服务器端之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享一个连接。

第二,要解决寻址的问题,A服务器上的应用要调用B服务器上的应用,A服务器上的应用需要通过底层RPC框架得知:如何连接到B服务器(主机或IP地址)以及特定的端口,方法的名称等信息,这样才能完成调用。

第三,A服务器上的应用发起远程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数需要序列化成二进制形式,然后再通过寻址和传输将序列化的二进制发送给B服务器。

第四:B服务器收到请求后,需要进行反序列化,恢复为内存中的表达方式,然后找到对应的方法进行本地调用并返回,序列化返回值并发送给A服务器。

第五:A服务器收到B服务器的返回值后,进行反序列化,恢复为内存中的表达方式,然后交给A服务器上的应用进行处理。

RPC的协议有很多,比如Java RMI、WebService的RPC风格、Hession、Thrift、REST API

2. RPC、RMI、SOAP、REST的区别RMI(remote method invocation,面向对象的远程方法调用)

RPC(remote procedure call,远程过程调用)



SOAP(simple object access protoal,简单对象访问协议)

REST(representational state transfer,表达性状态转移)可以都理解为调用远程方法的一些通信技术“风格”:

· RMI就好比它是本地工作,采用tcp/ip协议,客户端直接调用服务端上的一些方法。优点是强类型,编译期可检查错误,缺点是只能基于JAVA语言,客户机与服务器紧耦合。

· RPC是一个泛化的概念,严格来说一切远程过程调用手段都属于rpc范畴,包括rmi、hessian、soap、thrift、protobuf等等。

· SOAP是在XML-RPC基础上,使用标准的XML描述了RPC的请求信息(URI/类/方法/参数/返回值)。因为XML-RPC只能使用有限的数据类型种类和一些简单的数据结构,SOAP能支持更多的类型和数据结构。优点是跨语言,非常适合异步通信和针对松耦合的C/S,缺点是必须做很多运行时检查。

比较,它采用简单的URL方式来代替一个对象,优点是轻量,可读性较好,不需要其他类库支持,缺点是URL可能会很长,不容易解析。

3. Java RMI

Java RMI(Romote Method Invocation)是一种基于Java的远程方法调用技术,是Java特有的一种RPC实现。它能够部署在不同主机上的Java对象之间进行透明的通信与方法调用。

RMI工作原理:

首先,在一个JVM中启动rmiregistry服务,启动时可以指定服务监听的端口,也可以使用默认的端口。

其次,RMIServer在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming,Context,Registry等类的bind或rebind方法将刚才实例化好的实现类注册到RMIService上并对外暴露一个名称。

最后,RMIClient通过本地的接口和一个已知的名称(即RMIServer暴露出的名称)再使用RMI提供的Naming,Context,Registry等类的lookup方法从RMIService那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,想怎么调就怎么调。

RMI 采用stubs 和 skeletons 来进行远程对象(remote object)的通讯。stub 充当远程对象的客户端代理,有着和远程对象相同的远程接口,远程对象的调用实际是通过调用该对象的客户端代理对象stub来完成的,通过该机制RMI就好比它是本地工作,采用tcp/ip协议,客户端直接调用服务端上的一些方法。优点是强类型,编译期可检查错误,缺点是只能基于Java语言,客户机与服务器紧耦合。

RMI使用Demo:

定义RMI对外服务接口:RMI接口方法定义必须显示声明抛出RemoteException异常。

package com.yyy.RMIDemo.java.server;

 

import java.rmi.Remote;

import java.rmi.RemoteException;

 

/**

 * RMI对外服务接口
 *

 */

public interface HelloService extends Remote{

	public String sayHello(String someone) throws RemoteException;

}

服务端接口实现:服务端方法实现必须继承UnicastRemoteObject类,该类定义了服务调用方与服务提供方对象实例,并建立一对一的连接。

package com.yyy.RMIDemo.java.server;

 

import java.rmi.RemoteException;

import java.rmi.server.UnicastRemoteObject;

 

/**

 * 服务器端接口实现

 *

 */

public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {

 

	private static final long serialVersionUID = 4176511759435216154L;

 

	protected HelloServiceImpl() throws RemoteException {

		super();

		// TODO Auto-generated constructor stub

	}

 

	@Override

	public String sayHello(String someone) throws RemoteException {

		// TODO Auto- generated method stub

		return "Hello" + someone;

	}

 

}

RMI的通信端口是随机产生的,因此有可能被防火墙拦截,为了防止被防火墙拦截,需要强制指定RMI的通信端口。一般通过自定义一个RMISocketFactory类来实现,代码如下:
package com.yyy.RMIDemo.java.server;

 

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

import java.rmi.server.RMISocketFactory;

 

public class CustomerSocketFactory extends RMISocketFactory{

 

	

	@Override

	public ServerSocket createServerSocket(int port) throws IOException {

		// TODO Auto-generated method stub

		if (port == 0) {

			port = 8051;

		}

		System.out.println("rmi notify port : " + port);

		return new ServerSocket(port);

	}

 

	//指定通信端口,防止被防火墙拦截

	@Override

	public Socket createSocket(String host, int port) throws IOException {

		// TODO Auto-generated method stub

		return new Socket(host, port);

	}

 

}

RMI的通信端口是随机产生的,因此有可能被防火墙拦截,为了防止被防火墙拦截,需要强制指定RMI的通信端口。一般通过自定义一个RMISocketFactory类来实现,代码如下:

package com.yyy.RMIDemo.java.server;

 

import java.rmi.Naming;

import java.rmi.registry.LocateRegistry;

import java.rmi.server.RMISocketFactory;

 

public class ServerMain {

	public static void main(String[] args) throws Exception {

		//创建服务

		HelloService helloService = new HelloServiceImpl();

		//注册服务

		LocateRegistry.createRegistry(8801);

		//指定通信端口,防止被防火墙拦截

		RMISocketFactory.setSocketFactory(new CustomerSocketFactory());

		

		Naming.bind("rmi://localhost:8801/helloService", helloService);

		

		System.out.println("ServceMain provide service now");

	}

}
客户端远程调用RMI服务代码:
package com.yyy.RMIDemo.java.client;

 

import java.rmi.Naming;

 

import com.yyy.RMIDemo.java.server.HelloService;

 

public class ClientMain {

	public static void main(String[] args) throws Exception {

		HelloService helloService = 

				(HelloService)Naming.lookup("rmi://localhost:8801/helloService");

		System.out.println("RMI 客户端接收到的结果是:" + helloService.sayHello("RMI"));

	}

}

服务端RMI服务启动:

package com.yyy.RMIDemo.java.server;

 

import java.rmi.Naming;

import java.rmi.registry.LocateRegistry;

import java.rmi.server.RMISocketFactory;

 

public class ServerMain {

	public static void main(String[] args) throws Exception {

		//创建服务

		HelloService helloService = new HelloServiceImpl();

		//注册服务

		LocateRegistry.createRegistry(8801);

		//指定通信端口,防止被防火墙拦截

		RMISocketFactory.setSocketFactory(new CustomerSocketFactory());

		

		Naming.bind("rmi://localhost:8801/helloService", helloService);

		

		System.out.println("ServceMain provide service now");

	}

}

客户端远程调用RMI服务代码:

package com.yyy.RMIDemo.java.client;

 

import java.rmi.Naming;

 

import com.yyy.RMIDemo.java.server.HelloService;

 

public class ClientMain {

	public static void main(String[] args) throws Exception {

		HelloService helloService = 

				(HelloService)Naming.lookup("rmi://localhost:8801/helloService");

		System.out.println("RMI 客户端接收到的结果是:" + helloService.sayHello("RMI"));

	}

}

先运行服务器端程序ServerMain,然后运行ClinetMain,运行结果如下:

RMI 客户端接收到的结果是:HelloRMI

4. Hessian

Hessian 是由 caucho 提供的一个基于 binary-RPC 实现的远程通讯 library 。Hessian基于Http协议进行传输。

Binary-RPC 是一种和 RMI 类似的远程调用的协议,它和 RMI 的不同之处在于它以标准的二进制格式来定义请求的信息 ( 请求的对象、方法、参数等 ) ,这样的好处是什么呢,就是在跨语言通讯的时候也可以使用。传输协议基于TCP。

Hessian可通过Servlet提供远程服务,需要将匹配某个模式的请求映射到Hessian服务。也可Spring框架整合,通过它的DispatcherServlet可以完成该功能,DispatcherServlet可将匹配模式的请求转发到Hessian服务。Hessian的server端提供一个servlet基类, 用来处理发送的请求,而Hessian的这个远程过程调用,完全使用动态代理来实现的,,建议采用面向接口编程,Hessian服务通过接口暴露。

Hessian处理过程示意图:客户端——>序列化写到输出流——>远程方法(服务器端)——>序列化写到输出流 ——>客户端读取输入流——>输出结果

Spring + Hessian实现服务器端的demo:

在工程中导入hessian的jar包在web.xml中,我们配置SpringMVC的DispatcherServlet:

<!-- Spring hessian servlet -->

	<servlet>  

		<servlet-name>hessian</servlet-name>  

		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  

		<init-param>  

			<param-name>contextConfigLocation</param-name>  

			<param-value>classpath:spring-hessian.xml</param-value>  

		</init-param>  

		<load-on-startup>1</load-on-startup>  

		<async-supported>true</async-supported>  

	</servlet>  

	<servlet-mapping>  

		<servlet-name>hessian</servlet-name>  

		<url-pattern>/pubservice/*</url-pattern>  

	</servlet-mapping>

spring-hessian.xml的配置:

<bean id="/testService" class="com.example.platform.hession.MyHessianServiceExporter">

		<property name="service" ref="testServiceImpl" />

		<property name="serviceInterface" value="com.xinwei.platform.pubservice.TestService" />	

	</bean>

使用了org.springframework.remoting.caucho.HessianServiceExporter来发布服务。将程序部署在tomcat中。如果我们想从HessianServiceExporter的handleRequest方法中可以获得客户端request,那我们就会有很多种方法得到此request;Aop方式或重写方法,我们采用重写的方式:

/**

 * 

 */

package com.example.platform.hession;

 

import javax.servlet.ServletRequest;

 

/**

 * Hession service线程上下文,用以线程安全地保存客户端request


*

 */

public class HessionContext {

	private ServletRequest request;

	private static final ThreadLocal<HessionContext> localContext

							= new ThreadLocal<HessionContext>(){

		@Override

		public HessionContext initialValue(){

			return new HessionContext();

		}	

	};

	

	private HessionContext(){

		

	}

 

 

	public static ServletRequest getRequest() {

		return localContext.get().request;

	}

 

 

	public static void setRequest(ServletRequest request) {

		localContext.get().request = request;

	}

	

	public static void clear() {

		localContext.get().request = null;

	}

	

	
}

自定义类:

/**

 * 

 */

 

import java.io.IOException;

 

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.remoting.caucho.HessianExporter;

import org.springframework.web.HttpRequestHandler;

import org.springframework.web.HttpRequestMethodNotSupportedException;

import org.springframework.web.util.NestedServletException;

 

/**

*

 */

public class MyHessianServiceExporter extends HessianExporter implements HttpRequestHandler {

 

	private static final Logger logger = LoggerFactory.getLogger(InmpServiceExporter.class);

		

 

	@Override

	public void handleRequest(HttpServletRequest request, 

								HttpServletResponse response)

								throws ServletException, IOException {

		// TODO Auto-generated method stub

		if (!"POST".equals(request.getMethod())){

			throw new HttpRequestMethodNotSupportedException(

					request.getMethod(), new String[]{"POST"},

					"HessionSeriviceExporter only supports POST requests");

		}

		response.setContentType(CONTENT_TYPE_HESSIAN);

		try {

			//保存request到Hession线程上下文

			HessionContext.setRequest(request);

			invoke(request.getInputStream(), response.getOutputStream());

		} catch (Throwable ex) {

			// TODO: handle exception

			logger.error("Hession skeleton invocation failed");

			throw new NestedServletException("Hession skeleton invocation failed", ex);

		} finally{

			HessionContext.clear();

		}

		

		

	}

 

}

Service开发中就可以直接使用 HessianContext.getRequest(); 来获得客户端的request了,所有的客户端信息随你所用了,而且是线程安全的。


服务器端服务接口:

package com.example.platform.pubservice;

 

public interface TestService {

	public String sayHello(String datas);

}

接口实现类:

package com.example.platform.pubservice.impl;

 

import com.example.platform.pubservice.TestService;

 

public class TestServiceImpl extends InmpHessianHandle implements TestService{

 

	@Override

	public String sayHello(String datas) {

		// TODO Auto-generated method stub

		return "Hello " + datas;

	}

 

}

client调用:

public class TestClient {

    public static void main(String[] args) {

        try {

            String url = "http://localhost:8080/HessianDemo/pubservice/testService";

            HessianProxyFactory factory = new HessianProxyFactory();

            factory.setOverloadEnabled(true);

            TestService testService = (TestService) factory.create(TestService.class, url);

            System.out.println(basic.sayHello("SW"));

        }catch (Exception e){

            e.printStackTrace();

        }

    }

}

5. WebService

WebService是一种跨平台的RPC技术协议。

WebService技术栈由SOAP(简单对象访问协议)、UDDI(统一描述、发现与集成)、WSDL(网络服务描述语言:用来描述Service、以及如何访问WebService)组成。SOAP是一种使用XML进行数据编码的通信协议。独立于平台、独立于语言,简单可扩展。

WebService常用的两种实现:CXF和Axis2

详解见 从零开始写分布式服务框架 1.3小节

目前三种主流的web服务实现方法:

REST(新型):表象化状态转变 (软件架构风格)RESTEasy、Wink、CXF、Axis2…….

SOAP(比较成熟):简单对象访问协议 Xfire、Axis2、CXF、Axis1X

ML-RPC(淘汰):远程过程调用协议(慢慢被soap 所取代)

1、Java开发WebService最重要的两个规范:

JSR-224 (JAX-WS:Java API for XML-Based Web Services ) ,主要使用soap协议,使用wsdl来描述;

JSR-311 (JAX-RS:The Java API for RESTful Web Services),简化了 web service 的设计,它不再需要 wsdl ,也不再需要 soap 协议,而是通过最简单的 http 协议传输数据 ( 包括 xml 或 json) 。既简化了设计,也减少了网络传输量(因为只传输代表数据的 xml 或 json ,没有额外的 xml 包装)。

JAX-WS是针对WebService。而JAX-RS是针对RESTful HTTP Service。RESTful HTTP Service相关介绍可以参考:RESTful Service API 设计最佳工程实践和常见问题解决方案JAX-WS与JAX-RS两者是不同风格的SOA架构。前者以动词为中心,指定的是每次执行函数。而后者以名词为中心,每次执行的时候指的是资源。

JAX-RS是JAVA EE6 引入的一个新技术。是一个Java 编程语言的应用程序接口,支持按照表述性状态转移(REST)架构风格创建Web服务。

JAX-RS使用了Java SE5引入的Java标注来简化Web服务的客户端和服务端的开发和部署。

JAX-WS规范是一组XML web services的JAVA API,JAX-WS允许开发者可以选择RPC-oriented或者message-oriented 来实现自己的web services。

支持JAX-WS服务规范的框架有:CXF,Axis,Xfire。结合java语言均可可实现JAX-WS

支持JAX-RS服务规范的框架有:

1.CXF——XFire和Celtix的合并

2.Jersey——Sun公司的JAX-RS参考实现。

3.RESTEasy——JBoss的JAX-RS项目。

4.Restlet——也许是最早的REST框架了,它JAX-RS之前就有了。

在分布式服务框架中,除了实现RPC的特性以外,还包括负载均衡策略以及实现,服务的注册、发布与引入。服务的高可用策略以及服务治理等特性。

JAVA进阶架构程序员福利:我这里还总结整理了比较全面的JAVA相关的面试资料,都已经整理成了

PDF版,这些都可以分享给大家,关注私信我:【806】,免费领取!

相关推荐

盲盒小程序背后的技术揭秘:如何打造个性化购物体验

在2025年的今天,盲盒小程序作为一种新兴的购物方式,正以其独特的魅力和个性化体验吸引着越来越多的消费者。这种将线上购物与盲盒概念相结合的应用,不仅为消费者带来了未知的惊喜,还通过一系列技术手段实现了...

小程序·云开发已支持单日亿级调用量,接口可用率高达99.99%

2019-10-1914:1210月19日,由腾讯云与微信小程序团队联合举办的“小程序·云开发”技术峰会在北京召开。会上,微信小程序团队相关负责人表示“小程序·云开发”系统架构已经支持每天亿级别的...

程序员副业开启模式:8个GitHub上可以赚钱的小程序

前言开源项目作者:JackonYang今天推荐的这个项目是「list-of-wechat-mini-program-list」,开源微信小程序列表的列表、有赚钱能力的小程序开源代码。这个项目分为两部分...

深度科普:盲盒小程序开发的底层逻辑

在当下的数字化浪潮中,盲盒小程序以其独特的趣味性和互动性,吸引着众多消费者的目光。无论是热衷于收集玩偶的年轻人,还是享受拆盒惊喜的上班族,都对盲盒小程序情有独钟。那么,这种备受欢迎的盲盒小程序,其开发...

微信小程序的制作步骤

SaaS小程序制作平台,作为数字化转型时代下的创新产物,不仅将易用性置于设计的核心位置,让非技术背景的用户也能轻松上手,快速制作出功能丰富、界面精美的小程序,更在性能和稳定性方面投入了大量精力,以确保...

携程开源--小程序构建工具,三分钟搞定

前言今天推荐的这个项目是「wean」,一个小程序构建打包工具。在wean之前,大量小程序工具使用webpack进行打包,各种loader、plugin导致整个开发链路变长。wean旨在解...

校园小程序的搭建以及营收模式校园外卖程序校园跑腿校园圈子系统

校园小程序的架构设计主要包括云端架构和本地架构两部分。云端架构方面,采用Serverless架构可以降低技术门槛,通过阿里云、腾讯云等平台提供的云服务,可以实现弹性扩容和快速部署。例如,使用云数据库、...

盲盒小程序开发揭秘:技术架构与实现原理全解析

在2025年的今天,盲盒小程序作为一种结合了线上购物与趣味性的创新应用,正受到越来越多用户的喜爱。其背后的技术架构与实现原理,对于想要了解或涉足这一领域的人来说,无疑充满了神秘与吸引力。本文将为大家科...

月活百万的小程序架构设计:流量暴增秘籍

从小程序到"大"程序的蜕变之路当你的小程序用户量从几千跃升至百万级别时,原有的架构就像一件不合身的衣服,处处紧绷。这个阶段最常遇到的噩梦就是服务器崩溃、接口超时、数据丢失。想象一下,在...

认知智能如何与产业结合?专家学者共探理论框架与落地实践

当前,以大模型为代表的生成式人工智能等前沿技术加速迭代,如何将认知智能与产业结合,成为摆在各行各业面前的一个问题。论坛现场。主办方供图7月4日,2024世界人工智能大会暨人工智能全球治理高级别会议在...

现代中医理论框架

...

认知行为(CBT)中的ABC情绪理论

情绪ABC理论是由美国心理学家阿尔伯特·艾利斯(AlbertEllis1913-2007)创建的理论,A表示诱发性事件(Activatingevent),B表示个体针对此诱发性事件产生的一些信...

说说卡伦霍妮的理论框架,对你调整性格和人际关系,价值很大

01自在今天我主要想说下霍妮的理论框架。主要说三本书,第一本是《我们时代的神经症人格》,第二本是《我们内心的冲突》,第三本是《神经症与人的成长》。根据我的经验,三本书价值巨大,但并不是每个人都能读进去...

供应链管理-理论框架

一个最佳价值的供应链,应该是一个具有敏捷性、适应性和联盟功能(3A)的供应链,其基本要素包括战略资源、物流管理、关系管理以及信息系统,目标是实现速度、质量、成本、柔性的竞争优势。篇幅有...

微信WeUI设计规范文件下载及使用方法

来人人都是产品经理【起点学院】,BAT实战派产品总监手把手系统带你学产品、学运营。WeUI是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信Web开发量身设计,可以令用户的使用感知...

取消回复欢迎 发表评论: