1. 程式人生 > >Spring cloud HSF 整合 HSF 高階特性

Spring cloud HSF 整合 HSF 高階特性

本頁目錄

在之前的文件中,HSF 開發已經介紹瞭如何使用 Spring Cloud 來開發 HSF 應用。

本文將介紹一下 HSF 的一些高階特性在 Spring Cloud 開發方式下的使用方式。目前內容包含 單元測試 和 非同步呼叫 兩部分,後續會有更多的介紹。

單元測試

spring-cloud-starter-hsf 的實現依賴於 Pandora Boot,Pandora Boot 的單元測試可以通過 PandoraBootRunner 啟動,並與 SpringJUnit4ClassRunner 無縫整合。

我們將演示一下,如何在服務提供者中進行單元測試,供大家參考。

  1. 在 Maven 中新增 spring-boot-starter-test 的依賴。

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-test</artifactId>
    4. </dependency>
  2. 編寫測試類的程式碼。

    1. @RunWith(PandoraBootRunner.class)
    2. @DelegateTo(SpringJUnit4ClassRunner.class)
    3. // 載入測試需要的類,一定要加入 Spring Boot 的啟動類,其次需要加入本類。
    4. @SpringBootTest(classes = {HSFProviderApplication.class, EchoServiceTest.class })
    5. @Component
    6. public class EchoServiceTest {
    7. /**
    8. * 當使用 @HSFConsumer 時,一定要在 @SpringBootTest 類載入中,載入本類,通過本類來注入物件,否則當做泛化時,會出現類轉換異常。
    9. */
    10. @HSFConsumer(generic = true)
    11. EchoService echoService;
    12. //普通的呼叫
    13. @Test
    14. public void testInvoke() {
    15. TestCase.assertEquals("hello world", echoService.echo("hello world"));
    16. }
    17. //泛化呼叫
    18. @Test
    19. public void testGenericInvoke() {
    20. GenericService service = (GenericService) echoService;
    21. Object result = service.$invoke("echo", new String[] {"java.lang.String"}, new Object[] {"hello world"});
    22. TestCase.assertEquals("hello world", result);
    23. }
    24. //返回值 Mock
    25. @Test
    26. public void testMock() {
    27. EchoService mock = Mockito.mock(EchoService.class, AdditionalAnswers.delegatesTo(echoService));
    28. Mockito.when(mock.echo("")).thenReturn("beta");
    29. TestCase.assertEquals("beta", mock.echo(""));
    30. }
    31. }

非同步呼叫

HSF 提供了兩種型別的非同步呼叫,Future 和 Callback。

  1. 在演示非同步呼叫之前,我們先發佈一個新的服務: com.aliware.edas.async.AsyncEchoService。

    1. public interface AsyncEchoService {
    2. String future(String string);
    3. String callback(String string);
    4. }
  2. 服務提供者實現 AsyncEchoService,並通過註解釋出。

    1. @HSFProvider(serviceInterface = AsyncEchoService.class, serviceVersion = "1.0.0")
    2. public class AsyncEchoServiceImpl implements AsyncEchoService {
    3. @Override
    4. public String future(String string) {
    5. return string;
    6. }
    7. @Override
    8. public String callback(String string) {
    9. return string;
    10. }
    11. }

    從這兩點中可以看出,服務提供端與普通的釋出沒有任何區別,同樣,之後的配置和應用啟動流程也是一致的,詳情請參考 HSF 開發中建立服務提供者部分的內容。

    注意:非同步呼叫的邏輯修改都在消費端,服務端無需做任何修改。

