SI in Action:HTTP + Spring ws

HTTP

declarative就是解耦

  我们知道Spring框架可以帮助开发者实现面向接口编程,基于dependency injection (DI)可以为POJO注入依赖,这样起码实现了编译时解耦(运行时依然是实际对象之间紧耦合);Spring Integration更进一步:SI中POJOs可以作为处理节点,节点之间使用消息通道Channel串接起来形成完整的消息处理管道Pipe,由于Channel是通用组件,因此节点之间完全无耦合(开发编译时和运行时都没有耦合)。这样的应用程序是通过通道组装各种细粒度、可重用的节点构成功能管道(Such an application is built by assembling fine-grained reusable components to form a higher level of functionality.),每一条组装好的管道甚至还可以再次组合,si与springBatch及spring ws等可以结合。

  除了内部的这些消息通道,对外SI提供有通道适配器(channel adapters) 和网关(gateways) 来导入外部系统的消息

1、通道适配器是单向的one-way、只能发或者收;

2、gateway是r-r双向request/reply、可以同时做应答;

  比如inbound-channel-adapter只接收不响应可用于文件、inbound-gateway可以响应。 Spring Cloud Stream项目使用SI作为消息驱动的微服务运行引擎。官网示例Examples展示一个gateway接口(而且只有接口)可用于两种不同的服务实现一个是最简单的请求响应一个是webservice,这需要spring-integration-ws 和spring-integration-xml模块。Spring3位spring-ws扩展了restful支持,si2.0使用了Spring3的REST支持,用spring-ws入栈网关暴露POX/soap接口。

  SI的gateway可以作为抽象的调用stub:<int:gateway service-interface="" />. 之后的处理逻辑可以是同样写在xml配置文件里的表达式而不需要代码,和各种DSL结合的轻量xml处理也算是si的目标吧。

SI架构

  si里有用的一个东西是HTTP,因为借助它我们可以绕过soap,soap坑爹很久了,但对于遗留的soap格式交互无法改用rest的,可以基于si HTTP直接处理HTTP soap + 手撸(un)mashall xml,si术语叫做POX(plain old xml)、这样我们终于可以抛开WS-*和soap.

  soap属于文本协议,随着SOA盛极一时,甚至有二进制协议已死一说,但是当下二进制协议开始回归,如典型二进制协议RSocket、AMQP,和二进制协议SDK Protobuf 以及HTTP2.  微服务间交互的service Mesh抛弃文本协议重归二进制协议只是时间问题。SOA、soap的兴盛和衰退都有其原因,文本确实有安全、可见、跨平台、方便调试和扩展的优点,SOA时代对于多厂家的异构系统集成来说,soap确实是最省事儿的一站式交互协议,但是期望的系统大集成时代没有到来,没有市场和应用,这些技术自然持续衰败,最终技术圈失去耐心,推到重来,转向推微服务。

最典型的二进制协议回归是HTTP从1.1文本协议转到二进制协议HTTP2

  Protocol交互协议分为应用层schema/encoding(消息格式如json/xml、AMQP、HTTP)和传输层transport(如TCP/UDP)两方面。一个协议是文本协议还是二进制协议看它在应用层面向的是文本还是数据结构:

1、rest:transport是HTTP;schema随意一般用json(属于schemaless半格式化数据、json不须要类似xml的xsd这种schema),在简单场景下,可以不做序列化直接用官方参考实现JSON-java处理字符串KV映射,也就是说文本协议本质上没有结构,没有结构也就对交互双方没有约束,调用服务之间算是松耦合。

2、soap:transport随意一般是HTTP、只要是跨平台协议都可以如SMTP、MIME;schema固定是xml,典型的文本协议,但采用的文本格式是严格的xml,所以调用服务之间是紧耦合。

3、gRPC:transport是HTTP2;schema固定是Protobuf,属于开放二进制协议。AMQP与之类似,都是vender neutral开放的,目标是天然跨平台:AMQP is the Internet Protocol for Business Messaging. 

