社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
链接地址:https://helpcdn.aliyun.com/document_detail/63868.html
在之前的文档中,HSF 开发已经介绍了如何使用 Spring Cloud 来开发 HSF 应用。
本文将介绍一下 HSF 的一些高级特性在 Spring Cloud 开发方式下的使用方式。目前内容包含 单元测试 和 异步调用 两部分,后续会有更多的介绍。
Demo 源码下载: sc-hsf-provider 、 sc-hsf-consumer。
spring-cloud-starter-hsf 的实现依赖于 Pandora Boot,Pandora Boot 的单元测试可以通过 PandoraBootRunner 启动,并与 SpringJUnit4ClassRunner 无缝集成。
我们将演示一下,如何在服务提供者中进行单元测试,供大家参考。
在 Maven 中添加 spring-boot-starter-test 的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
编写测试类的代码。
@RunWith(PandoraBootRunner.class)
@DelegateTo(SpringJUnit4ClassRunner.class)
// 加载测试需要的类,一定要加入 Spring Boot 的启动类,其次需要加入本类。
@SpringBootTest(classes = {HSFProviderApplication.class, EchoServiceTest.class })
@Component
public class EchoServiceTest {
/**
* 当使用 @HSFConsumer 时,一定要在 @SpringBootTest 类加载中,加载本类,通过本类来注入对象,否则当做泛化时,会出现类转换异常。
*/
@HSFConsumer(generic = true)
EchoService echoService;
//普通的调用
@Test
public void testInvoke() {
TestCase.assertEquals("hello world", echoService.echo("hello world"));
}
//泛化调用
@Test
public void testGenericInvoke() {
GenericService service = (GenericService) echoService;
Object result = service.$invoke("echo", new String[] {"java.lang.String"}, new Object[] {"hello world"});
TestCase.assertEquals("hello world", result);
}
//返回值 Mock
@Test
public void testMock() {
EchoService mock = Mockito.mock(EchoService.class, AdditionalAnswers.delegatesTo(echoService));
Mockito.when(mock.echo("")).thenReturn("beta");
TestCase.assertEquals("beta", mock.echo(""));
}
}
HSF 提供了两种类型的异步调用,Future 和 Callback。
在演示异步调用之前,我们先发布一个新的服务: com.aliware.edas.async.AsyncEchoService。
public interface AsyncEchoService {
String future(String string);
String callback(String string);
}
服务提供者实现 AsyncEchoService,并通过注解发布。
@HSFProvider(serviceInterface = AsyncEchoService.class, serviceVersion = "1.0.0")
public class AsyncEchoServiceImpl implements AsyncEchoService {
@Override
public String future(String string) {
return string;
}
@Override
public String callback(String string) {
return string;
}
}
从这两点中可以看出,服务提供端与普通的发布没有任何区别,同样,之后的配置和应用启动流程也是一致的,详情请参考 HSF 开发中创建服务提供者部分的内容。
注意:异步调用的逻辑修改都在消费端,服务端无需做任何修改。
使用 Future 类型的异步调用的消费端,也是通过注解的方式将服务消费者的实例注入到 Spring 的 Context 中,并在 @HSFConsumer 注解的 futureMethods 属性中配置异步调用的方法名。
这里我们将 com.aliware.edas.async.AsyncEchoService 的 Future 方法标记为 Future 类型的异步调用。
@Configuration
public class HsfConfig {
@HSFConsumer(serviceVersion = "1.0.0", futureMethods = "future")
private AsyncEchoService asyncEchoService;
}
方法在被标记成 Future 类型的异步调用后,同步执行时的方法返回值其实是 null,需要通过 HSFResponseFuture 来获取调用的结果。
我们在这里通过 TestAsyncController 来进行演示,示例代码如下:
@RestController
public class TestAsyncController {
@Autowired
private AsyncEchoService asyncEchoService;
@RequestMapping(value = "/hsf-future/{str}", method = RequestMethod.GET)
public String testFuture(@PathVariable String str) {
String str1 = asyncEchoService.future(str);
String str2;
try {
HSFFuture hsfFuture = HSFResponseFuture.getFuture();
str2 = (String) hsfFuture.getResponse(3000);
} catch (Throwable t) {
t.printStackTrace();
str2 = "future-exception";
}
return str1 + " " + str2;
}
}
调用 /hsf-future/123 ,可以看到 str1 的值为 null, str2 才是真实的调用返回值 123。
当服务中需要结合一批操作的返回值进行处理时,参考如下的调用方式。
@RequestMapping(value = "/hsf-future-list/{str}", method = RequestMethod.GET)
public String testFutureList(@PathVariable String str) {
try {
int num = Integer.parseInt(str);
List<String> params = new ArrayList<String>();
for (int i = 1; i <= num; i++) {
params.add(i + "");
}
List<HSFFuture> hsfFutures = new ArrayList<HSFFuture>();
for (String param : params) {
asyncEchoService.future(param);
hsfFutures.add(HSFResponseFuture.getFuture());
}
ArrayList<String> results = new ArrayList<String>();
for (HSFFuture hsfFuture : hsfFutures) {
results.add((String) hsfFuture.getResponse(3000));
}
return Arrays.toString(results.toArray());
} catch (Throwable t) {
return "exception";
}
}
使用 Callback 类型的异步调用的消费端,首先创建一个类实现 HSFResponseCallback 接口,并通过 @Async 注解进行配置。
@AsyncOn(interfaceName = AsyncEchoService.class,methodName = "callback")
public class AsyncEchoResponseListener implements HSFResponseCallback{
@Override
public void onAppException(Throwable t) {
t.printStackTrace();
}
@Override
public void onAppResponse(Object appResponse) {
System.out.println(appResponse);
}
@Override
public void onHSFException(HSFException hsfEx) {
hsfEx.printStackTrace();
}
}
AsyncEchoResponseListener 实现了 HSFResponseCallback 接口,并在 @Async 注解中分别配置 interfaceName 为 AsyncEchoService.class、methodName 为 callback。
这样,就将 com.aliware.edas.async.AsyncEchoService 的 callback 方法标记为 Callback 类型的异步调用。
同样,通过 TestAsyncController 来进行演示,示例代码如下:
@RequestMapping(value = "/hsf-callback/{str}", method = RequestMethod.GET)
public String testCallback(@PathVariable String str) {
String timestamp = System.currentTimeMillis() + "";
String str1 = asyncEchoService.callback(str);
return str1 + " " + timestamp;
}
执行调用,可以看到如下结果:
消费端将 callback 方法配置为 Callback 类型异步调用时,同步返回结果其实是 null。
结果返回之后,HSF 会调用 AsyncEchoResponseListener 中的方法,在 onAppResponse 方法中我们可以得到调用的真实返回值。
如果需要将调用时的上下文信息传递给 callback ,需要使用 CallbackInvocationContext 来实现。
调用时的示例代码入下:
CallbackInvocationContext.setContext(timestamp);
String str1 = asyncEchoService.callback(str);
CallbackInvocationContext.setContext(null);
AsyncEchoResponseListener 示例代码如下:
@Override
public void onAppResponse(Object appResponse) {
Object timestamp = CallbackInvocationContext.getContext();
System.out.println(timestamp + " " +appResponse);
}
我们可以在控制台中看到输出了 1513068791916 123
,证明 AsyncEchoResponseListener 的 onAppResponse 方法通过 CallbackInvocationContext 拿到了调用前传递过来的 timestamp 的内容。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!