Feign

依赖管理

统一依赖版本号

<!--SpringBoot版本号-->
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.4.2</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--版本号-->
<properties>
	<java.version>1.8</java.version>
	<mybatis.version>2.1.0</mybatis.version>
	<mysql.connector.version>6.0.6</mysql.connector.version>
	<spring-cloud.version>2020.0.1</spring-cloud.version>
	<spring-cloud-netflix.version>2.2.0.RELEASE</spring-cloud-netflix.version>
	<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
<!--依赖管理-->
<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>${spring-cloud.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-netflix-dependencies</artifactId>
			<version>${spring-cloud-netflix.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-alibaba-dependencies</artifactId>
			<version>${spring-cloud-alibaba.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>${mybatis.version}</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysql.connector.version}</version>
		</dependency>
	</dependencies>
</dependencyManagement>

前提

说Feign之前,得先说说RestTemplate,RestTemplate用于访问远程http接口,比如A服务需要调用B方服务的方法,就可以使用RestTemplate,使用方式:

微服务使用nacos注册发现,这里需要加入ribbon作为负载均衡,添加依赖

<!--依赖-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-netflix-ribbon</artifactId>
</dependency>

Java代码

Application:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.example.feign.client")
@MapperScan("com.example.service.order.dao")
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}

Service:

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private UserClient userClient;

    @Override
    public Order getOrderById(int id) {
        Order order = orderDao.selectById(id);
        // RestTemplate调用
        User user = getUser(order.getUserId());
        order.setUser(user);
        return order;
    }

    private User getUser(int userId) {
		// service-user是用户服务的服务名称,已经注册到nacos,这里不用写IP地址和端口号直接,使用服务名称即可
        String url = "http://service-user/user/get/" + userId;
        return restTemplate.getForObject(url, User.class);
    }

}

注意:用服务名称代替服务地址和端口号这种方式一定要加@LoadBalanced服务均衡注解和ribbon依赖,本人尝试不用这个注解,不加依赖,出现service-user服务找不到错误,这里踩了个坑!!!

以上是RestTemplate使用方式,看起来已经很简单了,但还是有点缺点:需要在代码上拼接URL地址,用起来不够优雅,来,我们们看看Feign优雅的做法

Feign使用

Feign也是远程服务调用的框架,它可以让我们使用接口一样调用远程服务,具体使用方法:

依赖
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Application代码:
@SpringBootApplication
@EnableDiscoveryClient
// 允许使用Feign远程调用
@EnableFeignClients
@MapperScan("com.example.service.order.dao")
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

//    @LoadBalanced
//    @Bean
//    public RestTemplate getRestTemplate() {
//        return new RestTemplate();
//    }

}
创建user服务FeignClient
// service-user是在nacos注册的服务名称
@FeignClient(value = "service-user")
public interface UserClient {

	// 只要把User服务的Controller对应的方法copy过来即可,不用实现
	// Feign会通过动态代理的方式帮我们调用Http请求
    @GetMapping("/user/get/{id}")
    User getUser(@PathVariable("id") int userId);

}
Service:
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderDao orderDao;

//    @Autowired
//    private RestTemplate restTemplate;

    @Autowired
    private UserClient userClient;

    @Override
    public Order getOrderById(int id) {
        Order order = orderDao.selectById(id);
        // RestTemplate调用
        // User user = getUser(order.getUserId());
        // Feign调用
        User user = userClient.getUser(order.getUserId());
        order.setUser(user);
        return order;
    }

//    private User getUser(int userId) {
//        String url = "http://service-user/user/get/" + userId;
//        return restTemplate.getForObject(url, User.class);
//    }

}

是不是变得更加优雅?!

性能优化

Feign默认底层使用URLConnection方式进行Http请求,这种方式是没有连接池的,调用一次连接一次,性能上可以优化,我们一般是使用HttpClient或OkHttp。

这里我使用OkHttp作为访问Http的方式,如下:

添加依赖
<dependency>
	<groupId>io.github.openfeign</groupId>
	<artifactId>feign-okhttp</artifactId>
</dependency>
application.yml修改
feign:
  httpclient:
    enabled: false
  okhttp:
    enabled: true

这样我们就允许使用OkHttp进行Http请求,如果需要设置最大连接数、单个服务最大连接数、连接超时时间等简单参数,只要添加配置

feign:
  httpclient:
    enabled: false
    max-connections: 200
    max-connections-per-route: 50
  okhttp:
    enabled: true

但是我们要一些复杂的配置,则需要添加配置类,这里注意了,我们不能简简单单弄一个@Bean出来,否则okhttp会失效。

我们需要copy一份OkHttpFeignConfiguration的代码,对其进行修改即可。

参考链接:https://blog.csdn.net/eric520zenobia/article/details/103547552

最佳实践

生产环境中,会出现A服务和B服务都调用C服务的方法,如果A服务和B服务都定义C服务的FeignClient,是不是很多余,我们可以把C服务的FeignClient抽取出来,作为公共模块使用。

所以一般会新建一个模块(feign-api),把C服务的FeignClient挪到这个模块,A服务和B服务依赖这个模块就行。

这里会出现一个情况,我们在Service使用Autowire注入的方式使用FeignClient:

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private UserClient userClient;

    @Override
    public Order getOrderById(int id) {
        Order order = orderDao.selectById(id);
        User user = userClient.getUser(order.getUserId());
        order.setUser(user);
        return order;
    }

}

因为UserClient被我挪到了feign-api模块,包名不一样,也不在OrderApplication所在的包,所以会扫描不到,userClient会是空的

所以我们还要在Application代码修改一下:


@SpringBootApplication
@EnableDiscoveryClient
// 添加包名,这样SpringBoot才能扫描
@EnableFeignClients(basePackages = "com.example.feign.client")
// 还可以使用这种方式,用哪一个加哪一个
// @EnableFeignClients(clients = {UserClient.class})
@MapperScan("com.example.service.order.dao")
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值