原创

Java远程调用接口

1.远程调用方式

常见的远程调用方式有以下几种:

  • RPC:Remote Produce Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型

  • Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。缺点是消息封装臃肿。

    现在热门的Rest风格,就可以通过http协议来实现。

2.1.认识RPC

RPC,即 Remote Procedure Call(远程过程调用),是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。说得通俗一点就是:A计算机提供一个服务,B计算机可以像调用本地服务那样调用A计算机的服务。

通过上面的概念,我们可以知道,实现RPC主要是做到两点:

  • 实现远程调用其他计算机的服务
    • 要实现远程调用,肯定是通过网络传输数据。A程序提供服务,B程序通过网络将请求参数传递给A,A本地执行后得到结果,再将结果返回给B程序。这里需要关注的有两点:
      • 1)采用何种网络通讯协议?
        • 现在比较流行的RPC框架,都会采用TCP作为底层传输协议
      • 2)数据传输的格式怎样?
        • 两个程序进行通讯,必须约定好数据传输格式。就好比两个人聊天,要用同一种语言,否则无法沟通。所以,我们必须定义好请求和响应的格式。另外,数据在网路中传输需要进行序列化,所以还需要约定统一的序列化的方式。
  • 像调用本地服务一样调用远程服务
    • 如果仅仅是远程调用,还不算是RPC,因为RPC强调的是过程调用,调用的过程对用户而言是应该是透明的,用户不应该关心调用的细节,可以像调用本地服务一样调用远程服务。所以RPC一定要对调用的过程进行封装

RPC调用流程图:

想要了解详细的RPC实现,给大家推荐一篇文章:自己动手实现RPC

2.2.认识Http

Http协议:超文本传输协议,是一种应用层协议。规定了网络传输的请求格式、响应格式、资源定位和操作的方式等。但是底层采用什么网络传输协议,并没有规定,不过现在都是采用TCP协议作为底层传输协议。说到这里,大家可能觉得,Http与RPC的远程调用非常像,都是按照某种规定好的数据格式进行网络通信,有请求,有响应。没错,在这点来看,两者非常相似,但是还是有一些细微差别。

  • RPC并没有规定数据传输格式,这个格式可以任意指定,不同的RPC协议,数据格式不一定相同。
  • Http中还定义了资源定位的路径,RPC中并不需要
  • 最重要的一点:RPC需要满足像调用本地服务一样调用远程服务,也就是对调用过程在API层面进行封装。Http协议没有这样的要求,因此请求、响应等细节需要我们自己去实现。
    • 优点:RPC方式更加透明,对用户更方便。Http方式更灵活,没有规定API和语言,跨语言、跨平台
    • 缺点:RPC方式需要在API层面进行封装,限制了开发的语言环境。

例如我们通过浏览器访问网站,就是通过Http协议。只不过浏览器把请求封装,发起请求以及接收响应,解析响应的事情都帮我们做了。如果是不通过浏览器,那么这些事情都需要自己去完成。

2.3.如何选择?

既然两种方式都可以实现远程调用,我们该如何选择呢?

  • 速度来看,RPC要比http更快,虽然底层都是TCP,但是http协议的信息往往比较臃肿,不过可以采用gzip压缩。
  • 难度来看,RPC实现较为复杂,http相对比较简单
  • 灵活性来看,http更胜一筹,因为它不关心实现细节,跨平台、跨语言。

因此,两者都有不同的使用场景:

  • 如果对效率要求更高,并且开发过程使用统一的技术栈,那么用RPC还是不错的。
  • 如果需要更加灵活,跨语言、跨平台,显然http更合适

那么我们该怎么选择呢?

微服务,更加强调的是独立、自治、灵活。而RPC方式的限制较多,因此微服务框架中,一般都会采用基于Http的Rest风格服务。




2.Http客户端工具

既然微服务选择了Http,那么我们就需要考虑自己来实现对请求和响应的处理。不过开源世界已经有很多的http客户端工具,能够帮助我们做这些事情,例如:

  • HttpClient
  • OKHttp
  • URLConnection

2.1.HttpClient

2.1.1.介绍

HttpClient是Apache公司的产品,是Http Components下的一个组件。

