基於HttpClient實現RPC遠端服務呼叫功能【SpringBoot專案】
一、什麼是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