SpringCloudAlibaba OpenFeign
Nachdem wir zuvor die Nacos-Dienstregistrierung und -Erkennung verwendet haben, kann der Dienst-Remoteaufruf mit RestTemplate+Ribbon oder OpenFeign aufgerufen werden. In der tatsächlichen Entwicklung wird RestTemplate selten zum Aufrufen von Diensten verwendet. Bei jedem Aufruf müssen Adressen eingegeben und verschiedene Parameter konfiguriert werden, was sehr mühsam ist. Dieses Problem kann mit OpenFeign gelöst werden.
Wenn es also um OpenFeign geht, müssen Sie Feign erwähnen, da OpenFeign eine erweiterte Version von Feign ist. Feign ist ein leichter Restful-HTTP-Dienstclient mit integriertem Menüband für den Client-Lastausgleich. Wenn Sie Feign verwenden, müssen Sie nur eine Schnittstelle definieren und Anmerkungen hinzufügen, was den schnittstellenorientierten Programmiergewohnheiten entspricht und Remote-Dienstaufrufe einfacher macht.
OpenFeign ist eine weitere Kapselung von Feign, wodurch es die Standardanmerkungen und HttpMessageConverter von Spring MVC wie @RequestMapping unterstützt.
Integrieren Sie OpenFeign
Integrieren Sie OpenFeign in den Consumer-Client
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
bootstrap.yml
Fügen Sie OpenFeign-Unterstützung für Sentinel hinzu und fügen Sie Konfigurationselemente feign.sentinel.enabled
hinzu
server:
port: 9001
spring:
application:
name: consumer # 应用名
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务地址
sentinel:
transport:
port: 8719 # 启动http server,并且该服务与Sentinel仪表板进行交互,使sentinel可以控制应用,若端口占用则8719+1依次扫描
dashboard: 127.0.0.1:8080 # 仪表版访问地址
feign: # 增加对sentinel的支持
sentinel:
enabled: true
Lassen Sie mich hier darüber sprechen. Wenn keine feign.sentinel.enabled=true
Konfiguration hinzugefügt wird, wird die durch das Fallback-Attribut in @FeignClient definierte benutzerdefinierte Verarbeitungslogik wie Ausnahmen und Strombegrenzung nicht wirksam.
Fügen Sie der Hauptstartklasse eine Anmerkung hinzu @EnableFeignClients
und markieren Sie sie wie folgt, um OpenFeign zu aktivieren:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Consumer {
public static void main(String[] args) {
SpringApplication.run(Consumer.class, args);
}
}
Der Autor möchte hier noch einmal erwähnen, dass bei einem verteilten Projekt beim Extrahieren von Openfeign als separates Servicemodul der Paketname in meinem Openfeign möglicherweise nicht mit anderen Modulen übereinstimmt. Sie können die FeignClient-Komponente selbst deklarieren, um den Scanpfad festzulegen von FeignClient basePackages
. Beispiele sind wie folgt:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.alibaba.provider.feigns")
public class Consumer {
public static void main(String[] args) {
SpringApplication.run(Consumer.class, args);
}
}
Fügen Sie einen FeignClient-Client hinzu
/**
* "provider" : 表示调用的生产者服务名
* fallback:异常时进入的处理类
*/
@FeignClient(value = "provider", fallback = OpenFeignTestServiceFallback.class)
public interface OpenFeignTestService {
@RequestMapping(value = "/openFeignProviderTest", method = RequestMethod.GET)
public String openFeignProviderTest();
}
Erstellen Sie OpenFeignTestServiceFallback für die Fallback-Verarbeitung
@Component
public class OpenFeignTestServiceFallback implements OpenFeignTestService{
@Override
public String openFeignProviderTest() {
return "我是兜底方法";
}
}
Erstellen Sie einen Controller als Schnittstelle zum einfachen Aufrufen
@RestController
public class OpenFeignTestController {
@Resource
private OpenFeignTestService openFeignTestService;
@RequestMapping("/openFeignTest")
public String openFeignTest() {
return openFeignTestService.openFeignProviderTest();
}
}
Stellen Sie auf der Seite des Dienstproduzenten eine Schnittstelle bereit
@RestController
public class OpenFeignProviderTest {
@RequestMapping("/openFeignProviderTest")
public String openFeignProviderTest() {
return "OpenFeignTestController#openFeignProviderTest" + RandomUtils.nextInt(0, 1000);
}
}
Testschnittstelle
/openFeignTest
Sie können Curl oder einen Browser verwenden, um den Schnittstellentest auf der Service-Consumer-Seite aufzurufen .
curl http://localhost:9001/openFeignTest
==>OpenFeignTestController#openFeignProviderTest748
Optimieren Sie den Code:
In der @FeignClient-Annotation füllt das Wertattribut den Dienstnamen des Dienstanbieters aus. Es ist unangemessen, den Wert direkt zu schreiben. Wenn sich der Name des Dienstanbieters ändert, muss das, was hier geschrieben wird, geändert werden, und wenn ja wird verwendet in In mehr Fällen muss es überall geändert werden. Wie man es löst?
- Verwenden Sie eine separate Klasse für den Dienstnamen und definieren Sie statische Konstanten
- Verwenden Sie die Konfigurationsdateimethode und verwenden Sie Ausdrücke, um sie abzurufen.
Das Definieren einer statischen Konstantenklasse wird nicht mehr demonstriert. Verwenden Sie die Konfigurationsdateimethode:
provider:
name: provider
Verwenden Sie den Ausdruck, um den FeignClient-Client abzurufen
@FeignClient(value = "${provider.name}", fallback = OpenFeignTestServiceFallback.class)
Wird in Kombination mit Sentinel-Regeln verwendet
Unabhängig davon, ob es sich um RestTemplate+Ribbon oder den Remote-Aufruf von OpenFeign handelt, unterstützt es Sentinel. Konfigurieren Sie die Regeln zur Ressourcenflusssteuerung im Sentinel-Bedienfeld
Hinweis: Sentinel ist eine Lazy-Loading-Methode. Sie müssen die Schnittstelle einmal aufrufen, bevor Sie Regeln in der Konsole hinzufügen können
Fügen Sie im Sentinel-Panel openFeignProviderTest
Flusskontrollregeln, Schwellenwerttyp QPS, eigenständiger Schwellenwert 1 hinzu
/openFeignProviderTest
Okay, wir haben den Datenverkehr auf der Serverseite begrenzt. Versuchen Sie daher, mehrmals hintereinander auf die Client-Schnittstelle zuzugreifen.
Es ist ersichtlich, dass bei einigen Anforderungen die Fallback-Verschlechterung auftritt.
Wie also mit Ausnahmen umgehen?
Ändern Sie den Code der Serverschnittstelle, legen Sie eine Ausnahme fest und testen Sie die Schnittstelle
Ok, ich habe festgestellt, dass bei einer Ausnahme auf der Serverseite immer noch der Fallback-Prozess auf der Clientseite aktiviert wird. Ist das nicht sehr schön?
Was passiert also, wenn der Server ausfällt? Versuchen Sie, den Server auszuschalten.
Ja, Sie können auch in die Fallback-Verarbeitung einsteigen.
Implementieren Sie den Lastausgleich
OpenFeign verfügt außerdem über eine Lastausgleichsfunktion. Bei mehreren Servern wird der entsprechende Algorithmus verwendet, um einen Server zu finden, der Anfragen stellt.
Als nächstes gibt der Server die Portnummer des aktuellen Projekts aus und erstellt ein neues Serverprojekt.
@RestController
public class OpenFeignProviderTest {
@Value("${server.port}")
private Integer port;
@RequestMapping("/openFeignProviderTest")
public String openFeignProviderTest() {
return "OpenFeignTestController#openFeignProviderTest" + port;
}
}
Erstellen Sie ein neues Projekt und stellen Sie den Port auf 8003 ein.
server:
port: 8002
...多余部分省略
server:
port: 8003
...多余部分省略
Das Projekt ist gestartet und in nacos zu finden. Es gibt zwei Instanzen des Servers.
Rufen Sie die Client-Schnittstelle mehrmals auf, um die Ergebnisse anzuzeigen
Fordern Sie Dienste abfragend an.
OpenFeign-Timeout-Konfiguration
2020版本以前
Die Standardwartezeit von OpenFeign für die Rückgabe von Daten durch die Schnittstelle beträgt 1s
. Wenn sie 1 Sekunde überschreitet, wird ein Fehler gemeldet. Wenn ein Fallback vorliegt, wird eine Fallback-Verarbeitung durchgeführt.
Nach der Version 2020 lautet der Quellcode wie folgt:
public Options() {
//10L: connectTimeout
//60L: readTimeout
this(10L, TimeUnit.SECONDS, 60L, TimeUnit.SECONDS, true);
}
connectTimeout beträgt 10 Sekunden, readTimeout beträgt 60 Sekunden
Mit gewöhnlichen Schnittstellen gibt es kein Problem, aber wenn es sich um ein zeitaufwändiges Geschäft handelt, muss der Ausführungsprozess länger als 1 Sekunde dauern, was nicht sinnvoll ist.
Dann bietet OpenFeign eine Timeout-Konfiguration. Timeout-Konfiguration zur Consumer- Datei bootstrap.yml
hinzufügen :
feign.client.config.default.connectTimeout
:Timeout beim Verbindungsaufbaufeign.client.config.default.readTimeout
: Timeout-Konfiguration der Zeit, die zum Lesen von Ressourcen vom Server benötigt wird, nachdem die Verbindung hergestellt wurde
server:
port: 9001
spring:
application:
name: consumer # 应用名
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务地址
sentinel:
transport:
port: 8719 # 启动http server,并且该服务与Sentinel仪表板进行交互,使sentinel可以控制应用,若端口占用则8719+1依次扫描
dashboard: 127.0.0.1:8080 # 仪表版访问地址
feign:
sentinel:
enabled: true
client:
config:
default:
connectTimeout: 2000 # 建立连接超时时间
readTimeout: 2000 # 读取资源超时时间
@RequestMapping("/openFeignProviderTest")
public String openFeignProviderTest() throws InterruptedException {
Thread.sleep(5000);
return "OpenFeignTestController#openFeignProviderTest" + port;
}
Hier schlafen wir absichtlich 5 Sekunden lang und die Zeitüberschreitungszeit beträgt 2 Sekunden. Dann muss eine Zeitüberschreitung erfolgen und der Fallback wird aktiviert.
Versuchen Sie, die Zeit länger anzupassen
..
feign:
sentinel:
enabled: true
client:
config:
default:
connectTimeout: 7000 # 建立连接超时时间
readTimeout: 7000 # 读取资源超时时间
Kein Problem
OpenFeigns Protokoll
Um das Auffinden von Ausnahmen im Allgemeinen in der lokalen Entwicklungsumgebung zu erleichtern, drucken Sie die Protokolldetails der OpenFeign-Remote-Aufrufschnittstelle aus.
Fügen Sie die Konfiguration der Protokollebene in der Datei hinzu und fügen Sie den Paketnamen des 服务消费者bootstrap.yml
neuen Konfigurationselements hinzu, um die Schnittstelle zu deklarieren. Die vollständige Konfiguration lautet wie folgt:logging.level.
server:
port: 9001
spring:
application:
name: consumer # 应用名
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务地址
sentinel:
transport:
port: 8719 # 启动http server,并且该服务与Sentinel仪表板进行交互,使sentinel可以控制应用,若端口占用则8719+1依次扫描
dashboard: 127.0.0.1:8080 # 仪表版访问地址
feign:
sentinel:
enabled: true
client:
config:
default:
connectTimeout: 5000 # 建立连接超时时间
readTimeout: 5000 # 读取资源超时时间
logging:
level:
com.alibaba.provider.feigns: debug # 打印自己项目中com.alibaba.provider.feigns包的日志,级别是debug级别
Erstellen Sie dann eine Konfigurationsklasse in Java
@Configuration
public class OpenFeignLoggerConfiguration {
@Bean
public Logger.Level openFeignLoggerLevel() {
return Logger.Level.FULL; // FULL日志级别
}
}
OpenFeign-Protokolle haben die folgenden Ebenen
- NONE: kein Datensatz, Standard
- BASIC: Nur Anforderungsmethode und URL sowie Antwortstatuscode und Ausführungszeit aufzeichnen
- HEADERS: Zeichnen Sie nur grundlegende Informationen sowie Anforderungs- und Antwortheader auf
- VOLLSTÄNDIG: Zeichnen Sie den Header, den Text und die Metadaten der Anfrage und Antwort mit möglichst vollständigen Informationen auf
Nach der Konfiguration sieht es wie folgt aus:
Die Informationen sind sehr vollständig und leicht zu beheben.
Anforderungs- und Antwortkomprimierung
OpenFeign unterstützt die Anforderungs- und Antwortkomprimierung durch einfache Konfiguration, gzip
um die Effizienz der Datenübertragung zu verbessern.
Durch das Einschalten der Komprimierung können Netzwerkressourcen effektiv gespart werden, es erhöht jedoch den Druck auf die CPU während des Komprimierungs- und Dekomprimierungsprozesses. Es ist am besten, den Parameter für die minimale Anforderungslänge zu erhöhen.
bootstrap.yml
Fügen Sie der Datei eine Parameterkonfiguration (Dienstkonsumentenseite) hinzu :
server:
port: 9001
spring:
application:
name: consumer # 应用名
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务地址
sentinel:
transport:
port: 8719 # 启动http server,并且该服务与Sentinel仪表板进行交互,使sentinel可以控制应用,若端口占用则8719+1依次扫描
dashboard: 127.0.0.1:8080 # 仪表版访问地址
feign:
sentinel:
enabled: true
client:
config:
default:
connectTimeout: 5000 # 建立连接超时时间
readTimeout: 5000 # 读取资源超时时间
compression:
request:
enabled: true # 请求压缩启用
mime-types: text/xml, application/xml, application/json # 要压缩的类型
min-request-size: 2048 # 最小请求长度 单位:字节
response:
enabled: true # 响应压缩启用
logging:
level:
com.alibaba.provider.feigns: debug # 打印com.alibaba.provider.feigns包的日志,级别是debug级别
Nach der Komprimierung können Sie die Protokollsituation sehen:
Übergabe von OpenFeign-Parametern
Einfache Parameterübergabe
Serverschnittstelle
@PostMapping("/sampleParamsProviderTest")
public String sampleParamsProviderTest(@RequestParam("name") String name, @RequestParam("id") Integer id) {
return "OpenFeignProviderTest# sampleParamsProviderTest#" + port + "id=" + id + ",name=" + name;
}
ConsumerOpenFeign
@FeignClient(value = "${provider.name}", fallback = OpenFeignTestServiceFallback.class)
public interface OpenFeignTestService {
@PostMapping("/sampleParamsProviderTest")
public String sampleParamsProviderTest(@RequestParam("name") String name, @RequestParam("id") Integer id);
}
Rückfall der Verbraucher
@Component
public class OpenFeignTestServiceFallback implements OpenFeignTestService {
@Override
public String sampleParamsProviderTest(String name, Integer id) {
return "我是兜底方法" + name + id;
}
}
Verbraucher stellen eine Schnittstelle zum Testen bereit
@GetMapping("/sampleParamsProviderTest")
public String sampleParamsProviderTest() {
return openFeignTestService.sampleParamsProviderTest("gangge", 1);
}
@SpringQueryMap-Objektübergabe
Erstellen Sie dann zunächst ein zu übergebendes Objekt, das von Dienstanbietern und Verbrauchern gemeinsam genutzt wird.
public class Params {
private Integer id;
private String name;
.../getter、setter方法
}
Der Dienstanbieter erstellt eine Schnittstelle, über die Verbraucher anrufen können
@GetMapping("/springQueryMapProviderTest")
public String springQueryMapProviderTest(Params params) {
return "OpenFeignProviderTest# sampleParamsProviderTest#" + port + "id=" + params.getId() + ",name=" + params.getName();
}
ConsumerOpenFeign
@GetMapping("/springQueryMapProviderTest")
public String springQueryMapProviderTest(@SpringQueryMap Params params);
Rückfall der Verbraucher
@Override
public String springQueryMapProviderTest(Params params) {
return "我是兜底方法" + params.getId() + params.getName();
}
Verbraucherschnittstelle
@GetMapping("/springQueryMapTest")
public String springQueryMapTest() {
Params params = new Params();
params.setId(1);
params.setName("gangge");
return openFeignTestService.springQueryMapProviderTest(params);
}
Ergebnis
Komplexe Objektübertragung
Im Falle von Objekten innerhalb von Objekten bereiten Sie ein ComplexObject
und Result
ein Objekt vor (sowohl Anbieter als auch Verbraucher müssen sie erstellen).
ComplexObject
public class ComplexObject {
private Params params;
public Params getParams() {
return params;
}
public void setParams(Params params) {
this.params = params;
}
}
Result
public class Result {
private Integer code;
private String describe;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
}
Schreiben Sie eine Schnittstelle für den Dienstanbieter, akzeptieren Sie Verbraucheraufrufe und geben Sie dann das Ergebnisobjekt zurück.
@PostMapping("/complexObjectProviderTest")
public Result complexObjectProviderTest(@RequestBody ComplexObject complexObject) {
Result result = new Result();
result.setCode(200);
result.setDescribe("#complexObjectProviderTest" + complexObject.getParams().getName() + complexObject.getParams().getId());
return result;
}
Der Verbraucher deklariert die Clientschnittstelle und den Fallback-Vorgang
@PostMapping("/complexObjectProviderTest")
public Result complexObjectProviderTest(@RequestBody ComplexObject complexObject);
@Override
public Result complexObjectProviderTest(ComplexObject complexObject) {
return null;
}
Ergebnisobjekt zurückgeben**
@PostMapping("/complexObjectProviderTest")
public Result complexObjectProviderTest(@RequestBody ComplexObject complexObject) {
Result result = new Result();
result.setCode(200);
result.setDescribe("#complexObjectProviderTest" + complexObject.getParams().getName() + complexObject.getParams().getId());
return result;
}
Der Verbraucher deklariert die Clientschnittstelle und den Fallback-Vorgang
@PostMapping("/complexObjectProviderTest")
public Result complexObjectProviderTest(@RequestBody ComplexObject complexObject);
@Override
public Result complexObjectProviderTest(ComplexObject complexObject) {
return null;
}
Anmerkungen sind für komplexe Objekte völlig in Ordnung @RequestBody
, es kann jedoch nur einen @RequestBody-Parameter geben.