官网地址:http://hc.apache.org/index.html

特点:

  • 基于标准、纯净的Java语言。实现了Http1.0和Http1.1
  • 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)
  • 支持HTTPS协议。
  • 通过Http代理建立透明的连接。
  • 自动处理Set-Cookie中的Cookie。

HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性;简化了很多操作。

简单而言HttpClient就是加强版的HttpURLConnection,但是HttpClient能够维护客户端和服务端的Session。

2.1.2.使用

发起get请求:

    @Test
    public void testGet() throws IOException {
        
        CloseableHttpClient httpClient;
        /*初始化,相当于打开浏览器*/
        httpClient= HttpClients.createDefault();
        /*相当于在浏览器输入地址*/
        HttpGet request = new HttpGet("http://www.baidu.com");
        /*相当于在浏览器按下回车*/
        String response = httpClient.execute(request, new BasicResponseHandler());
        System.out.println(response);
    }

发起Post请求:

package com.cpp.Http;
import com.alibaba.fastjson.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
public class HttpClientTest {
    public static void main(String[] args) throws IOException {
        HttpClientTest httpClientTest = new HttpClientTest();
        httpClientTest.testGet();
    }
    public  void testGet() throws IOException, JSONException {
        /*初始化,相当于打开浏览器*/
        CloseableHttpClient httpClient = HttpClients.createDefault();
        /*相当于在浏览器输入地址*/
        HttpGet request = new HttpGet("http://127.0.0.1/seeyon/rest/token/rest/rest");
        /*相当于在浏览器按下回车*/
        String response = httpClient.execute(request, new BasicResponseHandler());
//        JSONArray jsonArray =  new JSONArray(response);
        JSONObject createJsonObject = JSON.parseObject(response);
        String id = createJsonObject.getString("id");
        String url = "http://127.0.0.1/seeyon/rest/thirdpartyPending/receive?token=" + id;

        HttpPost httpPost = new HttpPost(url);
        String str = "{
                "\t\t\t\"title\": \"test",\n" +
                "\t\t\t\"creationDate\": \"2020-05-20 12:40:49\",\n" +
                "\t\t\t\"thirdReceiverId\": \"test\",\n" +
                "\t\t\t\"senderName\": \"test\",\n" +
                "\t\t\t\"thirdSenderId\": \"\",\n" +
                "\t\t\t\"noneBindingSender\": \"test\",\n" +
                "\t\t\t\"noneBindingReceiver\": \"test\",\n" +
                "\t\t\t\"state\": \"0\",\n" +
                "\t\t\t\"registerCode\": \"3001\",\n" +
                "\t\t\t\"taskId\": \"1\"\n" +
                "\t\t}";
        String re = doPostJson(url, str);
        System.out.println(re);



    }
    public static String doPostJson(String url, String json) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            HttpPost httpPost = new HttpPost(url);
            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);
            response = httpClient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }
}

如果想要得到对象,我们还需要手动进行Json反序列化,这一点比较麻烦。

2.1.3.Json转换工具

HttpClient请求数据后是json字符串,需要我们自己把Json字符串反序列化为对象,我们会使用JacksonJson工具来实现。

JacksonJson是SpringMVC内置的json处理工具,其中有一个ObjectMapper类,可以方便的实现对json的处理:

对象转json

// json处理工具
    private ObjectMapper mapper = new ObjectMapper();
    @Test
    public void testJson() throws IOException {
        Type type = new Type();
        type.setId(1l);
        List<Blog> blogs = new ArrayList<>();
        Blog blog = new Blog();
        blogs.add(blog);
        type.setBlogs(blogs);
        // 序列化
        System.out.println("type = " + type);
        String json = mapper.writeValueAsString(type);
        System.out.println("json = " + json);
    }

结果:

json转普通对象

    // json处理工具
    private ObjectMapper mapper = new ObjectMapper();
    @Test
    public void testJson() throws IOException {
        Type type = new Type();
        type.setId(1l);
        List<Blog> blogs = new ArrayList<>();
        Blog blog = new Blog();
        blogs.add(blog);
        type.setBlogs(blogs);
        // 序列化
        System.out.println("type = " + type);
        String json = mapper.writeValueAsString(type);
        System.out.println("json = " + json);
        // 反序列化,接收两个参数:json数据,反序列化的目标类字节码
        Type result = mapper.readValue(json, Type.class);
        System.out.println("result = " + result);
    }