云总线必须直接跨平台,云更需要可以良好跨平台的交互协议
AMQP可以代替WebService,作为应用间交互的可靠应用层协议......
AMQP位置。要跨平台,transport必须是开放式系统互连协议 (Open System Interconnect ,OSI),可以是TCP或HTTP;schema随意,最好是开放的;

4、POX:transport是HTTP;schema是xml —— 可以替代soap;

  gRPC在非纯文本的大数据量场景可以代替rest、HTTP2+protoBuf代替HTTP1.1+json,单纯的HTTP协议性能并不比TCP差太多,主要是文本的解析性能损耗。跨平台要求Protocol交互协议整体是open Internet Protocol 开放的,包括:

1、transport是开放的传输协议(TCP是一种ad-hoc的开放协议,HTTP当然是全面开放的协议)

2、schema消息格式只要求开放即可,可以是专有实现只要公开,最好为各个异构平台提供SDK.

AMQP与ws的关系

HTTP/2 相比 1.0 有哪些重大改进?

ws-support:SI与Spring ws联合使用

  如果接收到的是soap消息,那么要令Message headers 包含SOAP Action header. 就得使用<int-ws:outbound-gateway...也就是说SI要处理soap ws需要Spring WS的支持,SI在ws方面的feature包括:

  1、POX- and SOAP-based gateways

  2、Simple HTTP-based integration;经测普通http入栈会抛弃soap消息的SOAP Action头部,不过没什么影响;

Hey Ho

  至于Spring ws,需要注意的是它只支持“从上到下”的ws开发,也即Contract First. 这很务实,看看ws的历史明白一下为什么soap坑爹:

  由于TCP是无处不在的,而HTTP是TCP之上考虑相对来说最全面的、应用最广的“应用层”协议,所以用HTTP进行跨系统的ws交互就成了最通用的一种消息交互形式:

ws底层协议HTTP还能自动跨防火墙,更加助长了ws的气焰

  但是实际上并没有对ws的“完全限定”定义,虽然W3C组织定义了soap和wsdl,但是在定义中也有免(甩)责(锅)声明,大意是我们只是定义个意思、工业界具体怎么用我们不管、对于其他定义我们也不置可否...尼玛soap可以看做只是个参考实现而已。在REST和soap之间的争论也表明soap和wsdl绝非ws的唯一选择,而且即使是soap这一边,soap也不是只能以HTTP作为传输协议的。也就是说ws和soap并没有必然联系,作为通用跨系统交互ws形式确实是首选,至于消息格式那你可以自由决定,和ws没关系,soap可以不是ws/HTTP形式的、ws的消息格式也可以不是soap的。oasis的陈述是这样的:A Web Service is a component that is described via WSDL and is capable of being accessed via standard software network protocols such as but not limited to SOAP over HTTP.   但是即使说wsdl自己,它也绝不是一个通用的东西。所以出现了gRPC,依然使用跨平台的Http但是抛弃了xml,固定用proto.

  对标准化ws的尝试在1990年前后十分盛行,大多数大型软件商乐于提供一揽子的工具赚钱、还有在WS-*规范里加上自己的一笔,使得WS-*规范几乎无所不包,这种情况可称之为:add-ons 重在掺和...这是囚徒困境,导致soap ws臃肿不堪,复杂且无用,使得很多人开始寻找可替代的、轻量级方案,其中之一就是REST。综上所述:SOAP迅速演变成了一场商业闹剧。

