RESTful Web服务和API的优点在于,任何使用HTTP协议的使用者都可以理解和使用它。 但是,同样的难题一遍又一遍地弹出:您是否应该将Web APis与客户端库一起使用? 如果是,您应该支持哪些语言或/和框架?
通常这并不是一个容易回答的问题。 因此,让我们退后一步,想一想总体思路:客户端库可以为消费者带来什么价值?
也许有人会说降低采用的门槛。 确实,特别是在强类型语言的情况下,从您喜欢的IDE探索API合同(请语法突出显示和自动完成!)非常方便。 但是总的来说, RESTful Web API非常简单,一开始就可以使用,并且好的文档当然在这里会更有价值。
其他人可能说,保护消费者避免使用多个API版本或粗糙边缘是件好事。 也是有道理的,但我认为这只是掩盖了有关Web API的设计和随着时间的发展而存在的缺陷。
总而言之,无论您决定捆绑多少个客户端,任何通用的HTTP使用者( curl , HttpClient , RestTemplate ,您都可以命名)仍然可以访问这些API。 做出选择是很棒的,但是维护费用可能会很高。 我们可以做得更好吗? 正如您可能已经猜到的,因此在本帖子中,我们当然有很多选择。
成功的关键要素是使用OpenAPI v3.0或什至其前身Swagger / OpenAPI 2.0 (或RAML , API Blueprint并没有多大关系)来维护RESTful Web API的准确规范。 在使用OpenAPI / Swagger的情况下,该工具为王:可以使用Swagger Codegen (一种模板驱动的引擎)以多种不同的语言生成API客户端(甚至是服务器存根),这就是我们要讨论的内容在这篇文章中。
为了简化操作,我们将实现上一篇文章中构建的人员管理Web API的使用者。 首先,我们需要得到它开始的OpenAPI V3.0规范的YAML (或JSON )格式。
java -jar server-openapi/target/server-openapi-0.0.1-SNAPSHOT.jar
然后:
wget http://localhost:8080/api/openapi.yaml
太棒了,实际上完成了一半的工作。 现在,让我们让Swagger Codegen带头。 为了不使问题复杂化,我们假设使用者也是Java应用程序,因此我们可以毫无困难地理解其机制(但是Java只是其中一种选择, 受支持的语言和框架的列表令人惊讶)。
在本文中,我们将使用OpenFeign ,这是最高级的Java HTTP客户端绑定程序之一。 它不仅非常简单易用,而且还提供了许多集成,我们将从中很快受益。
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-core</artifactId><version>9.7.0</version>
</dependency><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-jackson</artifactId><version>9.7.0</version>
</dependency>
Swagger Codegen可以作为独立的应用程序从命令行运行,也可以作为Apache Maven插件运行(后者是我们要使用的插件)。
<plugin><groupId>io.swagger</groupId><artifactId>swagger-codegen-maven-plugin</artifactId><version>3.0.0-rc1</version><executions><execution><goals><goal>generate</goal></goals><configuration><inputSpec>/contract/openapi.yaml</inputSpec><apiPackage>com.example.people.api</apiPackage><language>java</language><library>feign</library><modelPackage>com.example.people.model</modelPackage><generateApiDocumentation>false</generateApiDocumentation><generateSupportingFiles>false</generateSupportingFiles><generateApiTests>false</generateApiTests><generateApiDocs>false</generateApiDocs><addCompileSourceRoot>true</addCompileSourceRoot><configOptions><sourceFolder>/</sourceFolder><java8>true</java8><dateLibrary>java8</dateLibrary><useTags>true</useTags></configOptions></configuration></execution></executions>
</plugin>
如果某些选项不是很清楚,那么Swagger Codegen会提供很好的文档来进行说明。 要注意的重要方面是language和library ,它们分别设置为java和feign 。 需要注意的一件事是, OpenAPI v3.0规范的支持大部分已经完成,但是您仍然可能会遇到一些问题(如您所注意到的,版本是3.0.0-rc1 )。
构建完成后,您将得到的是纯朴的Java接口PeopleApi (带有OpenFeign批注),它是人员管理Web API规范(来自/contract/openapi.yaml )的直接投影。 请注意,所有模型类也会生成。
@javax.annotation.Generated(value = "io.swagger.codegen.languages.java.JavaClientCodegen",date = "2018-06-17T14:04:23.031Z[Etc/UTC]"
)
public interface PeopleApi extends ApiClient.Api {@RequestLine("POST /api/people")@Headers({"Content-Type: application/json", "Accept: application/json"})Person addPerson(Person body);@RequestLine("DELETE /api/people/{email}")@Headers({"Content-Type: application/json"})void deletePerson(@Param("email") String email);@RequestLine("GET /api/people/{email}")@Headers({"Accept: application/json"})Person findPerson(@Param("email") String email);@RequestLine("GET /api/people")@Headers({"Accept: application/json"})List<Person> getPeople();
}
让我们将其与具有相同规范的Swagger UI解释进行比较,可从http:// localhost:8080 / api / api-docs?url = / api / openapi.json获得 :
乍一看似乎正确,但我们最好确保一切按预期进行。 一旦有了带有OpenFeign注释的接口,就可以使用Feign构建器家族将其启用 (在本例中,通过代理实现),例如:
final PeopleApi api = Feign.builder().client(new OkHttpClient()).encoder(new JacksonEncoder()).decoder(new JacksonDecoder()).logLevel(Logger.Level.HEADERS).options(new Request.Options(1000, 2000)).target(PeopleApi.class, "http://localhost:8080/");
伟大,流利的建筑风格的岩石。 假设我们的人员管理Web API服务器已启动并正在运行(默认情况下,它将在http:// localhost:8080 /上可用):
java -jar server-openapi/target/server-openapi-0.0.1-SNAPSHOT.jar
我们可以通过调用新构建的PeopleApi实例方法来与之通信,如下面的代码片段所示:
final Person person = api.addPerson(new Person().email("a@b.com").firstName("John").lastName("Smith"));
真的很酷,如果我们将其倒回一点,我们实际上什么也没做。 仅提供Web API规范,一切都是免费提供给我们的! 但是,让我们不要在这里停下来,提醒自己,使用Java接口不会消除我们正在处理远程系统的现实。 毫无疑问,事情迟早会在这里失败。
不久前,我们了解了断路器及其在分布式系统中正确应用时的实用性。 以某种方式将这个功能引入我们基于OpenFeign的客户端真的很棒。 请欢迎该家族的另一个成员HystrixFeign builder与Hytrix库无缝集成:
final PeopleApi api = HystrixFeign.builder().client(new OkHttpClient()).encoder(new JacksonEncoder()).decoder(new JacksonDecoder()).logLevel(Logger.Level.HEADERS).options(new Request.Options(1000, 2000)).target(PeopleApi.class, "http://localhost:8080/");
我们唯一需要做的就是将这两个依赖项添加到使用者的pom.xml文件中(严格来说,如果您不介意使用旧版本,则不需要hystrix-core )。
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-hystrix</artifactId><version>9.7.0</version>
</dependency><dependency><groupId>com.netflix.hystrix</groupId><artifactId>hystrix-core</artifactId><version>1.5.12</version>
</dependency>
可以说,这是集成多么容易和直接的最好的例子之一。 但这还不是故事的结局。 分布式系统中的可观察性从来没有像现在这样重要,正如我们不久前所了解的那样 ,分布式跟踪在帮助我们解决这一问题上非常有用。 再说一次, OpenFeign开箱即用地支持它,让我们来看一下。
OpenFeign与兼容OpenTracing的跟踪器完全集成。 Jaeger跟踪器就是其中之一,它具有非常好的Web UI前端,可以探索跟踪和依赖项。 让我们先运行它,幸运的是它是完全Docker化的。
docker run -d -e \COLLECTOR_ZIPKIN_HTTP_PORT=9411 \-p 5775:5775/udp \-p 6831:6831/udp \-p 6832:6832/udp \-p 5778:5778 \-p 16686:16686 \-p 14268:14268 \-p 9411:9411 \jaegertracing/all-in-one:latest
为了使OpenFeign客户端了解OpenTracing功能,还必须引入几个其他依赖项。
<dependency><groupId>io.github.openfeign.opentracing</groupId><artifactId>feign-opentracing</artifactId><version>0.1.0</version>
</dependency><dependency><groupId>io.jaegertracing</groupId><artifactId>jaeger-core</artifactId><version>0.29.0</version>
</dependency>
从Feign构建器的角度来看,唯一的变化(除了引入tracer实例之外)是将客户端包装到TracingClient中 ,如下面的代码片段所示:
final Tracer tracer = new Configuration("consumer-openapi").withSampler(new SamplerConfiguration().withType(ConstSampler.TYPE).withParam(new Float(1.0f))).withReporter(new ReporterConfiguration().withSender(new SenderConfiguration().withEndpoint("http://localhost:14268/api/traces"))).getTracer();final PeopleApi api = Feign.builder().client(new TracingClient(new OkHttpClient(), tracer)).encoder(new JacksonEncoder()).decoder(new JacksonDecoder()).logLevel(Logger.Level.HEADERS).options(new Request.Options(1000, 2000)).target(PeopleApi.class, "http://localhost:8080/");
在服务器端,我们还需要与OpenTracing集成。 Apache CXF 对它提供了一流的支持 ,捆绑到cxf-integration-tracing-opentracing模块中。 让我们将其作为依赖项包括在内,这一次是服务器的pom.xml 。
<dependency><groupId>org.apache.cxf</groupId><artifactId>cxf-integration-tracing-opentracing</artifactId><version>3.2.4</version>
</dependency>
根据配置应用程序的方式,应该有一个可用的跟踪程序实例,例如,稍后应将其传递给OpenTracingFeature 。
// Create tracer
final Tracer tracer = new Configuration("server-openapi", new SamplerConfiguration(ConstSampler.TYPE, 1),new ReporterConfiguration(new HttpSender("http://localhost:14268/api/traces"))).getTracer();// Include OpenTracingFeature feature
final JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setProvider(new OpenTracingFeature(tracer()));
...
factory.create()
从现在开始,通过生成的OpenFeign客户端对任何人员管理API端点的调用将完全可在Jaeger Web UI中找到,该Web UI可从http:// localhost:16686 / search获取 (假设Docker主机为localhost )。
我们的场景非常简单,但请想象一下实际的应用程序,其中单个请求在系统中传输时,可能会发生数十个外部服务调用。 没有适当的分布式跟踪,每个问题都有机会变成一个谜。
附带说明一下,如果您更靠近图片中的痕迹,则可能会注意到服务器和使用者使用了不同版本的Jaeger API。 这不是错误,因为最新发布的Apache CXF版本使用的是较旧的OpenTracing API版本(因此,也使用了较旧的Jaeger客户端API),但这不会阻止事情按预期进行。
这样,就该总结了。 希望在RESTful Web服务和API的世界中基于合同(甚至更好,首先合同)开发的好处变得越来越明显:智能客户端的生成, 消费者驱动的合同测试 ,可发现性和丰富的文档越来越多只是一些提及。 请利用它!
完整的项目资源可在Github上找到 。
翻译自: https://www.javacodegeeks.com/2018/06/provide-client-libraries-apis.html