HttpClient-RestTemplate-Feign
如何通过Java发送HTTP请求,通俗点讲,如何通过Java(模拟浏览器)发送HTTP请求。
Java有原生的API可用于发送HTTP请求,即java.net.URL、java.net.URLConnection,这些API很好用、很常用,但不够简便;
所以,也流行有许多Java HTTP请求的framework,如,Apache的HttpClient。
httpclient之外RPC 以及队列的使用看可以说也是越来越广泛了。
在netty等NIO框架因为需要高效的传输所以往往选择RPC,队列则用于回调以及设备消息之间的传递。
Http这个经久不衰的大佬自然不用多说,简单,支持广泛,高度兼容性。
HttpClient
HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,使得客户端发送Http请求变得容易。
HttpClient使用:
使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。
1. 创建HttpClient对象。
2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
6. 释放连接。无论执行方法是否成功,都必须释放连接
RestTemplate
RestTemplate本是spring-web项目中的一个REST客户端,它遵循REST的设计原则,提供简单的API让我们可以调用HTTP服务。底层是对httpclient进行了封装。
ps:建议使用exchange ,其他方法都是对execute进行了封装,都拥有各自的局限性。
RestTemplate可以用来做负载均衡,RestTemplate本身不具有负载均衡的功能,该类也与Spring Cloud没有关系,但为何加入@LoadBalanced注解后,一个RestTemplate实例就具有负载均衡的功能呢?
实际上这要得益于RestTemplate的拦截器功能。
下面举例:基于自己配置的HttpClient,创建一个可负载均衡的RestTemplate
HttpClientProperties
HttpClient的配置信息
@ConfigurationProperties(prefix="spring.httpclient")
public class HttpClientProperties { private Integer connectTimeOut = ; private Integer socketTimeOut = ; private String agent = "agent";
private Integer maxConnPerRoute = ;
private Integer maxConnTotaol = ;
public Integer getConnectTimeOut() {
return connectTimeOut;
}
public void setConnectTimeOut(Integer connectTimeOut) {
this.connectTimeOut = connectTimeOut;
}
public Integer getSocketTimeOut() {
return socketTimeOut;
}
public void setSocketTimeOut(Integer socketTimeOut) {
this.socketTimeOut = socketTimeOut;
}
public String getAgent() {
return agent;
}
public void setAgent(String agent) {
this.agent = agent;
}
public Integer getMaxConnPerRoute() {
return maxConnPerRoute;
}
public void setMaxConnPerRoute(Integer maxConnPerRoute) {
this.maxConnPerRoute = maxConnPerRoute;
}
public Integer getMaxConnTotaol() {
return maxConnTotaol;
}
public void setMaxConnTotaol(Integer maxConnTotaol) {
this.maxConnTotaol = maxConnTotaol;
} }
HttpClientAutoConfiguration
根据HttpClient.class是否存在,bean是否存在,自动进行配置
@Configuration
@ConditionalOnClass({HttpClient.class})
@EnableConfigurationProperties(HttpClientProperties.class)
public class HttpClientAutoConfiguration { private final HttpClientProperties properties; public HttpClientAutoConfiguration(HttpClientProperties properties){
this.properties = properties;
} /**
* httpclient bean 的定义
* @return
*/
@Bean
@ConditionalOnMissingBean(HttpClient.class)
public HttpClient httpClient() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(properties.getConnectTimeOut())
.setSocketTimeout(properties.getSocketTimeOut()).build();// 构建requestConfig
HttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
.setUserAgent(properties.getAgent())
.setMaxConnPerRoute(properties.getMaxConnPerRoute())
.setMaxConnTotal(properties.getMaxConnTotaol())
.build();
return client;
}
}
@ConditionalOnClass:该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类;
@ConditionalOnMissingBean:该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean;可以给该注解传入参数例如@ConditionOnMissingBean(name = "example"),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行。
RestAutoConfig
创建负载均衡和直连的RestTemplate
@Configuration
public class RestAutoConfig { public static class RestTemplateConfig { @Bean//负载均衡的restTemplate
@LoadBalanced //spring 对restTemplate bean进行定制,加入loadbalance拦截器进行ip:port的替换
//"http://user/getusername,就能解析成http://127.0.0.1:8083//getusername
RestTemplate lbRestTemplate(HttpClient httpclient) {
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpclient));
template.getMessageConverters().add(,new StringHttpMessageConverter(Charset.forName("utf-8")));
template.getMessageConverters().add(,new FastJsonHttpMessageConvert5());
return template;
} @Bean //直连的restTemplat,这时只能使用http://127.0.0.1:8083//getusername地址,不能解析http://user/getusername
RestTemplate directRestTemplate(HttpClient httpclient) {
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpclient));
template.getMessageConverters().add(,new StringHttpMessageConverter(Charset.forName("utf-8")));
template.getMessageConverters().add(,new FastJsonHttpMessageConvert5());
return template;
} // FastJsonHttpMessageConvert4有一个bug,它是默认支持MediaType.ALL,spring在处理MediaType.ALL的时候会识别成字节流,而不是json,这里就对他进行改造和处理
public static class FastJsonHttpMessageConvert5 extends FastJsonHttpMessageConverter4{ static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); public FastJsonHttpMessageConvert5(){
setDefaultCharset(DEFAULT_CHARSET);
setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,new MediaType("application","*+json")));
} }
} }
调用RestTemplate的默认构造函数,RestTemplate对象在底层通过使用java.net包下的实现创建HTTP 请求,可以通过使用ClientHttpRequestFactory指定不同的HTTP请求方式。
ClientHttpRequestFactory接口主要提供了两种实现方式
- 一种是SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)创建底层的Http请求连接。
- 一种方式是使用HttpComponentsClientHttpRequestFactory方式,底层使用HttpClient访问远程的Http服务,使用HttpClient可以配置连接池和证书等信息。
GenericRest
既支持直连又支持服务发现的调用
/**
* 既支持直连又支持服务发现的调用
*
*/
@Service
public class GenericRest { @Autowired
@Qualifier("lbRestTemplate")
private RestTemplate lbRestTemplate; @Autowired
@Qualifier("directRestTemplate")
private RestTemplate directRestTemplate; private static final String directFlag = "direct://"; //返回的泛型用ParameterizedTypeReference<T>来指定
public <T> ResponseEntity<T> post(String url,Object reqBody,ParameterizedTypeReference<T> responseType){
RestTemplate template = getRestTemplate(url);
url = url.replace(directFlag, "");
return template.exchange(url, HttpMethod.POST,new HttpEntity(reqBody),responseType);
} public <T> ResponseEntity<T> get(String url,ParameterizedTypeReference<T> responseType){
RestTemplate template = getRestTemplate(url);
url = url.replace(directFlag, "");
return template.exchange(url, HttpMethod.GET,HttpEntity.EMPTY,responseType);
} private RestTemplate getRestTemplate(String url) {
if (url.contains(directFlag)) {
return directRestTemplate;
}else {
return lbRestTemplate;
}
} }
exchange支持‘含参数的类型’(即泛型类)作为返回类型,该特性通过‘ParameterizedTypeReference<T>responseType’描述。
进行服务调用:
public List<User> getUserList(User query) {
ResponseEntity<RestResponse<List<User>>> resultEntity = rest.post("http://"+ userServiceName + "/user/getList",query, new ParameterizedTypeReference<RestResponse<List<User>>>() {});
RestResponse<List<User>> restResponse = resultEntity.getBody();
if (restResponse.getCode() == ) {
return restResponse.getResult();
}else {
return null;
}
}
SpringCloud Feign—申明式服务调用
虽然RestTemplate已经可以将请求拦截来实现对依赖服务的接口调用,并对Http请求进行封装处理,形成一套模板化的调用方法,但是对服务依赖的调用可能不只一处,一个接口都会被多次调用,所以我们会像前面那样针对各个微服务字形封装一些客户端接口调用类来包装这些依赖服务的调用。
由于RestTemplate的封装,几乎每一个调用都是简单的模板化内容,Feign在此基础上做了进一步的封装,由它来帮助我们定义和实现依赖服务接口的定义。
在服务消费者创建服务调用接口,通过@FeignClient注解指定服务名来绑定服务,然后再使用SpringMVC的注解来绑定具体该服务提供的REST接口。
@FeignClient("biz-service-0")
public interface UserClient { @RequestMapping(method = RequestMethod.GET, value = "/getuser")
public User getuserinfo(); @RequestMapping(method = RequestMethod.GET, value = "/getuser")
public String getuserinfostr(); }
在服务消费者的web层进行调用:
@RestController
public class UserController { @Autowired
UserClient userClient; @RequestMapping(value = "/getuserinfo", method = RequestMethod.GET)
public User getuserinfo() {
return userClient.getuserinfo();
}
@RequestMapping(value = "/getuserinfostr", method = RequestMethod.GET)
public String getuserinfostr() {
return userClient.getuserinfostr(); }
通过Feign我们只需要定义服务绑定接口,以申明式的方法,优雅而简单的实现了服务调用。
HttpClient-RestTemplate-Feign的更多相关文章
- [Spring-Cloud-Alibaba] Sentinel 整合RestTemplate & Feign
Sentinel API Github : WIKI Sphu (指明要保护的资源名称) Tracer (指明调用来源,异常统计接口) ContextUtil(标示进入调用链入口) 流控规则(针对来源 ...
- Java实现远程服务生产与消费(RPC)的4种方法-RMI,WebService,HttpClient,RestTemplate
目录 一. 通过rmi实现远程服务的生产与消费 远程服务提供者实现. 创建rmi-provider项目(Maven) 远程服务消费者实现 创建rmi-consumer项目 二. 通过WebServic ...
- HttpClient&&RestTemplate学习
1. 什么是HttpClient HttpClient是Apache下面的子项目,可以提供高效的,最新的,功能丰富的支持HTTP协议的客户端编程工具包. 2. 为什么要学习HttpClient Htt ...
- Httpclient与RestTemplate的比较(比httpClient更优雅的Restful URL访问)
一.HttpClient (一)HttpClient 客户端 1.HttpClient 是 apache 的开源,需要引入两个包:httpclient-4.2.4.jar 和 httpcore-4.2 ...
- SpringBoot系列: RestTemplate 快速入门
====================================相关的文章====================================SpringBoot系列: 与Spring R ...
- spring cloud 声明式rest客户端feign调用远程http服务
在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.Feign就是Spring Cloud提供的一种声明式R ...
- 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务
在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.我们可以使用JDK原生的URLConnection.Ap ...
- RestTemplate发送HTTP、HTTPS请求
RestTemplate 使用总结 场景: 认证服务器需要有个 http client 把前端发来的请求转发到 backend service, 然后把 backend service 的结果再返 ...
- 使用Spring Cloud Feign
使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务 在spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就 ...
- 客户端远程调用Feign
客户端远程调用 Feign 什么是Feign? Feign是 Netflix 公司开源的声明式HTTP客户端 Github : Feign 源码 为什么需要Feign? 原代码可读性不高 复杂的URL ...
随机推荐
- 虚拟机 搭建LVS + DR + keepalived 高可用负载均衡
一:环境说明: LVS-DR-Master: 10.3.0.82 LVS-DR-Backup: 10.3.0.70 VIP: 10.3.0.60 ...
- 关于微信的jsdk的若干亲身实践之小结
前言: 业务来源:自主研发的手机app软件有分享文章到微信或者QQ以及微博的功能,而在微信中再次点击分享按钮的时候,情况就出现的不可把控了: 文章显示的缩略图不能正常显示:文章的简介不能显示……而我们 ...
- jQuery之ajaxForm提交表单
1.jQuery的设计非常优雅,其源代码亦给人以美感,利用jQuery框架写出来的js既简练又能完美跨浏览器. 2.jquery form插件是基于jQuery开发的一套能够利用ajax技术提 ...
- linux IP 设置
修改ip地址即时生效:# ifconfig eth0 192.168.1.102 netmask 255.255.255.0启动生效:修改/etc/sysconfig/network-scripts/ ...
- 2.6 CMMI2级——供应商协议管理(Supplier Agreement Management)
做软件开发的,不免要购买一些软硬件.软件可能是中间件.控件.插件.组件等,硬件可能是一些服务器.PDA.单片机等.只要稍微复杂的项目,都不可避免的会有采购的问题,就算目前没有采购,以后也会不可避免.另 ...
- Linux系统下Redis缓存安装配置
Redis是一个高性能的key-value数据库,现时越来越多企业与应用使用Redis作为缓存服务器.楼主是一枚JAVA后端程序员,也算是半个运维工程师了.在Linux服务器上搭建Redis,怎么可以 ...
- JavaMVC框架之SpringMVC
欢迎查看Java开发之上帝之眼系列教程,如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系.本系列教程希望您能站在上帝 ...
- 判断手机访问还是pc访问
function isMobile(){ // 如果有HTTP_X_WAP_PROFILE则一定是移动设备 if (isset ($_SERVER['HTTP_X_WAP_PROFILE'])) re ...
- javascript飞机大战-----001分析
1.游戏引擎 首先要做飞机大战要考虑的是这个游戏被分成了哪几大部分?这样我们一块一块去做,特别清晰明了.那么接下来我们就简单的分析下飞机大战分成了哪几大部分 1.游戏引擎 2.英雄机 3.敌机 4.子 ...
- codeforces#512 Div2
pre过了三题 终测又挂了一题 又掉分了 真的是 太菜了 A-In Search of an Easy Problem 水题 有一个1就是hard #include <bits/stdc++.h ...