Spring原生Rpc六种的正确打开方式实现示例
作者:kl 发布时间:2021-06-23 19:35:03
前言
在java生态圈谈到Rpc,很多人可能就会想到Dubbo、Motan、Grpc等框架。但是你知道吗?作为Java编程全家桶的Spring已经内置了多种RPC的实现方式,可以直接使用。存在即合理,有些场景下其实并不需要Dubbo,Grpc等重量级的RPC组件,那么Spring的轻量封装就可以派上用场了。下面就来探索下Spring中的RPC的实现方式以及如何使用的。
文中代码地址:https://gitee.com/kailing/spring-rpc
什么是Rpc?
Rpc(Remote Procedure Call): 封装了内部实现的远程调用过程就是rpc,rpc主要为了简化远程服务调用,通俗的讲就是调用远程服务(跨主机,跨进程)就像调用本地方法一样。Spring Cloud体系中的Fegin 技术也可以认为是采用http协议传输数据的一种Rpc技术。
Spring中的Rpc
Spring中内置了六种不同数据传输方式的原生的Rpc实现,分别是WebService、Jms、Rmi、Http、Hessian(http)、Amqp。熟悉Rpc的知道,在Java中,主要是通过生成服务接口的代理来实现Rpc服务的调用,Dubbo、Motan这样,Spring的实现也是这样。在Rpc服务调用中,有两个角色,分别是服务的提供者和调用者(消费者)。一方面服务调用者通过代理,在服务调用时会传输服务定义的接口名+方法参数给到提供者。另一方面服务提供者拿到接口信息找到本地服务生成调用结果返回给调用者。所以下面所述六种Rpc实现都会有一个公共的服务接口定义,以及各自的代理实现配置。
定义服务接口
/**
* @WebService 注解只用于ws 提供的RPC服务
*/ @WebService public interface AccountService { Account getAccount(String name);
class Account implements Serializable { private String name; public String getName(){ return name;
} public void setName(String name) { this.name = name;
}
}
}
公共的api,在Rpc的提供者和消费者中都会使用到,提供者中会实现这个接口提供服务,消费者会通过代理,生成这个接口的代理实现 ,然后通过底层封装发送具体的消息。和使用dubbo和motan类似
调用服务代码
@SpringBootApplication public class WsConsumerApplication { @Autowired private AccountService accountService; @PostConstruct public void callRpcService(){
System.out.println("RPC远程访问开始!");
System.err.println(accountService.getAccount("kl").getName());
System.out.println("RPC远程访问结束!");
} public static void main(String[] args) {
SpringApplication.run(WsConsumerApplication.class, args);
}
}
每个Rpc实现都一样,都是通过注入AccountService 接口的代理实现来调用服务。不过每个Rpc的代理的配置方式会略有不同,主要体现在不同的传输技术会用到不同的配置。总的来说,连接url(http://127.0.0.1、tcp://172.0.0.1、rmi://127.0.0.1),端口、代理接口信息等都是共同需要的。
WEBSERVICE的RPC实现
服务提供者
服务实现
@WebService(serviceName="AccountService",endpointInterface = "com.spring.rpc.api.AccountService") @Service public class AccountServiceImpl extends SpringBeanAutowiringSupport implements AccountService { Logger logger = LoggerFactory.getLogger(getClass()); @Override @WebMethod public Account getAccount(String name) {
logger.info("{} 请求获取账号!", name);
Account account = new Account();
account.setName(name + "的账号"); return account;
}
}
和其他服务实现不一样,WebService定义服务时,需要使用@WebService和@WebMethod注解标记
服务暴露
@Configuration public class WsConfig { private String ipList = "127.0.0.1"; private String userName = "admin"; private String passWord = "sasa"; @Bean public SimpleHttpServerJaxWsServiceExporter rmiServiceExporter(Authenticator authenticator) {
SimpleHttpServerJaxWsServiceExporter exporter = new SimpleHttpServerJaxWsServiceExporter();
exporter.setHostname("127.0.0.1");
exporter.setPort(8083);
exporter.setAuthenticator(authenticator); return exporter;
} @Bean public Authenticator authenticator(){
Authenticator authenticator = new Authenticator();
authenticator.setIpList(ipList);
authenticator.setUserName(userName);
authenticator.setPassWord(passWord); return authenticator;
}
}
完成如上代码,其实我们已经构建了一个完整的WebService服务,而且还加上了用户、密码和ip白名单等接口权限认证,访问:http://127.0.0.1:8083/AccountServiceImpl?WSDL 就可以看到服务的定义,如下:
服务消费者
@Configuration public class WsConfig { @Bean("accountService") public JaxWsPortProxyFactoryBean accountService()throws Exception{
JaxWsPortProxyFactoryBean factoryBean = new JaxWsPortProxyFactoryBean();
factoryBean.setServiceName("AccountService");
factoryBean.setPortName("AccountServiceImplPort");
factoryBean.setNamespaceUri("http://provider.ws.rpc.spring.com/");
URL wsdlDocumentUrl = new URL("http://127.0.0.1:8083/AccountServiceImpl?WSDL");
factoryBean.setWsdlDocumentUrl(wsdlDocumentUrl);
factoryBean.setServiceInterface(AccountService.class);
factoryBean.setUsername("admin");
factoryBean.setPassword("sasa"); return factoryBean;
}
}
通过声明JaxWsPortProxyFactoryBean来获得AccountService.class的代理实例。当注入服务调用方法时,实际上是触发了一次WebService的远程调用
HTTP的RPC实现
服务提供者
服务实现
@Service public class AccountServiceImpl implements AccountService { Logger logger = LoggerFactory.getLogger(getClass()); @Override public Account getAccount(String name) {
logger.info("{} 请求获取账号!", name);
Account account = new Account();
account.setName(name + "的账号"); return account;
}
}
服务暴露
@Configuration public class HttpConfig { @Bean("/AccountService") public HttpInvokerServiceExporter rmiServiceExporter(AccountServiceImpl accountService){
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
exporter.setService(accountService);
exporter.setServiceInterface(AccountService.class); return exporter;
} @Bean public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
ServletRegistrationBean servlet = new ServletRegistrationBean();
servlet.setServlet(dispatcherServlet);
servlet.setName("remoting");
servlet.setLoadOnStartup(1);
servlet.addUrlMappings("/remoting/*"); return servlet;
}
}
服务消费者
@Configuration public class HttpConfig { @Bean("accountService") public HttpInvokerProxyFactoryBean accountService(){
HttpInvokerProxyFactoryBean factoryBean = new HttpInvokerProxyFactoryBean();
factoryBean.setHttpInvokerRequestExecutor(new HttpComponentsHttpInvokerRequestExecutor());
factoryBean.setServiceUrl("http://127.0.0.1:8081/remoting/AccountService");
factoryBean.setServiceInterface(AccountService.class); return factoryBean;
}
}
可以看到,在配置Http实现的Rpc服务消费者时,和WebService是类似的,定义一个FactoryBean就ok了。其实其他的四种Rpc实现也都大同小异。后面就不一一列举了
文末结语
博文起草构思的时候本来打算将Spring中内置六种Rpc实现都详细描述下,后面看着看着就觉得使用起来真的很类似。只不过像Amqp和Jms以及WebService等实现需要有这方面技术经验的人才能看的明白。但单就Rpc使用和实现来说基本差不多,所以后面就没有一一列出占用篇幅。但是上面提到的WebService、Jms、Rmi、Http、Hessian、Amqp这六种实现在上面的git仓库中都有详细的实例程序。感兴趣的不妨下载下来跑一跑,看下每个实现的代理工厂类都是如何实现的,非常有助于你真正理解Rpc的调用过程,以及实现自己的Rpc轮子。
来源:http://www.kailing.pub/article/index/arcid/241.html


