Java实现远程服务生产与消费(RPC)的4种方法-RMI,WebService,HttpClient,RestTemplate
本文将通过具体的远程服务发布与消费案例展示4种RPC远程调用方法.
一. 通过rmi实现远程服务的生产与消费
- Java自身提供了
java.rmi
包, 方便开发者进行远程服务的部署与消费, 下面将通过具体案例进行讲解.
远程服务提供者实现.
创建rmi-provider项目(Maven)
- 创建
UserService
接口.
//将要发布的服务的接口
public interface UserService extends Remote {
public String helloRmi(String name) throws RemoteException;
}
- 创建
UserServiceImpl
实现类
- 注意,
UserServiceImpl
除了实现UserService
接口外, 还要继承UnicastRemoteObject
类, 你可以理解为它是一个发布出去供他人调用的类, 当UserServiceImpl
实现了这个类后,UserServiceImpl
就能被发布出去供别人调用.
//将要发布的服务的实现类
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
public UserServiceImpl() throws RemoteException {
super();
}
public String helloRmi(String name) throws RemoteException {
return "hello " + name;
}
}
- 发布远程服务
public static void main(String[] args) {
try {
//完成远程服务的发布
LocateRegistry.createRegistry(8888);//将远程服务发布在本地的8888端口
String name = "rmi://localhost:8888/rmi";//发布的远程服务被访问的url
UserService userService = new UserServiceImpl();//创建一个提供具体服务的远程对象
Naming.bind(name, userService);//给远程服务绑定一个url
System.out.println("--- 已发布rmi远程服务 ---");
} catch (Exception e) {
e.printStackTrace();
}
}
远程服务消费者实现
创建rmi-consumer项目
- 把
rmi-provider
项目种的UserService
接口与UserServiceImpl
实现类复制到本rmi-consumer
项目中.(这一步可以进行优化解耦, 我们可以多创建一个rmi-resource
项目, 让rmi-provider
和rmi-consumer
共同依赖rmi-resource
项目, 然后把资源文件比如远程服务所用到的UserService
等放入rmi-resource
项目中) - 远程服务消费者对远程服务发起调用.
public static void main(String[] args) {
try {
//发布远程服务的访问url
String name = "rmi://localhost:8888/rmi";
//通过发布远程服务的url, 获取远程服务的代理对象
UserService userService = (UserService) Naming.lookup(name);
System.out.println("获得的远程服务的代理对象:" + userService.getClass().getName());
String result = userService.helloRmi("rmi");//拿到远程方法调用的结果
System.out.println("result: " + result);
}catch (Exception e) {
e.printStackTrace();
}
}
//最后输出
获得的远程服务的代理对象:com.sun.proxy.$Proxy0
result: hello rmi
- 通过最后的输出我们看到获得的远程服务对象是动态代理产生的.
二. 通过WebService实现远程服务的生产与消费
- WebService协议是RPC的一种具体实现, 服务提供方和消费方通过
http + xml
进行通信.
远程服务提供者实现.
- 首先创建远程服务接口
UserService
及其实现类UserServiceImpl
.
- 注意, 使用
WebService
时需要对远程服务加上注解@WebService
@WebService
public interface UserService {
public String sayHello(String name);
}
@WebService
public class UserServiceImpl implements UserService {
@Override
public String sayHello(String name) {
return "hello " + name + "~";
}
}
- 发布远程服务, 过程和
rmi
差不多, 需要提供远程服务的访问地址和具体的远程服务实现类, 使用Endpoint
类的publish()
方法进行发布, 这都是JDK封装好的.
public class WsProviderApp {
public static void main(String[] args) {
//发布的WebService的被访问地址
String address = "http://localhost:9999/ws";
//创建远程服务对象
UserService userService = new UserServiceImpl();
//发布服务
Endpoint.publish(address, userService);
System.out.println("远程服务已经发布...");
}
}
查看远程服务文档wdsl
- 和
rmi
不同的是, WebService发布后, 调用者可以通过查看它的文档对远程服务发起调用. - 查看的方法是在浏览器中输入远程服务的访问地址加上
?wdsl
, 比如本案例中是http://localhost:9999/ws?wsdl
- 注意, 在客户端调用远程方法时需要用工具对wdsl文档进行解析, 并获得调用远程方法的工具类. 具体操作见下一段.
远程服务消费者实现.
- 首先根据文档获得调用远程服务的工具类, JDK已经为我们封装好了获取的工具, 它在
bin
目录下, 名字是wsimport
- 打开命令行, 在命令行中输入解析命令
wsimport -keep -d C:\githubRepositories\shopping\ws-consumer\src\main\java -p com.shenghao.client http://localhost:9999/ws?wsdl
解释:
1. wsimport 是命令的名字
2. -keep 用于保留生成的类, 如果没有该指令会只生成class文件
3. -d 后面接项目中存放这些工具类的包, 填绝对路径
4. -p 填wdsl文档的地址
5. 可以看到命令执行完后, 指定的包中出现一堆相关的类, 最直接调用到的类是UserServiceImplService
. 下面演示对远程方法进行调用.
public static void main(String[] args) {
//创建服务类对象
UserServiceImplService service = new UserServiceImplService();
//获得远程服务的代理对象
UserServiceImpl userService = service.getUserServiceImplPort();
System.out.println(userService.getClass().getName());
//对远程服务对象的方法进行调用
String result = userService.sayHello("炭烧生蚝");
System.out.println(result);
}
//结果输出
com.sun.proxy.$Proxy32
hello 炭烧生蚝~
三. 通过HttpClient实现远程服务的生产与消费
- 这里我们换一个案例进行演示. 假设现在有一套用户系统和一套订单系统, 要实现用户系统访问订单系统以获得某个用户的订单信息.
远程服务提供者实现
- 提供远程服务的过程和响应web请求很相似, 只不过响应的不是
<html>
标签, 而是json
字符串. 微信小程序前后端通信也是这个原理.
- 创建名为
order-sys
的Maven项目, 指定打包为war
包.
点击这里查看pom.xml文件, 常规操作
<properties>
<!-- spring 依赖 -->
<spring.version>4.3.18.RELEASE</spring.version>
<jstl.version>1.2</jstl.version>
<servlet-api.version>2.5</servlet-api.version>
<jsp-api.version>2.0</jsp-api.version>
<jackson.version>2.9.0</jackson.version>
</properties>
<dependencies>
<!-- jsp相关依赖 -->
<!-- servlet依赖 -->
<!-- jstl依赖 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp-api.version}</version>
<scope>provided</scope>
</dependency>
<!-- springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
<build>
<finalName>order</finalName>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/order</path>
<port>7070</port>
</configuration>
</plugin>
</plugins>
</build></code></p>
2. 创建订单类
public class Order {
private String id;
private Double total;
private String date;
//get / set ...
}
- 对外提供服务, 发布时打包发布到
Tomcat
上
@Controller
public class OrderController {
/**
* 接收http请求, 响应订单集合, 异步响应
* 将list集合序列化为json串响应
* @param uid
* @return
*/
@RequestMapping("/loadOrderList2")
@ResponseBody
public List<Order> loadOrderList2(String uid){
System.out.println("uid: " + uid);
//模拟订单数据
Order o1 = new Order();
o1.setId("111");
o1.setTotal(333.33);
o1.setDate("2019-4-29");
Order o2 = new Order();
o2.setId("222");
o2.setTotal(444.44);
o2.setDate("2019-5-29");
Order o3 = new Order();
o3.setId("333");
o3.setTotal(555.55);
o3.setDate("2019-6-29");
List<Order> list = new ArrayList<>();
list.add(o1);
list.add(o2);
list.add(o3);
return list;
}
}
远程服务消费者实现
- 在服务消费端使用
HttpClient
发送请求, 可以理解为模拟浏览器发送post/get请求.HttpClient
为我们封装了拼接一个请求的细节, 使得发送一个请求变得容易.
public static void main(String[] args) throws IOException {
//发送远程的http请求的地址
String url = "http://localhost:7070/order/loadOrderList2";
//创建HttpClient对象
CloseableHttpClient client = HttpClients.createDefault();
//创建HttpPost对象, 发送post请求
HttpPost method = new HttpPost(url);
//封装发送到服务提供者的参数
NameValuePair id = new BasicNameValuePair("uid", "10001");
List<NameValuePair> params = new ArrayList<>();
params.add(id);
//封装请求体数据
method.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
//发送具体的http请求
HttpResponse response = client.execute(method);
//获得服务提供者响应的具体数据
HttpEntity entity = response.getEntity();
//获得http的响应体
InputStream is = entity.getContent();
int len = 0;
char[] buf = new char[1024];
//使用字符流读
InputStreamReader reader = new InputStreamReader(is);
StringBuffer sb = new StringBuffer();
while((len = reader.read(buf)) != -1){
sb.append(String.valueOf(buf, 0, len));
}
System.out.println(sb);
//将响应回来的json字符串解析为Order集合
List<Order> list = JSON.parseArray(sb.toString(), Order.class);
for(Order o : list){
System.out.println(o.getId() + "\t" + o.getTotal() + "\t" + o.getDate());
}
}
四. 通过spring提供的RestTemplate实现远程服务的生产与消费
- 通过一个红包系统和订单系统进行演示, 红包系统访问订单系统, 获得某个用户的订单信息, 派发红包.
- 订单系统继续沿用
HttpClient
中的订单系统, 通过访问loadOrderList2
方法能返回一个订单集合Json字符串.
远程服务消费者实现.
@Controller
public class RedController {
//注入由spring提供的RestTemplate对象
@Autowired
private RestTemplate restTemplate;
/**
* 发送远程的http请求, 消费http服务
* 获得订单对象的集合
*/
@RequestMapping("/loadOrderList3")
@ResponseBody
public List<ResponseEntity<Order[]>> loadOrderList3(String uid){
//发送远程http请求的url
String url = "http://localhost:7070/order/loadOrderList2";
//发送到远程服务的参数
MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
params.add("uid", uid);
//通过RestTemplate对象发送post请求
ResponseEntity<Order[]> entitys = restTemplate.postForEntity(url, params, Order[].class);
//查看响应的状态码
System.out.println(entitys.getStatusCodeValue());
//查看响应头
HttpHeaders headMap = entitys.getHeaders();
for(Map.Entry<String, List<String>> m : headMap.entrySet()){
System.out.println(m.getKey() + ": " + m.getValue());
}
return Arrays.asList(entitys);
}
}
Java实现远程服务生产与消费(RPC)的4种方法-RMI,WebService,HttpClient,RestTemplate的更多相关文章
- 【转】Java中字符串中子串的查找共有四种方法(indexof())
原文网址:http://wfly2004.blog.163.com/blog/static/1176427201032692927349/ Java中字符串中子串的查找共有四种方法,如下:1.int ...
- Java中字符串中子串的查找共有四种方法(indexof())
Java中字符串中子串的查找共有四种方法(indexof()) Java中字符串中子串的查找共有四种方法,如下:1.int indexOf(String str) :返回第一次出现的指定子字符串在此字 ...
- Java判断一个字符是否是数字的几种方法的代码
在工作期间,将写内容过程经常用到的一些内容段做个记录,下面内容是关于Java判断一个字符是否是数字的几种方法的内容,希望能对码农们有好处. public class Test{ public stat ...
- 如何在java中跳出当前多重嵌套循环?有几种方法?
如何在java中跳出当前多重嵌套循环?有几种方法? - 两种方法 - 1.在外层循环定义标记 ok: for(int i=0;i<100;i++){ ...
- Java:判断字符串是否为数字的五种方法
Java:判断字符串是否为数字的五种方法 //方法一:用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = str. ...
- Java去除掉HTML里面所有标签的两种方法——开源jar包和自己写正则表达式
Java去除掉HTML里面所有标签,主要就两种,要么用开源的jar处理,要么就自己写正则表达式.自己写的话,可能处理不全一些自定义的标签.企业应用基本都是能找开源就找开源,实在不行才自己写…… 1,开 ...
- 普通Java类获取spring 容器的bean的5种方法
方法一:在初始化时保存ApplicationContext对象方法二:通过Spring提供的工具类获取ApplicationContext对象方法三:继承自抽象类ApplicationObjectSu ...
- Java多线程:向线程传递参数的三种方法
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果.但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别.由于线程 ...
- Java/JSP获得客户端网卡MAC地址的三种方法解析
java/jsp获得客户端(IE)网卡MAC地址的方法大概有三种. 1.通过命令方式,在客户端执行Ipconfig 等等.(java/jsp) 2.通过ActiveX的方法.(jsp) 3.通过向13 ...
随机推荐
- 用大白话揭开Ajax长轮询(long polling)的神秘面纱
在看这篇Ajax长轮询之前可以先看看Ajax轮询技术(没有长),有助于理解: Ajax长轮询属于Ajax轮询的升级版,在客户端和服务端都进行了一些改造,使得消耗更低,速度更快. "不间断的通 ...
- jquery-easyui 中表格的行编辑功能
具体实现代码如下: <table id="tt"></table> $('#tt').datagrid({ title:'Editable DataGrid ...
- 让IE6支持min-height,max-height等的方法
1.IE6支持max-height解决方法 IE6支持最大高度解决CSS代码:.yangshi{max-height:1000px;_height:expression((document.do ...
- 关于Cocos2d-x中节点的获取
方法一: 1.在.h文件的属性里面先声明要使用的节点或者变量. private: Label *scorelabel; 2.在.cpp文件中创建并使用这个节点或者变量. scorelabel = La ...
- try catch 异常处理
1.捕获指定异常 2.捕获所有异常(catch(...))
- perl 内置操作符 $^O -判断操作系统环境
今天看bowtie2的源代码的时候,发现有这样一段用法: my $os_is_nix = $^O ne "MSWin32"; my $align_bin_s = $os_is_ni ...
- R语言绘图边框
在R语言中, 绘图边框一共有3个区域: device region : figure region : plot region : 在描述不同区域大小的时候,有对应的不同参数: din : 返回d ...
- 在GIT中创建一个空分支
ref: https://segmentfault.com/a/1190000004931751
- c++ json cpp
一 编译链接 1 在相应官网下载jsoncpp 2 解压得到jsoncpp-src-0.5.0文件 3 打开jsoncpp-src-0.5.0 -> makefiles -> vs71 - ...
- 字符串池化 python
前言 在 Python 中经常通过内存池化技术来提高其性能,那么问题来了,在什么情况下会池化呢? 让我们通过几个例子进行一下理解一下. 预备知识 在查看例子之前,首先要提 python 中的一个函数 ...