天天看點

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

openfeign內建sentinel實作服務降級

  • 使用openfeign調用服務(不含sentinel)
    • 代碼
    • 測試
  • openfeign內建sentinel實作服務降級
    • 引入sentinel相關環境
    • 編寫@FeignClient注解接口的實作類
    • 在服務提供者中,人為添加異常代碼,以供測試 / 或者不啟動服務提供者,服務消費者找不到服務提供者,也會報錯,同樣能夠達到測試效果!
    • 測試,啟動服務提供者及服務消費者
    • 使用openfeign的工廠模式fallbackFactory來實作服務降級
      • 編寫屬性fallbackFactory配置的類,并實作接口FallbackFactory 《T》
      • 在@FeignClient中配置fallbackFacroty屬性=FallbackFactoryConfig .class
      • 同樣,編寫類FallbackFactoryConfigImpl實作@FeignClient注解的接口
      • 在FallbackFactoryConfig中實作工廠接口FallbackFactory的方法 create
      • 測試,浏覽器通路:[http://localhost:8083/another/consumer](http://localhost:8083/another/consumer)
      • 另外一種方式擷取異常資訊,其他的沒有變化,隻是直接将方法的實作寫在FallbackFactoryConfig裡面

使用openfeign調用服務(不含sentinel)

代碼

application.properties

server.port=8083
spring.application.name=nacos-consumer02
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.enabled=true

spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos

#feign.sentinel.enabled=true

#開啟熱部署
#spring.devtools.restart.enabled=true
           

主啟動類

package com.xl.projects;

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 NacosConsumer02Application {
	public static void main(String[] args) {
		SpringApplication.run(NacosConsumer02Application.class, args);
	}
}

           

Controller

package com.xl.projects.controller;

import javax.annotation.Resource;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.xl.projects.feign.Consumer02Feign;

@RestController
public class Consumer02Controller {
	
	@Resource
	private Consumer02Feign consumer02Feign;
	
	@GetMapping("/another/consumer")
	public String testInvoke() {
		return consumer02Feign.invokeProvider("oooh!~~~,this is another consumer,named consumer02");
	}
	
}

           

@FeignClient注解的接口

package com.xl.projects.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "nacos-provider",fallback = FeignFallbackImpl.class)
public interface Consumer02Feign {

	/**
	 * 	注意!!!,這裡需要顯示的指定@RquestParam,否者調用的時候會報錯!
	 * 
	 * @param param
	 * @return
	 */
	@GetMapping("/provider/test")
	String invokeProvider(@RequestParam String param);
}

           

以上代碼為服務調用者(消費者)代碼,以下為服務提供者的Controller代碼,其他部分略:

package com.xl.projects.controller;

import java.util.Date;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * For Test!!!
 * @author xl
 *
 */
@RestController
public class TestProviderController {
	
	
	@Value("${spring.application.name}")
    private String appName;
	
	/**
	 * For Test!
	 * @param param
	 * @return
	 */
	@GetMapping("/provider/test")
	public String test(String param) {
//		throw new RuntimeException("服務端測試異常!");
		return new Date().getSeconds()+
				", this is provider return msg: current param="+param;
	}
	
	@GetMapping("/test")
	public String testParam() {
		return appName;
	}
}
           

pom.xml 中需添加spring cloud open feign的依賴:

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.xl.projects</groupId>
		<artifactId>xl-springcloud-parent-pom</artifactId>
		<version>1.0.0</version>
	</parent>
	<artifactId>xl-nacos-cunsumer02</artifactId>


	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>
		
<!-- 		<dependency> -->
<!--             <groupId>com.alibaba.cloud</groupId> -->
<!--             <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> -->
<!--         </dependency> -->
        
<!--         <dependency> -->
<!-- 			<groupId>org.springframework.boot</groupId> -->
<!-- 			<artifactId>spring-boot-devtools</artifactId> -->
<!-- 			<scope>test</scope> -->
<!-- 		</dependency> -->

	</dependencies>

</project>
           

測試

啟動Nacos, 啟動服務提供者項目以及服務消費者項目。浏覽器通路:

http://localhost:8083/another/consumer :

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

上圖說明使用openfeign調用成功。

在上面@FeignClient注解的接口代碼中,@FeignClient注解有個屬性fallback!這個fallback是幹什麼用的呢?見官網:

https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#spring-cloud-feign-circuitbreaker

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級
Spring Cloud CircuitBreaker supports the notion of a fallback: a default code path that is executed when the circuit is open
 or there is an error.
           

大意翻譯 : 當遇到一個錯誤或者熔斷器處于開放狀态,那麼程式就會執行fallback屬性配置的類!

To enable fallbacks for a given @FeignClient set the fallback attribute to the class name that implements the fallback. 
You also need to declare your implementation as a Spring bean.
           

大意翻譯 : fallback配置的類需要實作@FeignClient配置的接口,并且還的是一個Spring bean (類上需要加上@Component注解!)

根據以上翻譯内容,“當程式遇到錯誤時,就會執行fallback配置的類” ,說明fallback可以充當服務降級的作用,那如何通過fallback來實作服務降級呢?

openfeign內建sentinel實作服務降級

可通過openfeign的注解@FeignClient的屬性fallback結合sentinel實作服務降級!

引入sentinel相關環境

引入依賴

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
           

application.properties中引入配置(啟用):feign.sentinel.enabled=true

server.port=8083
spring.application.name=nacos-consumer02
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.enabled=true

spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos

feign.sentinel.enabled=true

#開啟熱部署
#spring.devtools.restart.enabled=true
           

編寫@FeignClient注解接口的實作類

package com.xl.projects.feign;

import org.springframework.stereotype.Component;

@Component
public class FeignFallbackImpl implements Consumer02Feign {

	@Override
	public String invokeProvider(String param) {
		return ">>>>>>>>>>>>fallback results>>>>>>>>>>>>";
	}

}
           
openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

在服務提供者中,人為添加異常代碼,以供測試 / 或者不啟動服務提供者,服務消費者找不到服務提供者,也會報錯,同樣能夠達到測試效果!

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

測試,啟動服務提供者及服務消費者

啟動服務消費者時,控制台報錯,啟動失敗!

Caused by: java.lang.IllegalAccessError: class org.springframework.cloud.openfeign.HystrixTargeter$$EnhancerBySpringCGLIB$$29430b6f cannot access its superclass org.springframework.cloud.openfeign.HystrixTargeter
	at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_201]
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_201]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_201]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_201]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_201]
	at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:535) ~[spring-core-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	... 78 common frames omitted
           

解決:

删除/取消項目熱部署,但,有個問題是熱部署依賴是在父項目的pom中的,直接屏蔽掉,其他子項目都無法使用熱部署的功能了。是以,需要将熱部署的依賴拷貝到本服務消費的pom中,同時将<scope>改為test。因為這樣會覆寫父pom中的依賴,同時scope為test也不會影響正常的編譯階段和運作階段。

修改後 :

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.xl.projects</groupId>
		<artifactId>xl-springcloud-parent-pom</artifactId>
		<version>1.0.0</version>
	</parent>
	<artifactId>xl-nacos-cunsumer02</artifactId>


	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>
		
		<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        
        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>test</scope>
		</dependency>

	</dependencies>

</project>
           

再次啟動,成功!通路位址 :

http://localhost:8083/another/consumer

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

驗證成功!

使用openfeign的工廠模式fallbackFactory來實作服務降級

形如:

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

根據@FeignClient的屬性fallback=xxx.class已經可以達到服務降級的目的了,為什麼還需要fallbackFactory呢??

見官網 :

If one needs access to the cause that made the fallback trigger, one can use the fallbackFactory attribute inside @FeignClient
           

大意翻譯 : 如果想要擷取到觸發fallback的原因(如:異常資訊等),那麼可以通過配置fallbackFactory來擷取。

編寫屬性fallbackFactory配置的類,并實作接口FallbackFactory 《T》

說明 :

  • FallbackFactory的包為:feign.hystrix.FallbackFactory ; 而非org.springframework.cloud.openfeign.FallbackFactory !

  • 泛型 T 為 @FeignClient注解的接口類型,也可以是實作接口的類

package com.xl.projects.feign;

import org.springframework.stereotype.Component;

import feign.hystrix.FallbackFactory;

@Component
public class FallbackFactoryConfig implements FallbackFactory<Consumer02Feign> {

	@Override
	public Consumer02Feign create(Throwable cause) {
		FallbackFactoryConfigImpl fallbackFactoryConfigImpl = new FallbackFactoryConfigImpl();
		fallbackFactoryConfigImpl.setThrowable(cause);
		return fallbackFactoryConfigImpl;
	}

	
	/*
	@Override
	public FallbackFactoryConfigImpl create(Throwable cause) {
		
		FallbackFactoryConfigImpl fallbackFactoryConfigImpl
		= new FallbackFactoryConfigImpl() {

			@Override
			public String invokeProvider(String param) {
				System.out.println("調用了實作工廠");
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public String test2() {
				// TODO Auto-generated method stub
				return super.test2();
			}
			
		};
		
		return fallbackFactoryConfigImpl;
	}
*/
}
           

在@FeignClient中配置fallbackFacroty屬性=FallbackFactoryConfig .class

package com.xl.projects.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "nacos-provider",fallbackFactory = FallbackFactoryConfig.class)
public interface Consumer02Feign {