猜你喜欢
- 本文实例为大家分享了Android实现表情功能的具体代码,供大家参考,具体内容如下Dialog实现表情评论功能核心问题:1、如何得到键盘弹起
- 不说废话了,进入我们今天的主题吧。先贴上前面内容的地址:Android手势ImageView三部曲(一)Android手势ImageView
- 什么是注解?对于很多初次接触的开发者来说应该都有这个疑问?Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安
- SpringCloud 整合ribbon的时候出现了这个问题java.lang.IllegalStateException: No inst
- 多线程一直是工作或面试过程中的高频知识点,今天给大家分享一下使用 ThreadPoolTaskExecutor 来自定义线程池和实现异步调用
- 前言aop面向切面编程,是编程中一个很重要的思想本篇文章主要介绍的是SpringBoot切面Aop的使用和案例什么是aopAOP(Aspec
- 1.取整运算符取整从字面意思理解就是被除数到底包含几个除数,也就是能被整除多少次,那么它有哪些需要注意的地方呢?先看下面的两端代码: &nb
- 本文实例讲述了C#编程调用Cards.dll实现图形化发牌功能。分享给大家供大家参考,具体如下:using System;using Sys
- 配置AOPAOP简介要介绍面向切面变成(Aspect-Oriented Programming,AOP),需要先考虑一个这样的场景:公司有一
- 背景由于前前前阵子写了个壳,得去了解类的加载流程,当时记了一些潦草的笔记。这几天把这些东西简单梳理了一下,本文分析的代码基于Android8
- 本文告诉大家简单的方法进行时间计算。实际上使用 TimeSpan 可以做到让代码比较好懂,而代码很简单。例如我使用下面的代码表示 5 秒co
- Style.xml的妙用Style.xml之于Android犹如css之于Jsp妙用<?xml version="1.0&q
- Java中线程分为两种类型:用户线程和守护(服务)线程。通过Thread.setDaemon(false)设置为用户线程;通过Thread.
- 解决 INSTALL FAILED CONFLICTING PROVIDER的问题方法 在安装Android应用时出现
- 本文实例讲述了Android TextView跑马灯效果实现方法。分享给大家供大家参考,具体如下:public class MyTextVi
- 编写C#程序的时候,我们都遇到过配置文件,而如今绝大多数的配置文件都是用XML写的。所以在处理的时候就需要操作XML文件。那么C#如何操作X
- 本文实例讲述了C#中IEnumerable接口用法。分享给大家供大家参考。具体分析如下:枚举数可用于读取集合中的数据,但不能用于修改基础集合
- 前言Ehcache 是一个成熟的缓存框架,你可以直接使用它来管理你的缓存。Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案
- 最近完成的差不多的项目突然需要加退款的流程需求了,所以来小小的实现以下。其实对比其他的支付和退款来说,支付宝算是特别专业,也是特别简单的一个
- 题目描述:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数