Java實現遠程服務生產與消費(RPC)的4種方法-RMI,WebService,HttpClient,RestTemplate

本文將經過具體的遠程服務發佈與消費案例展現4種RPC遠程調用方法.html

一. 經過rmi實現遠程服務的生產與消費

  • Java自身提供了java.rmi包, 方便開發者進行遠程服務的部署與消費, 下面將經過具體案例進行講解.

遠程服務提供者實現.

建立rmi-provider項目(Maven)

  1. 建立UserService接口.
//將要發佈的服務的接口
public interface UserService extends Remote {
    public String helloRmi(String name) throws RemoteException;
}
複製代碼
  1. 建立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;
    }
}
複製代碼
  1. 發佈遠程服務
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項目

  1. rmi-provider項目種的UserService接口與UserServiceImpl實現類複製到本rmi-consumer項目中.(這一步能夠進行優化解耦, 咱們能夠多建立一個rmi-resource項目, 讓rmi-providerrmi-consumer共同依賴rmi-resource項目, 而後把資源文件好比遠程服務所用到的UserService等放入rmi-resource項目中)
  2. 遠程服務消費者對遠程服務發起調用.
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進行通訊.

遠程服務提供者實現.

  1. 首先建立遠程服務接口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 + "~";
    }
}
複製代碼
  1. 發佈遠程服務, 過程和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文檔進行解析, 並得到調用遠程方法的工具類. 具體操做見下一段.

遠程服務消費者實現.

  1. 首先根據文檔得到調用遠程服務的工具類, JDK已經爲咱們封裝好了獲取的工具, 它在bin目錄下, 名字是wsimport
  2. 打開命令行, 在命令行中輸入解析命令
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. -pwdsl文檔的地址 複製代碼

  5. 能夠看到命令執行完後, 指定的包中出現一堆相關的類, 最直接調用到的類是UserServiceImplService. 下面演示對遠程方法進行調用.java

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字符串. 微信小程序先後端通訊也是這個原理.
  1. 建立名爲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> 複製代碼複製代碼<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. 建立訂單類git

public class Order {
    private String id;
    private Double total;
    private String date;

    //get / set ...
}
複製代碼
  1. 對外提供服務, 發佈時打包發佈到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;
    }
}
複製代碼

遠程服務消費者實現

  1. 在服務消費端使用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);
    }
}
複製代碼
相關文章
相關標籤/搜索