一、什么是RPC?
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
二、为什么要使用RPC?
在大型项目开发的过程中,为了减轻服务器的负载就要进行分模块来布置项目,从开发到运行都不可能仅仅只是在一个服务器节点上的,它们会按照各个模块,比如前端,后端,数据库等等模块来搭建分布式系统。这时我们要考虑一个问题,当各个模块之间进行交互的时候该怎么办?这里我们举一个简单的例子,我们把一个醒目分为两个模块,用户信息模块,订单模块,当订单项目需要查询用户信息的时候怎么查询?这时我们可以用户信息模块设计一个用户信息的接口然后在订单项目中使用RPC进行远程服务调用该接口,这样就轻松实现了两个项目之间的访问。不过这种方式在有很多服务嵌套调用的时候效率会降低,所以尽量不要使用。
下面,我们来实现上面的这个例子,通过demo我们会很轻松的理解RPC远程调用。
三、实战
1.创建两个maven项目,添加springboot项目依赖。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
根据我们上面的分析,要在订单系统中访问用户系统中的信息,我们应该在订单系统中实现RPC来调用用户系统中的接口,所以我们要在订单系统中添加HttpClient依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
2.我们搭建好项目后就开始编写代码,这里我们在DAO层写入固定的值,只是为了测试就不用访问数据库了。下面是用户系统中的代码:
UserEntity.java
public class UserEntity {
private Integer id;
private String username;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public UserEntity(Integer id, String username) {
super();
this.id = id;
this.username = username;
}
}
UserDao.java:
@Repository
public class UserDao {
public List<UserEntity> getAllUser() {
List<UserEntity> list=new ArrayList<UserEntity>();
for(int i=0;i<20;i++) {
list.add(new UserEntity(i, "name"+i));
}
return list;
}
}
UserService.java:
@Service
public class UserService {
@Autowired
private UserDao userDao;
public List<UserEntity> getAllUser(){
return userDao.getAllUser();
}
public UserEntity getUser(Integer id) {
List<UserEntity> list=getAllUser();
for(int i=0;i<list.size();i++) {
UserEntity user=list.get(i);
if(id==user.getId()) {
return user;
}
}
return null;
}
}
UserController.java:
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/getUser")
@ResponseBody
public UserEntity getUser(Integer id) {
return userService.getUser(id);
}
}
接着我们来写一个APP类来快速启动项目:
@SpringBootApplication
@EnableAutoConfiguration
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
为了两个项目便于区分,我们使用yml文件来指定其项目路径以及端口号:
server:
context-path: /memeber
3.接下来我们编写订单系统中代码:
在这里我们只需要编写controller层即可:
OrderController .java:
@EnableAutoConfiguration
@RestController
public class OrderController {
public static void main(String[] args) {
SpringApplication.run(OrderController.class, args);
}
@RequestMapping("/OrderUser")
public String getOrderUserId(Integer id) throws IOException {
String result=get("http://localhost:8080/memeber/getUser?id="+id);
return result;
}
public String get(String Url) throws IOException {
CloseableHttpClient httpClient=HttpClients.createDefault();
//创建httpGet
HttpGet httpGet=new HttpGet(Url);
System.out.println("URL is "+httpGet.getURI());
CloseableHttpResponse response = null;
try {
//执行http请求
response=httpClient.execute(httpGet);
//获取http响应体
HttpEntity entity=response.getEntity();
System.out.println("-----------------");
//打印响应状态
System.out.println(response.getStatusLine());
if(entity!=null) {
System.out.println("Response Content Length:"+entity.getContentLength());
String content=EntityUtils.toString(entity);
System.out.println("Response Content:"+content);
return content;
}
System.out.println("------------------");
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
response.close();
httpClient.close();
}
return null;
}
}
同样在yml文件中指定端口号及路径:
server:
context-path: /order
port: 8081
至此我们已经完成了两个简单的项目,启动这两个项目,在浏览器中输入:http://localhost:8081/order/OrderUser?id=2 即可看到下面的数据表示我们RPC调用成功: