springcloud学习day2--使用 fegin 发起远程调用和 gateway 统一网关
feign
使用 feign 代替 RestTemplate 发送远程调用请求
在使用RestTemplate 发送请求时,如果遇到复杂的请求参数,将会非常麻烦
所以我们可以使用springcloud的 feign 组件来发送远程调用请求
操作步骤:
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 在springboot启动类上添加 @EnableFeignClients 注解,开启 feign 的相关功能
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
- 编写 feign 的客户端,与 Controller 类的编写完全一致
@FeignClient("userservice") // 请求的服务名称
public interface UserClient {
//设置请求方式、请求路径、请求参数,和返回的数据类型
@GetMapping("/user/{id}")
User getUserById(@PathVariable("id") Long id);
}
- 在想要发起远程调用时,只需调用该接口即可请求到对应的服务
@Autowired
private UserClient userClient;
userClient.getUserById(id);
feign的性能优化
Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:
-
URLConnection:默认实现,不支持连接池
-
Apache HttpClient :支持连接池
-
OKHttp:支持连接池
因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。
优化步骤:
- 引入Apache的HttpClient依赖:
<!--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 # 每个路径的最大连接数
- 使用 jmeter 等工具来进行压力测试,找到连接池的最佳配置
feign 的最佳实践
按照上面的做法来发起远程调用,我们还会遇到这样的问题
问题:多个消费者,都使用了同一个服务者的服务,那么我们要在所有的消费者中把相同的代码都得写一遍,这显然是个体力活
优化方案:将 fegin 发起远程调用的代码抽取出来,封装为一个模块(fegin-api),在需要使用这些服务的模块中,引入fegin-api 依赖即可
操作步骤:
- 创建一个新的模块,引入 fegin 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 编写远程调用代码
// 发起远程调用代码
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User getUserById(@PathVariable("id") Long id);
}
//对应的实体类
@Data
public class User {
private Long id;
private String username;
private String address;
}
- 在需要使用该服务的模块中引入依赖
<dependency>
<groupId>cn.itcast.demo</groupId>
<artifactId>feign-api</artifactId>
<version>1.0</version>
</dependency>
- 在使用自动装配注入fegin-api中的对象时,可能会出现,bean加载失败的错误;这是因为springboot的默认注解扫描范围是启动类所在的包,而我们依赖的 fegin-api 中的包名,可能跟启动类的包名不一样,所以 fegin-api中的类并没有注册为 ioc 容器中的 bean
解决方式一:
指定Feign应该扫描的包:
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
解决方式二:
指定需要加载的Client接口:精确定位
@EnableFeignClients(clients = {UserClient.class})
统一网关 gateway
网关的作用
网关可以起到保护系统的作用
- 身份认证和权限校验
- 服务路由、负载均衡
- 请求限流
gateway
gateway是springcloud 中实现网关的一种方式
搭建步骤:
- 创建项目,引入nacos服务发现和gateway依赖
<!--gateway网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服务发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 配置application.yml,包括服务基本信息、nacos地址、路由
server:
port: 10010 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 网关路由配置
- id: user-service # 路由id,自定义,只要唯一即可
uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
- id: orderservice
uri: lb://orderservice
predicates:
- Path=/order/**
# 当有更多的服务时,可以按照上面的格式进行添加
路由断言工厂
上面的例子我们使用了 predicates: - Path=/user/**,这只是断言判断的一种方式
像这样的断言工厂在SpringCloudGateway还有十几个,我们可以根据需要来选择
| 名称 | 说明 | 示例 |
|---|---|---|
| After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
| Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
| Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
| Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
| Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
| Host | 请求必须是访问某个host(域名) | - Host=.somehost.org,.anotherhost.org |
| Method | 请求方式必须是指定方式 | - Method=GET,POST |
| Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
| Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
| RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
| Weight | 权重处理 |
网关过滤器
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:

过滤器工厂
过滤器可以对路由的请求和响应做加工处理,比如添加请求头,springcloud官方提供了30多种过滤器,我们可以根据需要进行选择
需求:给所有进入userservice的请求添加一个请求头:Truth=itcast is freaking awesome!
只需要修改gateway服务的application.yml文件,添加路由过滤即可:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
filters: # 过滤器
- AddRequestHeader=Truth,lalalala # 添加请求头
当前过滤器写在userservice路由下,因此仅仅对访问userservice的请求有效。
默认过滤器
如果要对所有的路由都生效,则可以将过滤器工厂写到default下。格式如下:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
default-filters: # 默认过滤项
- AddRequestHeader=Truth,lalalala
自定义全局过滤器
如果springcloud提供的过滤器不能满足我们的需求,我们还可以自定义全局过滤器
操作步骤:
- 自定义类,实现GlobalFilter 接口,并实现其 filter 方法
@Order(-1) //过滤器优先级,数字越大级别越低,执行顺序越靠后
@Component // 将过滤器注册为bean
public interface GlobalFilter {
/**
* 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
*
* @param exchange 请求上下文,里面可以获取Request、Response等信息
* @param chain 用来把请求委托给下一个过滤器
* @return {@code Mono<Void>} 返回标示当前过滤器业务结束
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
该过滤器会对所有的路由生效,并且可在里面添加自己的业务逻辑
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:

排序规则:
- 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
- GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
- 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
- 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
跨域问题
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS
在gateway服务的application.yml文件中,添加下面的配置:
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期

浙公网安备 33010602011771号