EJB、SOA,多少曾经的权威一地鸡毛

  起初ws被认为是一种RPC,之后人们发现ws和RPC的很多差异,ws更像消息,而xml则是数据的平台中立表达,是SOA构想的“世界语”。

  实际上传输协议和消息格式本来就是无耦合的,基于Spring WS的SI继续遵循了这个最佳实践,其实在SI之前已经有了基于Spring WS和JMS协议、邮件协议以及XMPP(Extensible Messaging and Presence Protocol)的ws.

  SI首推POX,soap也支持。SI的纯HTTP的通道适配器一般是用于非xml的或者是REST的消息。无论是POX还是soap,对xml消息的处理都少不了,这方面建议用Spring OXM module.

  OXM模块起初是作为Spring WS子项目的一部分而开发,到spring3.0,OXM成为了spring core框架的一个模块,主要的俩接口:

package org.springframework.oxm;

public interface Marshaller {

  boolean supports(Class<?> clazz);

  void marshal(Object graph, Result result) throws IOException, XmlMappingException;

}

public interface Unmarshaller {

  boolean supports(Class<?> clazz);

  Object unmarshal(Source source) throws IOException, XmlMappingException;

}

  如果你的场景仅仅是想做JavaBean和XML节点元素的互相转换,而不涉及动态XML的处理——说人话就是直接手撸(un)mashall,那么JAXB绝对是一个不错的选择。在比较新的jdk版本中,JAXB都是jdk的扩展包javax中自带的类库,不需要你引入第三方jar包。如果是已经有了wsdl+xsd的从上到下方式,可以用JDK的对象生成工具:

jdk1.8.0_151\bin>xjc -d C:\...\javaO -p ch.iec -readOnly -wsdl C:\...\*.wsdl -encoding UTF-8

  从wsdl生成代码包ch.iec到javaO目录下。JAXB可以与OXM结合使用,其他场景比如是先有代码的从下至上方式,可以对业务对象类加JAXB注释,注意从下至上方式是不好的。@XmlJavaTypeAdapter可以做一些非JDK标准类型到标准类型的转换:

@XmlElement

@XmlJavaTypeAdapter(JodaDateTimeAdapter.class)

private DateTime startOfLegDate;

  javax.xml.bind.annotation.adapters.XmlAdapter实现:

public class JodaDateTimeAdapter extends XmlAdapter<Calendar, DateTime> {

  @Override

  public DateTime unmarshal(Calendar cal) throws Exception {

    return new DateTime(cal.getTime(), ISOChronology.getInstanceUTC());

  }

  @Override

  public Calendar marshal(DateTime dt) throws Exception {

    GregorianCalendar cal = new GregorianCalendar();

    cal.setTimeInMillis(dt.getMillis());

    cal.setTimeZone(TimeZone.getTimeZone("UTC"));

    return cal;

  }

}

  SI基于OXM做了一个很薄的适配器层封装,开箱即用,配置一个Jaxb2Marshaller:

<beans:bean id="legMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">

  <beans:property name="classesToBeBound" value="siia.booking.domain.trip.LegQuoteCommand"/>

  </beans:bean>

  配置中使用引入si-xml  namespace:

  http://www.springframework.org/schema/integration/xml

  http://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd

  在两个通道之间做转换:

<channel id="javaLegQuoteCommands"/> //输入通道

<si-xml:marshalling-transformer input-channel="javaLegQuoteCommands" output-channel="xmlLegQuotes" marshaller="legMarshaller" //si的转换器需要一个marshaller

result-transformer="resultToDocumentTransformer"/>

<beans:bean id="resultToDocumentTransformer" class="org.springframework.integration.xml.transformer.ResultToDocumentTransformer"/>

<channel id="xmlLegQuotes"/> //输出通道、输出payLoad为Result的消息。对javax.xml.transform.Result能做的只是get/set system ID,用于错误消息或者relate to a file on the local filesystem.

