一、feign使用
1、集成方法
1.1、pom
consumer添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
1.2、启动类
consumer启动类加上@EnableFeignClients注解,即开启feign;
1.3、consumer调用provider:
编写一个中间service,consumer调用service,service调用provider。这个service类头加@FeignClient注解:
@FeignClient(value = "my-service",path = "/myProvider")
FeignClient的几个属性:
(1)value:provider配置文件中的spring.application.name,
注意这里的value值不分大小写,my-service和MY-SERVICE都可以。
(2)name:name和value这两个属性的作用是一样的,如果没有配置url,那么配置的值将作为服务的名称,用于服务的发现,反之只是一个名称。
(3)path:如果provider在配置文件里面配置了server.context-path,FeignClient就需要指明path。
(4)url:属性一般用于调试程序,允许我们手动指定@FeignClient调用的地址。
@FeignClient(value = "goods-service", url="http://127.0.0.1:7001")
public interface GoodsService
{
@GetMapping("/goods/list")
String getGoodsList();
}
2、调用方法
上面提到的service,关于service调用provider传参的问题:
2.1、无参调用
post、get请求都可以
2.2、非对象参数
如Integer、String等,适用于get请求。
service方法上需要加@RequestParam注解,且括号中一定要有值(参数的别名)和被调用方provider的参数名保持一致,被调用方可加@RequestParam也可不加;
2.3、对象参数
javaBean,map,list等,只适用于post请求。
被调用方provider加@RequestBody注解,调用方service的方法可加可不加;
2.4、header头部参数:
provider和service方法上都使用@RequestHeader注解,且value指明参数别名。
二、集成demo
1、初版
1.1、父pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo.nacos</groupId>
<artifactId>nacos-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>nacos-demo</name>
<packaging>pom</packaging>
<!-- springBoot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<!-- 模块说明:这里声明多个子模块 -->
<modules>
<module>provider</module>
<module>consumer</module>
<module>nacos-common</module>
</modules>
<!-- 版本说明:这里统一管理依赖的版本号 -->
<dependencyManagement>
<dependencies>
<!-- SpringCloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
</dependency>
<!-- SpringCloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<!--服务注册-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<!-- nacos 注册中心客户端 -->
<!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>-->
<!-- <version>2.1.0.RELEASE</version>-->
<!-- </dependency>-->
<!--集中配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--nocas必须在web环境下-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.2、provider:
(1)pom:
<parent>
<artifactId>nacos-demo</artifactId>
<groupId>com.demo.nacos</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.demo.nacos</groupId>
<artifactId>nacos-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- SpringCloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
</dependency>
<!-- SpringCloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
</dependency>
<!--服务注册-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--集中配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
(2)bootstrap:
spring:
application:
name: my-service
cloud:
compatibility-verifier:
enabled: false
nacos:
#服务注册
discovery:
server-addr: xxx.xx.xxx:8848
#配置中心
config:
server-addr: xxx.xx.xxx:8848
group: wtyy
profiles:
active: prod
server:
port: 1111
servlet:
context-path: /myProvider
(3)启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
(4)controller接口
import com.demo.nacos.common.dto.UserDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/configTest")
//刷新配置
@RefreshScope
public class ProviderTestController {
@Value("${user.text}")
private String userText;
private static Logger logger = LoggerFactory.getLogger(ProviderTestController.class);
/**
* 1、无参 无返回
* @param request
* @return
* @throws InterruptedException
*/
@GetMapping("/hanNoParam")
public void getUserText(HttpServletRequest request) throws InterruptedException {
logger.info("调用无参接口");
}
/**
* 2、有参
* @return
* @throws InterruptedException
*/
@GetMapping("/hanParams")
public String hanParams(String userName) throws InterruptedException {
logger.info("调用有参接口");
return userName+":"+userText;
}
/**
* 3、有参,对象参数
* @return
* @throws InterruptedException
*/
@PostMapping("/hanObjParams")
public UserDTO hanObjParams(@RequestBody UserDTO userDTO) throws InterruptedException {
logger.info("调用对象参数接口");
return userDTO;
}
/**
* 4、参数加header
* @return
* @throws InterruptedException
*/
@PostMapping("/hanParamsAndHeader")
public String hanParamsAndHeader(@RequestBody UserDTO userDTO,@RequestHeader("token") String token) throws InterruptedException {
logger.info("调用参数和header接口");
return userDTO.getUserName()+"的token:"+token;
}
}
1.3、consumer:
(1)pom:
<parent>
<artifactId>nacos-demo</artifactId>
<groupId>com.demo.nacos</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.demo.nacos</groupId>
<artifactId>nacos-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- SpringCloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
</dependency>
<!-- SpringCloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
</dependency>
<!--服务注册-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--集中配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
</dependencies>
(2) bootstrap:
spring:
application:
name: my-consumer
cloud:
compatibility-verifier:
enabled: false
nacos:
#服务注册
discovery:
server-addr: xxx.xx.xxx:8848
#配置中心
config:
server-addr: xxx.xx.xxx:8848
group: wtyy
server:
port: 2222
servlet:
context-path: /myConsumer
(3) 启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
(4) service:重点
import com.demo.nacos.common.dto.UserDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "my-service",path = "/myProvider")
public interface NacosConfigTestService {
@RequestMapping("/configTest/hanNoParam")
void hanNoParam();
@RequestMapping("/configTest/hanParams")
String hanParams(@RequestParam("userName")String userName);
@RequestMapping("/configTest/hanObjParams")
UserDTO hanObjParams(@RequestBody UserDTO userDTO);
@RequestMapping("/configTest/hanParamsAndHeader")
String hanParamsAndHeader(@RequestBody UserDTO userDTO,@RequestHeader("token") String token);
}
(5) controler:
import com.demo.nacos.common.dto.UserDTO;
import com.demo.nacos.consumer.service.NacosConfigTestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/consumerTest")
public class ConsumerTestController {
@Autowired
private NacosConfigTestService nacosConfigTestService;
@GetMapping("/hanNoParam")
public void getTemp(HttpServletRequest request) throws InterruptedException {
nacosConfigTestService.hanNoParam();
}
@GetMapping("/hanParams")
public String hanParams(String userName) throws InterruptedException {
return nacosConfigTestService.hanParams(userName);
}
@PostMapping("/hanObjParams")
public UserDTO hanObjParams(@RequestBody UserDTO userDTO) throws InterruptedException {
return nacosConfigTestService.hanObjParams(userDTO);
}
@PostMapping("/hanParamsAndHeader")
public String hanParamsAndHeader(@RequestBody UserDTO userDTO,HttpServletRequest request) throws InterruptedException {
String token = request.getHeader("token");
return nacosConfigTestService.hanParamsAndHeader(userDTO,token);
}
}
1.4、测试
分别启动provider、consumer,可以看到都注册到nacos上了:
(1)非对象参数
(2)对象参数
(3)对象参数&header
2、feign负载均衡
将provider配置文件修改为1112再启动一次,可以看到nacos上provider有两个节点:
2.1、默认轮询:
下面调用多次http://localhost:2222/myConsumer/consumerTest/hanNoParam接口,可以看到两个provider轮询调用:
这是因为nacos上两个节点的权重默认都是一样的:
2.2、自定义权重:
下面将1111节点权重设置为2,1112节点设置为0:
再次请求接口,从控制台日志可以看到都打到1111节点上了。
2.3、下线:
(1)这时点击1111节点的下线:
因为在线上的1112节点权重为0(等于下线),所以请求接口报错:
(2)而将1112节点权重改为1,这时相当于1111下线了,线上是1112这个单节点
再次请求接口,请求都走到1112节点了
(3)点击1111节点的上线按钮,恢复双节点:
多次请求接口,可以看到两个节点都有访问。
3、feign的继承特性
上面的代码有很多重复的地方,下面利用feign的继承特性进行封装。
(1)公共代码封装
把公共代码封装成接口,放到commom组件中
package com.demo.nacos.common.api;
import com.demo.nacos.common.dto.UserDTO;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RequestMapping("/configTest")
public interface CommonTestService {
/**
* 1、无参 无返回
* @return
* @throws InterruptedException
*/
@RequestMapping("/hanNoParam")
void hanNoParam();
/**
* 2、有参
* @return
* @throws InterruptedException
*/
@RequestMapping("/hanParams")
String hanParams(@RequestParam("userName")String userName);
/**
* 3、有参,对象参数
* @return
* @throws InterruptedException
*/
@RequestMapping("/hanObjParams")
UserDTO hanObjParams(@RequestBody UserDTO userDTO);
/**
* 4、参数加header
* @return
* @throws InterruptedException
*/
@RequestMapping("/hanParamsAndHeader")
String hanParamsAndHeader(@RequestBody UserDTO userDTO,@RequestHeader("token") String token);
}
(2)provider生产者controller接口改造:
实现公共接口并覆写方法即可
package com.demo.nacos.provider.controller;
import com.demo.nacos.common.api.CommonTestService;
import com.demo.nacos.common.dto.UserDTO;
import com.demo.nacos.provider.mapper.UserDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestController
//刷新配置
@RefreshScope
public class ProviderTestController implements CommonTestService {
@Value("${user.text}")
private String userText;
private static Logger logger = LoggerFactory.getLogger(ProviderTestController.class);
@Autowired
private UserDao userDao;
@RequestMapping("/getAllUsers")
public List<UserDTO> getAllUsers(){
return userDao.getAllUsers();
}
/**
* 1、无参 无返回
* @return
* @throws InterruptedException
*/
@Override
public void hanNoParam() {
logger.info("调用无参接口");
}
/**
* 2、有参
* @return
* @throws InterruptedException
*/
@Override
public String hanParams(String userName) {
logger.info("调用有参接口");
return userName+":"+userText;
}
/**
* 3、有参,对象参数
* @return
* @throws InterruptedException
*/
@Override
public UserDTO hanObjParams(@RequestBody UserDTO userDTO) {
logger.info("调用对象参数接口");
return userDTO;
}
/**
* 4、参数加header
* @return
* @throws InterruptedException
*/
@Override
public String hanParamsAndHeader(@RequestBody UserDTO userDTO,@RequestHeader("token") String token) {
logger.info("调用参数和header接口");
return userDTO.getUserName()+"的token:"+token;
}
}
(3)feign调用
继承即可
package com.demo.nacos.consumer.service;
import com.demo.nacos.common.api.CommonTestService;
import com.demo.nacos.common.dto.UserDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "my-service",path = "/myProvider")
public interface NacosConfigTestService extends CommonTestService {
}
(4)测试:
所有接口调用正常
无参:
普通参数:
对象参数&header: