rpc技术使用
使用前景,项目是springcloud项目,想调用外部一个服务,这个服务不会注入到我的注册中心去,我也不知道他是个啥玩意,就知道是个java服务,springcloud自带的Ribbon+RestTemplate或者Feign都是rpc远程调用技术,但是分装了,我本来想使用spring的restTemplate,并配置该客户端工程,设置连接时间,但是启动会报错,影响该springcoud的内部配置,会报错,配置代码如下,如果不用cloud,使用springboot就可以
restTemplate
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* @version 0.0.1
* @program: fuse-system
* @description: 普通服务之间调用配置类
* https://www.cnblogs.com/eternityz/p/12241380.html
* @author: huangzq
* @create: 2021-01-08 09:24
*/
@Configuration
public class RestTemplateConfig extends RibbonAutoConfiguration {
/**
* RibbonAutoConfiguration.class类中有方法创建并注入了ClientHttpRequestFactory,
* 该类中方法simpleClientHttpRequestFactory也创建了,优先采用simpleClientHttpRequestFactory的,
* 这样我不知道会对这个cloud服务造成什么影响,暂时放弃该配置类,cloud内部应该可以在yml中设置连接超时时间
*
* @param factory
* @return
*/
@Bean("restTemplate")
public RestTemplate restTemplate(@Qualifier("simpleClientHttpRequestFactory") ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(15000);
factory.setReadTimeout(5000);
return factory;
}
}
工具类
package com. utils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
/**
* @version 0.0.1
* @program: fuse-system
* @description: 普通服务之间调用工具类
* @author: huangzq
* @create: 2021-01-08 09:16
*/
public class RestTemplateUtil {
/**
* 发送表单参数的post请求
*
* @param url 请求url
* @param param 参数
* @param respType 返回类型
* @return T
*/
public static <T> T postForm(String url, Map<String, List<Object>> param, Class<T> respType) {
return getRestInstance().postForEntity(url, getHttpEntity(param, false), respType).getBody();
}
/**
* 发送表单参数的异步post请求
*
* @param url 请求url
* @param callback 回调接口
* @param respType 返回类型
*/
public static <T> void asyncPostForm(String url, Map<String, List<Object>> param,
Class<T> respType, ListenableFutureCallback<ResponseEntity<T>> callback) {
getAsyncRestInstance().postForEntity(url, getHttpEntity(param, false), respType).addCallback(callback);
}
/**
* 发送表单有参数get请求
*
* @param url 请求url
* @param param 参数对象
* @param respType 返回类型
* @return T
*/
public static <T> T getForm(String url, Class<T> respType, Map<String,String> param) {
return getRestInstance().getForEntity(url, respType, param).getBody();
}
/**
* @Description: 发送表单无参数的get请求
* @Param: [url, param, respType]
* @return: T
* @Author: tonyzhang
* @Date: 2019-01-18 17:23
*/
public static <T> T getForm(String url, Class<T> respType) {
return getRestInstance().getForObject(url, respType);
}
/**
* 获取HttpEntity实例对象
*
* @param param 参数对象
* @param isJson true 发送json请求,false发送表单请求
* @return HttpEntity
*/
private static <P> HttpEntity<P> getHttpEntity(P param, boolean isJson) {
HttpHeaders headers = new HttpHeaders();
if (isJson) {
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
} else {
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
}
return new HttpEntity<>(param, headers);
}
/*-----------------生产单例对象,方便自定义如何构造对象------------------*/
private static RestTemplate restInit() {
//设置连接超时和读取超时时间
SimpleClientHttpRequestFactory factory=new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setReadTimeout(5000);
RestTemplate restTemplate = new RestTemplate(factory);
FormHttpMessageConverter fastConverter = new FormHttpMessageConverter();
WxMappingJackson2HttpMessageConverter wmc=new WxMappingJackson2HttpMessageConverter();
restTemplate.getMessageConverters().add(fastConverter);
restTemplate.getMessageConverters().add(wmc);
return restTemplate;
}
private static AsyncRestTemplate asyncRestInit() {
return new AsyncRestTemplate();
}
private static RestTemplate getRestInstance() {
return RestSingle.INSTANCE;
}
private static AsyncRestTemplate getAsyncRestInstance() {
return AsyncRestSingle.INSTANCE;
}
private static class RestSingle {
private static final RestTemplate INSTANCE = restInit();
}
private static class AsyncRestSingle {
private static final AsyncRestTemplate INSTANCE = asyncRestInit();
}
}
添加其他类型支持
package com. .utils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.util.ArrayList;
import java.util.List;
/**
* @version 0.0.1
* @program: fuse-system
* @description: 封装转换器, 添加更多类型的支持
* @author: huangzq
* @create: 2021-01-08 09:18
*/
public class WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public WxMappingJackson2HttpMessageConverter(){
List<MediaType> mediaTypes=new ArrayList<>();
//添加text/html类型的支持
mediaTypes.add(MediaType.TEXT_HTML);
//添加text/plain类型的支持.微信接口会用到
mediaTypes.add(MediaType.TEXT_PLAIN);
setSupportedMediaTypes(mediaTypes);
}
}
想使用的话,就直接注入到你的需要的业务层即可
参考:https://www.cnblogs.com/eternityz/p/12241380.html
HttpClients
使用HttpClient,一般都需要设置连接超时时间和获取数据超时时间。这两个参数很重要,目的是为了防止访问其他http时,由于超时导致自己的应用受影响。
4.5版本中,这两个参数的设置都抽象到了RequestConfig中,由相应的Builder构建,具体的例子在下面post带文件上传的请求中体现出来了
导入必要的jar包
<!-- httpclient核心包 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.4</version>
</dependency>
get
/**
* 给第三方服务创建数据集
*
* @param createApiDataSetVo
* @return 0成功 1失败
*/
@Override
public Integer createApiDataSet(CreateApiDataSetVo createApiDataSetVo) {
//1.打开浏览器
CloseableHttpClient httpClient = HttpClients.createDefault();
//调用地址
String url = "http://localhost:8080/user/1";
//2.声明get请求
HttpGet request = new HttpGet(url);
//发出请求 这种方式是可以的,下面的方式也可以,但是不知道我调用的服务是怎么返回数据的,要约定 下
// String result = null;
// try {
// //3.发送请求
// result = client.execute(request,new BasicResponseHandler());
// } catch (IOException e) {
// e.printStackTrace();
// }
//3.发送请求
CloseableHttpResponse response = null;
try {
response = httpClient.execute(request);
//4.判断状态码
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
//使用工具类EntityUtils,从响应中取出实体表示的内容并转换成字符串
String string = EntityUtils.toString(entity, "utf-8");
log.info("数据创建成功,返回内容为:{}", string);
return 0;
} else {
log.info("第三方创建数据集失败,第三方返回的状态码为:{}", response.getStatusLine().getStatusCode());
}
} catch (IOException e) {
log.error("第三方创建数据集失败:", e);
} finally {
//5.关闭资源
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return 1;
}
post 普通(form普通表单key-value)
form表单调用另外一个接口的,不是json串形式调用,所以被调用的后台是否添加@RequestBody
@Override
public ResponseEntity fileByLocalToApi() {
//1.打开浏览器
CloseableHttpClient httpClient = HttpClients.createDefault();
//2.声明post请求
HttpPost httpPost = new HttpPost("https://www.oschina.net/");
//3.开源中国为了安全,防止恶意攻击,在post请求中都限制了浏览器才能访问
httpPost.addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36");
//4.判断状态码
List<NameValuePair> parameters = new ArrayList<NameValuePair>(0);
parameters.add(new BasicNameValuePair("scope", "project"));
parameters.add(new BasicNameValuePair("q", "java"));
CloseableHttpResponse response = null;
try {
// 创建form表单对象
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters,"UTF-8");
//这个是判断form表单是转成json形式的发送给被调用服务
//formEntity.setContentType("Content-Type:application/json");
httpPost.setEntity(formEntity);
//5.发送请求
response = httpClient.execute(httpPost);
if(response.getStatusLine().getStatusCode()==200){
HttpEntity entity = response.getEntity();
String string = EntityUtils.toString(entity, "utf-8");
log.info("文件上传功能,返回内容为:{}",string);
} else {
log.info("文件推送失败,第三方返回状态码为:{},信息为:{}",response.getStatusLine().getStatusCode(),EntityUtils.toString(response.getEntity(), "utf-8"));
return null;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//6.关闭资源
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
post 普通(json串形式)
/**
* 向第三方发送模型信息
*
* @param modelId
* @return
*/
@Override
public ResponseEntity<String> modelApiSend(String modelId) {
String url = "http://localhost:8080/modelInfo";
//1.打开浏览器
CloseableHttpClient httpClient = HttpClients.createDefault();
//2.声明post请求
HttpPost httpPost = new HttpPost(url);
//4.判断状态码 这个是我的一个实体类
ApiModelSendMsg apiModelSendMsg = new ApiModelSendMsg();
apiModelSendMsg.setBusType("dsadsa");
apiModelSendMsg.setConfigFilePath("dsdasdsadasdsadasd");
CloseableHttpResponse response = null;
try {
String CONTENT_TYPE_TEXT_JSON = "text/json";
httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
httpPost.setHeader("Accept", "application/json");
Gson gson = new Gson();
String parameter = gson.toJson(apiModelSendMsg);
//解决在调用的服务端中文乱码问题
StringEntity se = new StringEntity(parameter, Charsets.UTF_8);
se.setContentType(CONTENT_TYPE_TEXT_JSON);
httpPost.setEntity(se);
//5.发送请求
response = httpClient.execute(httpPost);
if(response.getStatusLine().getStatusCode()==200){
HttpEntity entity = response.getEntity();
String string = EntityUtils.toString(entity, "utf-8");
log.info("模型信息发送成功:{}",string);
} else {
log.info("模型信息发送失败,第三方返回状态码为:{},信息为:{}",response.getStatusLine().getStatusCode(),EntityUtils.toString(response.getEntity(), "utf-8"));
return ResponseEntity.ok("error");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//6.关闭资源
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return ResponseEntity.ok("ok");
}
post文件(支持多文件与其他参数传递)
/**
* 文件从本地下发给第三方服务
*
* @return
*/
@Override
public ResponseEntity fileByLocalToApi() {
//文件URL,此处取豆瓣上的一个图片
String fileUrl = "https://huangzouqiang-huangzouqiang.oss-cn-beijing.aliyuncs.com/3/2020-12-31/thum/01_20201231_101426_94O4W1d1.jpg";
String fileUrl2 = "https://huangzouqiang-huangzouqiang.oss-cn-beijing.aliyuncs.com/3/2020-12-31/pic/01_20201231_101426_94O4W1d1.jpg";
try {
//提取到文件名
String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
//转换成文件流
InputStream is = new URL(fileUrl).openStream();
InputStream is2 = new URL(fileUrl2).openStream();
//接收文件的服务器地址
String uploadURL = "http://localhost:8080/fileupload";
//创建HttpClient
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(uploadURL);
//构建超时等配置信息
RequestConfig config = RequestConfig.custom()
//连接超时时间
.setConnectTimeout(1000)
//从连接池中取的连接的最长时间
.setConnectionRequestTimeout(1000)
//数据传输的超时时间
.setSocketTimeout(6 * 1000)
.build();
httpPost.setConfig(config);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
/*绑定文件参数,传入文件流和contenttype,此处也可以继续添加其他formdata参数*/
builder.addBinaryBody("file", is, ContentType.MULTIPART_FORM_DATA, "1111.jpg");
builder.addBinaryBody("file", is2, ContentType.MULTIPART_FORM_DATA, "22222.jpg");
builder.addTextBody("prefix", "zhangsan");
//builder.add
HttpEntity entity = builder.build();
httpPost.setEntity(entity);
//执行提交
HttpResponse response = httpClient.execute(httpPost);
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
//将响应的内容转换成字符串
String result = EntityUtils.toString(responseEntity, Charset.forName("UTF-8"));
//此处根据服务器返回的参数转换,这里返回的是JSON格式
// JSONObject output = JSON.parseObject(result);
// JSONArray body = output.getJSONArray("body");
// String resUrl = body.get(0) + "";
// System.out.println(resUrl);
System.out.println(result);
}
if (is != null) {
is.close();
}
if (is2 != null) {
is2.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
post文件接收服务端(另外一个服务项目)
/**
* 上传文件
*
*/
@PostMapping("fileupload")
public String upload(@RequestParam(defaultValue = "", required = false) String prefix,
@RequestParam("file") MultipartFile... files) {
for (MultipartFile file : files) {
if (file.isEmpty()) {
continue;
}
String fileName = file.getOriginalFilename();
File dest = new File(new File("C:\\Users\\shinelon\\Pictures\\jpeg\\").getAbsolutePath()+ "/" + fileName);
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
try {
// 保存文件
file.transferTo(dest);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
try {
//为了测试httpClient连接超时的问题
//在客户端那个设置调用超时请求中 数据传输的超时时间
//setSocketTimeout(2 * 1000) 这样会出现超时
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "success";
}
说明:
setConnectTimeout:设置连接超时时间,单位毫秒。
连接超时的意思是:
例如:httpclient连接超时我设置为60秒,如果我上传一个文件要几十分钟,那会导致连接超时么?
这个不会超时的,超时从最后一次操作算起,传文件算操作中,那就不会超时。传完了60秒啥都不做就超时了,只会出现在获取数据超时那个属性,也就是setSocketTimeout中。
setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
setSocketTimeout:请求获取数据的超时时间,单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。
参考:http://www.yiidian.com/springcloud/httpclient-development.html
https://www.cnblogs.com/kitor/p/11235762.html
https://www.cnblogs.com/tobeymarshall/p/10215101.html
https://www.cnblogs.com/linus-tan/p/10592767.html
HttpURLConnection的get和post
package com. .utils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
/**
* @version 0.0.1
* @program: fuse-system
* @description: 网络流形式服务调用工具类
* @author: huangzq
* @create: 2021-01-08 14:05
*/
@Slf4j
public class HttpUtil {
public static String executeGetMethod(String path, String authorization) {
String response = "";
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Authorization", "Basic " + authorization);
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
// 获得返回值
InputStream in = connection.getInputStream();
response = getResponse(in);
log.info("response:{}", response);
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
public static String executePostMethod(String path, JSONObject param) throws SocketTimeoutException {
String paramStr = param.toString();
String response = "";
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.setRequestProperty("Charset", "UTF-8");
connection.setRequestProperty("Content-Length", String.valueOf(paramStr.length()));
connection.setRequestProperty("Content-Type", "application/json");
connection.connect();
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
// 写入请求的字符串
out.writeBytes(paramStr);
out.flush();
out.close();
// 获得返回值
InputStream in = connection.getInputStream();
response = getResponse(in);
connection.disconnect();
} catch (SocketTimeoutException e) {
throw e;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
private static String getResponse(InputStream in) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder builder = new StringBuilder();
String line;
try {
while ((line = reader.readLine()) != null) {
builder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return builder.toString();
}
}
参考:https://blog.youkuaiyun.com/egg1996911/article/details/73822803