POX

  POX也可以称之为XML web service with Spring ws.  从Spring WS开始就提倡并支持:可以解耦应用代码和复杂的soap处理,因为soap里头的xm和命名空间l处理很烦人。Spring WS已经做了编程模型上的简化:把spring配置与简单的基于接口/注释的端点相结合,SI继承了这种简单模型,并添加了简化的消息模型、做了EIP的实现。

  按照EIP实现ws,可以做诸如对消息的split和聚合,你可以只用Spring WS做到,但再加上SI可以省略不少工作,这就是为什么SI要基于Spring WS提供ws支持。要对外暴露纯正的ws服务,可以用Spring WS的入栈网关,支持POX及soap,配合使用SI的int-ws:inbound-gateway可以将ws请求导入SI消息通道。也可以只用纯HTTP组件,对soap自己做marshal。用Spring WS得配置MessageDispatcherServlet(也可以不配的,它可以将ws请求引入si消息管道),它带有getWsdlDefinition、getXsdSchema方法。该servlet专用于简化ws消息的分发,是对springMVC DispatcherServlet的替代。在Spring-WS中可以基于xsd和一些转换动态生成wsdl。也可以不使用DispatcherServlet,直接配置UriEndpointMapping,不过url path都固定为services.

  集成处理一般不需要理解消息内容,那是所集成的业务系统的事,但有时需要对内容做特定处理比如基于内容的路由、消息内容增强之类,这类简单处理可以用XPath或者Extensible Stylesheet Language Transformation (XSLT). 如果是更复杂的处理须要将xml转换为业务对象的,可以用object-to-XML mapping (OXM) 做marshalling and unmarshalling.

  spring OXM做了一层技术无关的java与xml映射的通用抽象、通过这个通用抽象层将代码与实现细节解耦,并提供了一致的异常处理层,这种解耦是通过封装Marshaller的对象到xml映射以及Unmarshaller映射实现的:

package org.springframework.oxm;

public interface Marshaller {

  boolean supports(Class<?> clazz);

  void marshal(Object graph, Result result) throws IOException, XmlMappingException;

}

package org.springframework.oxm;

public interface Unmarshaller {

  boolean supports(Class<?> clazz);

  Object unmarshal(Source source) throws IOException, XmlMappingException;

}

spring OXM作为spring WS项目的一部分开始开发、到spring3.0并入了核心spring framework.

  可以结合使用Java Architecture for XML Binding (JAXB) v2 与 spring OXM. 你可以使用JAXB注释去注释业务类而不是从xsd生成业务类,比如使用javax.xml.bind.annotation.XmlRootElement注释声明文档根,像这样:

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "legQuote")

public class LegQuoteCommand implements Command {

  private Leg leg;

  private HotelCriteria hotelCriteria;

  private FlightCriteria flightCriteria;  .......

    还可以使用XmlJavaTypeAdapter注释去适配一些非JDK类:

@XmlRootElement

public class Leg {

  @XmlElement

  @XmlJavaTypeAdapter(JodaDateTimeAdapter.class)

  private DateTime startOfLegDate;  .....

  JodaDateTimeAdapter 是这样的:

public class JodaDateTimeAdapter extends XmlAdapter<Calendar, DateTime> {

  @Override

  public DateTime unmarshal(Calendar cal) throws Exception {

    return new DateTime(cal.getTime(), ISOChronology.getInstanceUTC());

  }

  @Override

  public Calendar marshal(DateTime dt) throws Exception {

    GregorianCalendar cal = new GregorianCalendar();

    cal.setTimeInMillis(dt.getMillis());

    cal.setTimeZone(TimeZone.getTimeZone("UTC"));

  return cal;

  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,283评论 0 10
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 某些人昨天上班的状态一定是这样的 或者这样的 或者这样的 熬夜看球,终于累成狗~~~ 今天做一款汤,给球迷们补补....
    非狒狒肥阅读 539评论 2 8
  • 今天阅读的章节是——如何阅读科学和数学类的书? ·四个收获(你从中得到了什么?) 一:学习数学可以像学习语言一样,...
    绿肥红瘦judy阅读 154评论 0 0
  • 课程结束后,我的伙伴跟我说,你不是想问我吗?你问吧,你还想问什么?我说我就是想知道你作为我的钱的时候,你是什么感觉...
    喜悦唐喀吽阅读 203评论 0 0