赞
踩
微服务都是独立部署的,要实现一个业务可能需要多个服务之间的通信,所以远程调用必不可少,本文将提供三种远程调用的方案:jodd-http、RestTemplate、Feign。
在此说一下 springboot父子 工程的搭建流程:
先创建一个springboot项目,然后删掉不必要的文件,如下图:
然后创建子项目,新建模块,选择 maven 工程
注意:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring.cloud-version>2021.0.5</spring.cloud-version> </properties> <dependencyManagement> <dependencies> <!--spring-cloud--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud-version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--spring-cloud-alibaba--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.6.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
前期准备:
搭建好工程:
remote_service 是模拟远程服务的一个模块,其 controller 代码如下:
@RequestMapping("/remote")
@RestController
public class RemoteController {
@GetMapping("/{name}")
public String sayHello(@PathVariable("name") String name) {
return name.toUpperCase() + " 发起了请求,并请求成功";
}
}
RestTemplate 是spring自带的远程调用工具,所以只要有 spring 的依赖,都可以直接使用(随便导入一个 spring-boot-dependencies 或者 spring-boot-starter-web 都行)。
在使用 RestTemplate 之前,需要先在容器中注册。将下述代码写在一个配置类(@Configuration)中,或者直接写在 启动类(main)中都可以。
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
controller 代码:
@RequestMapping("/rest")
@org.springframework.web.bind.annotation.RestController
public class RestController {
private static final String NAME = "rest_template";
@Autowired
private RestTemplate restTemplate;
@GetMapping
public String sayHello() {
String url = "http://127.0.0.1:8088/remote/" + NAME;
String msg = restTemplate.getForObject(url, String.class);
return msg;
}
}
测试: 请求成功。
RestTemplate 的使用比较简单,这里再简单说一下它的常用方法:
方法 | 请求 | 说明 |
---|---|---|
getForEntity | GET | 常用:public <T> ResponseEntity<T> getForEntity(地址, 返回类型) |
getForObject | GET | 常用:public <T> T getForObject(地址, 返回类型) 对getForEntity函数的进一步封装,只关注返回的消息体的内容,对其他信息都不关注 |
postForEntity | POST | 常用:public <T> ResponseEntity<T> postForEntity(地址, 上传的参数(可为空),返回类型) |
postForObject | POST | 常用:public <T> ResponseEntity<T> postForObject(地址, 上传的参数(可为空),返回类型) 对postForEntity函数的进一步封装,只关注返回的消息体的内容,对其他信息都不关注 |
postForLocation | POST | 常用:public URI postForLocation(地址, 上传的参数(可为空)) 返回新资源所在的 url |
put | PUT | 常用:public void put(地址, 上传的参数(可为空)) |
delete | DELETE | 常用:public void delete(地址, 上传的参数) |
在这里补充一下 Restful 的 HTTP动词 :
需要导入两个依赖:
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<!-- jodd-http -->
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<version>3.7.1</version>
</dependency>
工具类:
import com.alibaba.fastjson.JSONObject; import jodd.http.HttpRequest; import jodd.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.Map; public class HttpUtil { public static String post(String url, Map<String, Object> map) { HttpResponse res = HttpRequest.post(url).connectionTimeout(90000).timeout(90000) .contentType("application/json", "UTF-8") .bodyText(JSONObject.toJSONString(map), "application/json", "UTF-8") .send(); res.charset("utf-8"); return res.bodyText(); } public static String post(String url, String jsonStr) { HttpResponse resp = HttpRequest.post(url).connectionTimeout(60000).timeout(60000) .contentType("application/json", StandardCharsets.UTF_8.toString()) .bodyText(jsonStr ,"application/json", "UTF-8") .send(); resp.charset(StandardCharsets.UTF_8.toString()); return resp.bodyText(); } public static String get(String url, Map<String, String> params) { HttpRequest request = HttpRequest.get(url); if (params != null) { request.query(params); } HttpResponse response = request.send(); return response.bodyText(); } }
Controller:
@RequestMapping("/jodd")
@RestController
public class JoddController {
private static final String NAME = "jodd_http";
@GetMapping
public String sayHello() {
String url = "http://127.0.0.1:8088/remote/" + NAME;
String msg = HttpUtil.get(url, new HashMap<>());
return msg;
}
}
注意: 这个我在之前的文章中讲过,点击跳转 ,这里就不再多说。
鉴于前面的两种方法,对于参数复杂URL难以维护,所以建议使用 Feign 。
Feign 是基于 springcloud 来发起远程请求的,需要与 nacos 等注册中心搭配使用,点击跳转注册中心文章。
要引入 Feign 依赖,就需要先引入 springcloud 的依赖:
<!--spring-cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
注意: 引入 springcloud 依赖的时候,需要注意 springboot 的版本。
然后导入 Feign 依赖:
<!--Feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.2</version>
</dependency>
注意: 启动项目报错: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
解决方案: https://blog.csdn.net/qq_43788878/article/details/115764008
在启动类上添加一个 @EnableFeignClients
编写 Feign 接口
@Component
//@FeignClient:微服务客户端注解,value:指定微服务的名字,这样就可以使Feign客户端直接找到对应的微服务
@FeignClient(value = "remote")
public interface MyFeignClient {
@GetMapping("/remote/{name}")
String sayHello(@PathVariable("name") String name);
}
这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:
这样,Feign就可以帮助我们发送http请求,无需自己使用RestTemplate来发送了。
controller
@RestController("/feign")
public class FeignController {
private static final String NAME = "feign";
@Resource
private MyFeignClient feignClient;
@GetMapping
public String sayHello() {
String msg = feignClient.sayHello(NAME);
return msg;
}
}
以上就是 Feign 的使用步骤,因为懒得去搭建注册中心,所以就不测试了。
Feign可以支持很多的自定义配置,如下表所示:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般情况,直接使用默认配置就OK了。
自定义配置的方法:
方法一:修改配置文件
feign:
client:
config:
remote: # 针对某个微服务的配置,这里也可以使用 default(全局配置),针对所有微服务
loggerLevel: FULL # 日志级别
日志的四种级别:
- NONE:不记录任何日志信息,这是默认值
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据
方式二:Java代码
先声明一个类,然后声明一个Logger.Level的对象
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.FULL; // 日志级别为BASIC
}
}
启动类添加注解(全局配置,针对所有微服务):
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
添加到自定义 FeignClient 接口中(针对某一微服务):
@FeignClient(value = "remote", configuration = DefaultFeignConfiguration .class)
URLConnection:默认实现,不支持连接池
Apache HttpClient :支持连接池
OKHttp:支持连接池
优化演示:
引入 feign-httpclient 依赖(带连接池的客户端)
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置连接池和日志
feign:
client:
config:
default: # default全局的配置
loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
httpclient:
enabled: true # 开启feign对HttpClient的支持
max-connections: 200 # 最大的连接数
max-connections-per-route: 50 # 每个路径的最大连接数
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。