Future

  1. 使用 Future 型別的非同步呼叫的消費端,也是通過註解的方式將服務消費者的例項注入到 Spring 的 Context 中,並在 @HSFConsumer 註解的 futureMethods 屬性中配置非同步呼叫的方法名。

    這裡我們將 com.aliware.edas.async.AsyncEchoService 的 Future 方法標記為 Future 型別的非同步呼叫。

    1. @Configuration
    2. public class HsfConfig {
    3. @HSFConsumer(serviceVersion = "1.0.0", futureMethods = "future")
    4. private AsyncEchoService asyncEchoService;
    5. }
  2. 方法在被標記成 Future 型別的非同步呼叫後,同步執行時的方法返回值其實是 null,需要通過 HSFResponseFuture 來獲取呼叫的結果。

    我們在這裡通過 TestAsyncController 來進行演示,示例程式碼如下:

    1. @RestController
    2. public class TestAsyncController {
    3. @Autowired
    4. private AsyncEchoService asyncEchoService;
    5. @RequestMapping(value = "/hsf-future/{str}", method = RequestMethod.GET)
    6. public String testFuture(@PathVariable String str) {
    7. String str1 = asyncEchoService.future(str);
    8. String str2;
    9. try {
    10. HSFFuture hsfFuture = HSFResponseFuture.getFuture();
    11. str2 = (String) hsfFuture.getResponse(3000);
    12. } catch (Throwable t) {
    13. t.printStackTrace();
    14. str2 = "future-exception";
    15. }
    16. return str1 + " " + str2;
    17. }
    18. }

    呼叫 /hsf-future/123 ,可以看到 str1 的值為 null, str2 才是真實的呼叫返回值 123。

    呼叫 /hsf-future/123 單結果

  3. 當服務中需要結合一批操作的返回值進行處理時,參考如下的呼叫方式。

    1. @RequestMapping(value = "/hsf-future-list/{str}", method = RequestMethod.GET)
    2. public String testFutureList(@PathVariable String str) {
    3. try {
    4. int num = Integer.parseInt(str);
    5. List<String> params = new ArrayList<String>();
    6. for (int i = 1; i <= num; i++) {
    7. params.add(i + "");
    8. }
    9. List<HSFFuture> hsfFutures = new ArrayList<HSFFuture>();
    10. for (String param : params) {
    11. asyncEchoService.future(param);
    12. hsfFutures.add(HSFResponseFuture.getFuture());
    13. }
    14. ArrayList<String> results = new ArrayList<String>();
    15. for (HSFFuture hsfFuture : hsfFutures) {
    16. results.add((String) hsfFuture.getResponse(3000));
    17. }
    18. return Arrays.toString(results.toArray());
    19. } catch (Throwable t) {
    20. return "exception";
    21. }
    22. }

    呼叫 /hsf-future/123 多結果

Callback

  1. 使用 Callback 型別的非同步呼叫的消費端,首先建立一個類實現 HSFResponseCallback 介面,並通過 @Async 註解進行配置。

    1. @AsyncOn(interfaceName = AsyncEchoService.class,methodName = "callback")
    2. public class AsyncEchoResponseListener implements HSFResponseCallback{
    3. @Override
    4. public void onAppException(Throwable t) {
    5. t.printStackTrace();
    6. }
    7. @Override
    8. public void onAppResponse(Object appResponse) {
    9. System.out.println(appResponse);
    10. }
    11. @Override
    12. public void onHSFException(HSFException hsfEx) {
    13. hsfEx.printStackTrace();
    14. }
    15. }

    AsyncEchoResponseListener 實現了 HSFResponseCallback 介面,並在 @Async 註解中分別配置 interfaceName 為 AsyncEchoService.class、methodName 為 callback。

    這樣,就將 com.aliware.edas.async.AsyncEchoService 的 callback 方法標記為 Callback 型別的非同步呼叫。

  2. 同樣,通過 TestAsyncController 來進行演示,示例程式碼如下:

    1. @RequestMapping(value = "/hsf-callback/{str}", method = RequestMethod.GET)
    2. public String testCallback(@PathVariable String str) {
    3. String timestamp = System.currentTimeMillis() + "";
    4. String str1 = asyncEchoService.callback(str);
    5. return str1 + " " + timestamp;
    6. }

    執行呼叫,可以看到如下結果:

    呼叫 /hsf-callback/123 結果

    消費端將 callback 方法配置為 Callback 型別非同步呼叫時,同步返回結果其實是 null。

    結果返回之後,HSF 會呼叫 AsyncEchoResponseListener 中的方法,在 onAppResponse 方法中我們可以得到呼叫的真實返回值。

  3. 如果需要將呼叫時的上下文資訊傳遞給 callback ,需要使用 CallbackInvocationContext 來實現。

    呼叫時的示例程式碼入下:

    1. CallbackInvocationContext.setContext(timestamp);
    2. String str1 = asyncEchoService.callback(str);
    3. CallbackInvocationContext.setContext(null);

    AsyncEchoResponseListener 示例程式碼如下:

    1. @Override
    2. public void onAppResponse(Object appResponse) {
    3. Object timestamp = CallbackInvocationContext.getContext();
    4. System.out.println(timestamp + " " +appResponse);
    5. }

    我們可以在控制檯中看到輸出了 1513068791916 123,證明 AsyncEchoResponseListener 的 onAppResponse 方法通過 CallbackInvocationContext 拿到了呼叫前傳遞過來的 timestamp 的內容。

    hsf callback console