	/**
	 * 	注意!!!,這裡需要顯示的指定@RquestParam,否者調用的時候會報錯!
	 * 
	 * @param param
	 * @return
	 */
	@GetMapping("/provider/test")
	String invokeProvider(@RequestParam String param);
	
	
	@GetMapping("/test2")
	String test2() ;
	
}
           

同樣,編寫類FallbackFactoryConfigImpl實作@FeignClient注解的接口

package com.xl.projects.feign;

public class FallbackFactoryConfigImpl implements Consumer02Feign {
	
	private Throwable throwable;
	
	@Override
	public String invokeProvider(String param) {
		System.out.println("調用了實作接口 :"+ this.getThrowable().getMessage());
		return "調用了實作接口 :"+ this.getThrowable().getMessage();
	}

	@Override
	public String test2() {
		// TODO Auto-generated method stub
		return null;
	}

	public Throwable getThrowable() {
		return throwable;
	}

	public void setThrowable(Throwable throwable) {
		this.throwable = throwable;
	}

}

           

在FallbackFactoryConfig中實作工廠接口FallbackFactory的方法 create

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

在抛出異常時,上圖建立的fallbackFactoryConfigImpl對象,會去調用自己對應的方法invokeProvider,同時擷取到對應的異常資訊“cause”。

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

測試,浏覽器通路:http://localhost:8083/another/consumer

controller

package com.xl.projects.controller;

import javax.annotation.Resource;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.xl.projects.feign.Consumer02Feign;

@RestController
public class Consumer02Controller {
	
	@Resource
	private Consumer02Feign consumer02Feign;
	
	@GetMapping("/another/consumer")
	public String testInvoke() {
		return consumer02Feign.invokeProvider("oooh!~~~,this is another consumer,named consumer02");
	}
	
}
           
openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

因為,并沒有啟動服務提供者nacos-provider,是以會抛出該異常,同時通過fallbackFactory的方式擷取到了異常資訊,符合預期!!!

另外一種方式擷取異常資訊,其他的沒有變化,隻是直接将方法的實作寫在FallbackFactoryConfig裡面

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級
package com.xl.projects.feign;

import org.springframework.stereotype.Component;

import feign.hystrix.FallbackFactory;

@Component
public class FallbackFactoryConfig implements FallbackFactory<Consumer02Feign> {

//	@Override
//	public Consumer02Feign create(Throwable cause) {
//		FallbackFactoryConfigImpl fallbackFactoryConfigImpl = new FallbackFactoryConfigImpl();
//		fallbackFactoryConfigImpl.setThrowable(cause);
//		return fallbackFactoryConfigImpl;
//	}

	
	
	@Override
	public FallbackFactoryConfigImpl create(Throwable cause) {
		
		FallbackFactoryConfigImpl fallbackFactoryConfigImpl
		= new FallbackFactoryConfigImpl() {

			@Override
			public String invokeProvider(String param) {
				System.out.println("調用了實作工廠");
				// TODO Auto-generated method stub
				return "調用了實作工廠,異常資訊為: " + cause.getMessage();
			}

			@Override
			public String test2() {
				// TODO Auto-generated method stub
				return super.test2();
			}
			
		};
		
		return fallbackFactoryConfigImpl;
	}

}

           

測試結果:

openfeign內建sentinel實作服務降級使用openfeign調用服務(不含sentinel)openfeign內建sentinel實作服務降級

同樣擷取到了異常資訊,說明FallbackFactoryConfig 中方法invokeProvider覆寫了FallbackFactoryConfigImpl 中的方法的實作!

繼續閱讀