结果:

json转集合

json转集合比较麻烦,因为你无法同时把集合的class和元素的class同时传递到一个参数。

因此Jackson做了一个类型工厂,用来解决这个问题:

    // json处理工具
    private ObjectMapper mapper = new ObjectMapper();
    @Test
    public void testJson() throws IOException {
        Type type = new Type();
        type.setId(1l);
        List<Blog> blogs = new ArrayList<>();
        Blog blog = new Blog();
        blogs.add(blog);
        type.setBlogs(blogs);

        // 序列化,得到对象集合的json字符串
        String json = mapper.writeValueAsString(Arrays.asList(type, type));

        System.out.println("json=="+json);
        // 反序列化,接收两个参数:json数据,反序列化的目标类字节码
        List<Type> types = mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, Type.class));
        for (Type u : types) {
            System.out.println("u = " + u);
        }
    }

结果:

解析数据demo

1.使用JsonUtil

public static void main(String[] args) throws BusinessException {
		String accessToken = "rRob1pQsag5U00";
		String baseUrl = "https://app.ekuaibao.com/";
//		String url = baseUrl + "api/openapi/v1/provisional/getProvisionalAuth?accessToken=" + accessToken;
		String url = baseUrl + "api/openapi/v1/staffs/getStaffIds?accessToken=" + accessToken;
		Map paramMap = new HashMap();
		paramMap.put("type", "CODE");
		String[] s = new String[1];
		s[0] = "9014025295066775752";
		paramMap.put("conditionIds", s);
		String jsonString=JsonUtil.mapToJson(paramMap);
//		String jsonString = JSON.toJSONString(paramMap);
		System.out.println(jsonString);
		String Result = HttpClientUtil.doPost(url, jsonString, "utf-8");
		System.out.println(Result);
		Map map=JsonUtil.jsonToMap(Result);
		String items= (String) map.get("items");
		items=items.substring(1, items.length()-1);
		System.out.println(items);
		Map item=JsonUtil.jsonToMap(items);
		String uid =  (String) item.get("id");
		String userId = (String) item.get("userId");
		String getUrl = "https://app.ekuaibao.com/api/openapi/v1/provisional/getProvisionalAuth?accessToken="
				+ accessToken;
		Map getUrlMap = new HashMap();
		getUrlMap.put("uid", uid);
		getUrlMap.put("userId", userId);
		getUrlMap.put("pageType", "home");
		getUrlMap.put("isApplet", true);
		getUrlMap.put("expireDate", 120);
		getUrlMap.put("overdueTokenRedirect", "[https://www.ekuaibao.com](https://www.ekuaibao.com)");
		String getUrlJsonString = JsonUtil.mapToJson(getUrlMap);
		String getUrlResult = HttpClientUtil.doPost(getUrl, getUrlJsonString, "utf-8");
		Map getUrlResultMap = JsonUtil.jsonToMap(getUrlResult);
		String ResultValue = (String) getUrlResultMap.get("value");
		Map urlResult = JsonUtil.jsonToMap(ResultValue);
		System.out.println("单点url==" + urlResult.get("message"));
	}
	
	

2.使用fastjson

需要的依赖:fastjson

     <!--阿里 JSON,用于解析信息-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.57</version>
        </dependency>
