当GraphQL遇到VSAM
Posted 三言两语CICS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了当GraphQL遇到VSAM相关的知识,希望对你有一定的参考价值。
VSAM作为Mainframe的核心文件系统,经过数十年的发展,积累了大量的企业用户数据。随着业务需求的不断变化,如何高效、简洁地访问VSAM中的数据是每一个企业在各个时期都要面临的问题。
我们来设想这样一个场景,一个外部的请求希望查询主机中的数据,需要在一次请求中,尽可能精确地获取需要的数据。看似很简单的一个需求,仔细分析一下,我们至少需要考虑以下几点:
一次请求获取所有的数据,数据格式可定制,不冗余。
需要对外提供服务,用于数据访问。
实现对VSAM文件的访问。
实现数据格式的转换。
为了解决以上问题,我们设计了如下架构:
GraphQL实现架构图
01
一次请求获取所有的数据,
数据格式可定制,不冗余
谈到“在一次请求中获取所有数据”,这就不得不提到GraphQL(https://graphql.cn)了。GraphQL是Facebook 2015年开源的数据查询规范。引用GraphQL官方文档的一句话“ask exactly what you want”。GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。
使用GraphQL至少可以帮我们解决以下问题:
1. 请求你所要的数据,不多不少
向你的 API 发出一个 GraphQL 请求就能准确获得你想要的数据,不多不少。GraphQL 查询总是返回可预测的结果。
2. 多个资源,只用一个请求
典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。
3. 使用你现有的数据和代码
GraphQL 引擎已经有多种语言实现,通过 GraphQL API 能够更好利用你的现有数据和代码。你只需要为类型系统的字段编写函数,GraphQL 就能通过优化并发的方式来调用它们。
Graphql-java(github.com/graphql-java/graphql-java)就是GraphQL标准的Java语言实现之一,下面我们只需要实现标准的Graphql-java的服务器端,以支持请求的解析、验证和执行GraphQL查询。
为了响应用户对数据的查询请求,我们需要GraphQL Schema来描述服务数据的结构。如下所示,我们定义了Query类型,它使用bookbyid返回书籍的详细信息。我们还需要定义Book类型,包括id、name、pageCount、author。其中author对应的是Author类型,因此我们又定义了Author类型。
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Long
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
在GraphQL服务器上需要通过Runtime Wiring,才能真正执行GraphQL Schema,使其运行起来。例如:
privateRuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
.type(newTypeWiring("Query")
.dataFetcher("bookById",graphQLDataFetchers.getBookByIdDataFetcher()))
.type(newTypeWiring("Book")
.dataFetcher("author",graphQLDataFetchers.getAuthorDataFetcher()))
.build();
}
GraphQL Schema中的每个字段类型,都需要关联一个DataFetcher,用来返回 Schema中定义好的数据。
这里我们一共实现了两个DataFetcher方法,需要注意的是第二方法GetAuthorDataFetcher()。与Book DataFetcher相比,我们没有参数,但是我们有一个Book实例。
这里需要理解一个重要概念:对于GraphQL中的每个字段,DataFetcher总是以自上而下的方式调用,父级的结果作为子级DataFetcher的DataFetcherEnvironment,从而保证我们可以使用之前获取的Book.authorId,然后使用authorId以查找和返回响应的Author信息。这也是我们能够在一次请求中查询多个数据源的关键。
public DataFetcher getBookByIdDataFetcher(){
return dataFetchingEnvironment -> {
String bookId =dataFetchingEnvironment.getArgument("id");
return book.getBook(bookId);
};
}
public DataFetchergetAuthorDataFetcher() {
return dataFetchingEnvironment -> {
Map<String, String> book =dataFetchingEnvironment.getSource();
String authorId =book.get("authorId");
return author.getAuthor(authorId);
};
}
02
需要对外提供服务,用于数据查询
事实上,我们创建了一个基于Spring-boot的Java Web工程。在这个web工程的基础上,经过上面几个步骤,我们搭建了一个简单的GraphQL服务器。GraphQL只关心查询本身,它不处理任何与HTTP或JSON相关的内容。如下所示,GraphQL通过@Bean让自己像其他Bean一样接受Spring的管理,然后由Spring负责通过HTTP暴露API。
@Component
public classGraphQLProvider {
private GraphQLgraphQL;
@Bean
public GraphQL graphQL() {
return graphQL;
}
@PostConstruct
public void init() throws IOException {
URL url =Resources.getResource("schema.graphqls");
String sdl = Resources.toString(url,Charsets.UTF_8);
GraphQLSchema graphQLSchema =buildSchema(sdl);
this.graphQL =GraphQL.newGraphQL(graphQLSchema).build();
}
private GraphQLSchema buildSchema(Stringsdl) {
//
…
}
}
考虑后续需要和主机交互,我们选择CICS Liberty作为这个工程的容器。CICS从V5版本开始引入了Liberty,全面支持JEE。Liberty为在CICS上进行应用开发打开了一扇全新的窗口。应用开发人员可以在集成开发环境上进行JEE web应用开发,然后部署到Liberty上。
这样,Java应用开发人员就能参与到CICS商业应用的开发、扩展和更新中来。Liberty为熟悉web技术的开发人员提供了能力,使得他们能够创建基于web的接口与现有CICS应用交互,将接口现代化。
03
实现对VSAM文件的访问
在早期的开发中,主机上的程序多以COBOL语言为主。基于COBOL语言,我们可以方便地使用CICS的API访问VSAM文件。在引入了Liberty之后,我们可以使用JCICS编写访问CICS资源的Java应用程序,JCICS已经支持EXEC CICS API的大部分功能。
例如,在本方案中,我们在实现GraphQL的DataFetcher功能时,返回了book.getBook(bookId)。我们需要在getBook(bookId)这个方法中,实现对VSAM的查询。
我们知道现有的COBOL程序(FCBOOK)已经实现了根据bookId查询VSAM文件的功能。我们要在程序中使用JCICS提供的LINK API和 Channel-Container完成对FCBOOK的调用和数据传输。部分代码如下:
import com.ibm.cics.server.*
…
public Map<String, Object> getBook(String bookId) {
…
// 创建channel和container,用于CICS LINK
Channel bookChannel = task.createChannel("QUARY-BOOK");
Container bdc = bookChannel.createContainer("QUARY-BOOKDATA");
bdc.putString(bookId);// "book-01 "
Program QuaryBook = new Program();
QuaryBook.setName("FCBOOK");
// LINK到已有的COBOL程序FCBOOK。
QuaryBook.link(bookChannel);
Channel currentCobolChannel = task.getChannel("QUARY-BOOK");
Container bdco = currentCobolChannel.getContainer("QUARY-BOOKDATA");
if (bdco != null) {
// 取回Container中的数据,并初始化bookData(Java POJO类的对象)
bookData = new BookData(bdco.get());
return ImmutableMap.of("id", bookData.getBookId(), "name", bookData.getBookName(), "pageCount",
bookData.getPagecount(), "authorId", bookData.getAuthrId());
}
…
}
另外,JCICS也提供了直接对VSAM文件访问的API,可以使用这些API直接访问VSAM文件,而不用通过LINK的方式,下表是访问KSDS文件的JCICS方法和对应的CICS API。
Classes and methods for keyed files
04
实现数据格式的转换
在上述实现中,我们直接从container("QUARY-BOOKDATA")中取出了COBOL的数据,并直接用这个数据初始化了我们的POJO对象。
这其中我们借助了IBM Record Generator 的帮助,它可以生成用来描述特定语言的记录结构的Java POJO类。这个POJO类可以帮助我们实现在z/OS应用程序(如CICS COMAMARAS或VSAM文件)中面向字节的记录结构和Java程序之间的数据交换,如下图所示
数据流
关于如何使用IBM Record Generator这里不再详细讲解,请参考。
最后,如下图所示,我们使用GraphQL的查询方式,在一次查询请求中,同时获取了BOOK和AUTHOR两种数据,并且以JSON的方式,精确地返回了我们想要的数据。
GraphQL 请求结果
综上,我们遵循GraphQL的查询规范,用一次请求完成了对主机上多个VSAM文件的查询。依托CICS Liberty的运行时环境,我们使用JCICS完成了对CICS资源和程序的调用,同时使用IBM Record Generator工具帮助我们解决了COBOL COPYBOOK和JAVA POJO之间的数据映射问题。
整个解决方案以CICS Liberty为核心,其关键点在于CICS TS作为一个主机平台交易中间件,在融入Java的过程中自己也扩展到了应用服务器的领域,能够为客户提供更多的开发语言和更灵活的应用框架,以助力客户快速开发,应对当前外部业务日新月异的变化与挑战。
了解更多
[1] https://graphql.cn
[2] Using JCICS:https://ibm.co/2XD6kEp
[3] https://github.com/graphql-java/graphql-java
[4]
[5] CICS对Java的支持:https://ibm.co/2XLojbF
dypdongATcn.ibm.com
IBM中国系统实验室CICS L3技术支持工程师,致力于解决CICS Web Service相关问题。兼任IBM CICS statistics visualizer研发工程师和技术支持
liuniaoqATcn.ibm.com
现任IBM中国系统实验室CICS Service团队组长,在交易中间件领域从事相关技术工作七年
* 替换AT为@
扫码关注 “三言两语CICS”
内容声明:本文仅代表作者的个人观点,与IBM立场、策略和观点无关。文中专业名词因翻译原因,表述中难免存在差异。如有疑惑,请以英文为准。同时数据来源于实验室环境,仅供参考。如果您对我们的话题感兴趣,请给我们留言。
以上是关于当GraphQL遇到VSAM的主要内容,如果未能解决你的问题,请参考以下文章