public static void main(String[] args) throws BusinessException {
		String accessToken = "rRob1pQsag5U00";
		String baseUrl = "https://app.ekuaibao.com/";
		String url = baseUrl + "api/openapi/v1/staffs/getStaffIds?accessToken=" + accessToken;
		Map paramMap = new HashMap();
		paramMap.put("type", "CODE");
		String[] s = new String[1];
		s[0] = "9014025295066775752";
		paramMap.put("conditionIds", s);
		String jsonString = JSON.toJSONString(paramMap);
		System.out.println(jsonString);
		String Result = HttpClientUtil.doPost(url, jsonString, "utf-8");

		System.out.println(Result);
		JSONObject jsonObject = JSONObject.parseObject(Result);
		JSONArray jsonArray = jsonObject.getJSONArray("items");
		Iterator<Object> jsonIterator = jsonArray.iterator();
		JSONObject parseObject = (JSONObject) jsonIterator.next();
		String uid = parseObject.getString("id");
		String userId = parseObject.getString("userId");
		String getUrl = "https://app.ekuaibao.com/api/openapi/v1/provisional/getProvisionalAuth?accessToken="
				+ accessToken;
		Map getUrlMap = new HashMap();
		getUrlMap.put("uid", uid);
		getUrlMap.put("userId", userId);
		getUrlMap.put("pageType", "home");
		getUrlMap.put("isApplet", true);
		getUrlMap.put("expireDate", 120);
		getUrlMap.put("overdueTokenRedirect", "[https://www.ekuaibao.com](https://www.ekuaibao.com)");
		String getUrlJsonString = JSON.toJSONString(getUrlMap);
		String getUrlResult = HttpClientUtil.doPost(getUrl, getUrlJsonString, "utf-8");
		Map getUrlResultMap = (Map) JSON.parse(getUrlResult);
		Map ResultMapValue = (Map) getUrlResultMap.get("value");
		String urlResult = ResultMapValue.get("message").toString();
		System.out.println("单点url==" + urlResult);

	}

2.2. OkHttpClient

OkHttpClient官网 OkHttp GitHub地址

导入依赖:

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.6.0</version>
</dependency>
<!--JSON解析工具-->
<dependency>
    <groupId>com.fasterxml.jackson</groupId>
    <artifactId>jackson-parent</artifactId>
    <version>2.8</version>
</dependency>

使用:

package com.cpp.Http;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
/**
 * @author : cpp
 * @create : 2020-05-23 - 13:23
 * @describe:测试testOkHttpClient
 */
public class testOkHttpClient {

    public static void main(String[] args) {
        try {
            /*向指定手机号发送短信*/
            String result=run("http://neteasymusic.xiongsihao.com/captcha/sent?phone=137xxxxxxxx");
            System.out.println(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//    Request是OkHttp中访问的请求,Builder是辅助类。Response即OkHttp中的响应。
    private static String run(String url) throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().string();
        } else {
            throw new IOException("Unexpected code " + response);
        }
    }
}

Request是OkHttp中访问的请求,Builder是辅助类。Response即OkHttp中的响应。

2.3.Spring的RestTemplate

是Spring用于同步client端的核心类,简化了与http服务的通信,并满足RestFul原则,程序代码可以给它提供URL,并提取结果。默认情况下,RestTemplate默认依赖jdk的HTTP连接工具。

当然也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp。

RestTemplate包含以下几个部分:

  • HttpMessageConverter 对象转换器
  • ClientHttpRequestFactory 默认是JDK的HttpURLConnection
  • ResponseErrorHandler 异常处理
  • ClientHttpRequestInterceptor 请求拦截器

导入依赖:

<!--RestTemplate依赖在spring-web这个包下-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.0.4.RELEASE</version>
</dependency>

使用:

package com.cpp.Http;

import org.springframework.web.client.RestTemplate;

/**
 * @author : cpp
 * @create : 2020-05-23 - 13:41
 * @describe:
 */
public class testRestTemplate {

    public static void main(String[] args) {
        RestTemplate restTemplate=new RestTemplate();
        //获取网易云热评http://neteasymusic.xiongsihao.com/song/url?id=1398663411
        String result = restTemplate.getForObject("http://neteasymusic.xiongsihao.com/comment/hotwall/list",String.class);//调用的ul与返回类型
        String result1 = restTemplate.getForObject("http://neteasymusic.xiongsihao.com/song/url?id=1398663411",String.class);//调用的ul与返回类型
        System.out.println(result1);
    }
}
  • 作者:管理员(联系作者)
  • 发表时间:2020-06-02 11:59
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 公众号转载:请在文末添加作者公众号二维码
  • 评论

    王哞哞 游客
    测试
    水龙訡 游客
    测试
    总有bug想害朕 QQ
    测试
    游客
     @ 高 游客
    测试。。。