如何发布和引用服务
- 想要构建微服务,首先要解决的问题是,服务提供者如何发布一个服务,服务消费者如何引用这个服务。
- 具体来说,就是这个服务的接口名是什么?
- 调用这个服务需要传递哪些参数?
- 接口的返回值是什么类型?
- 以及一些其他接口描述信息。
RESTful API
- RESTful API 的方式,主要被用作 HTTP 或者 HTTPS 协议的接口定义,即使在非微服务架构体系下,也被广泛采用。
@Path("/rest") public interface RestfulService { @GET @Produces(MediaType.APPLICATION_JSON) List<User> getUsers(@QueryParam("uid") int uid); @GET @Path("/primitive") @Produces(MediaType.TEXT_PLAIN) String testPrimitiveType(); @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.APPLICATION_JSON) Response add(@FormParam("id") int id, @FormParam("name") String name); }
- 服务提供者这一端通过部署代码到 Tomcat 中,并配置 Tomcat 中如下的 web.xml,就可以通过 servlet 的方式对外提供 RESTful API。
<listener> <listener-class>com.weibo.api.motan.protocol.restful.support.servlet.RestfulServlet </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</se <load-on-startup>1</load-on-startup> <init-param> <param-name>resteasy.servlet.mapping.prefix</param-name> <param-value>/servlet</param-value> <!-- 此处实际为 servlet-mapping 的 url-patt </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping>
- 这样服务消费者就可以通过 HTTP 协议调用服务了。
- 因为 HTTP 协议本身是一个公开的协议,对于服务消费者来说几乎没有学习成本,所以比较适合用作跨业务平台之间的服务协议。
- 比如有一个服务,不仅需要在业务部门内部提供服务,还需要向其他业务部门提供服务,甚至开放给外网提供服务,这时候采用 HTTP 协议就比较合适,也省去了沟通服务协议的成本。
XML 配置
- XML 配置方式的服务发布和引用主要分三个步骤:
- 服务提供者定义接口,并实现接口。
- 服务提供者进程启动时,通过加载 server.xml 配置文件将接口暴露出去。
- 服务消费者进程启动时,通过加载 client.xml 配置文件来引入要调用的接口。
- 通过在服务提供者和服务消费者之间维持一份对等的 XML 配置文件,来保证服务消费者按照服务提供者的约定来进行服务调用。
- 在这种方式下,如果服务提供者变更了接口定义,不仅需要更新服务提供者加载的接口描述文件 server.xml,还需要同时更新服务消费者加载的接口描述文件 client.xml。
- 一般是私有 RPC 框架会选择 XML 配置这种方式来描述接口,因为私有 RPC 协议的性能要比 HTTP 协议高,所以在对性能要求比较高的场景下,采用 XML 配置的方式比较合适。
- 但这种方式对业务代码侵入性比较高,XML 配置有变更的时候,服务消费者和服务提供者都要更新,所以适合公司内部联系比较紧密的业务之间采用。
- 如果要应用到跨部门之间的业务调用,一旦有 XML 配置变更,需要花费大量精力去协调不同部门做升级工作。
IDL 文件
- IDL 就是接口描述语言(interface description language)的缩写,通过一种中立的方式来描述接口,使得在不同的平台上运行的对象和不同语言编写的程序可以相互通信交流。
- IDL 主要是用作跨语言平台的服务之间的调用,有两种最常用的 IDL:一个是 Facebook 开源的 Thrift 协议,另一个是 Google 开源的 gRPC 协议。
- gRPC 协议使用 Protobuf 简称 proto 文件来定义接口名、调用参数以及返回值类型。
- 假如服务提供者使用的是 Java 语言,那么利用 protoc 插件即可自动生成 Server 端的 Java 代码。
- 假如服务消费者使用的也是 Java 语言,那么利用 protoc 插件即可自动生成 Client 端的 Java 代码。
- 假如服务消费者使用的是 PHP 语言,那么利用 protoc 插件即可自动生成 Client 端的 PHP 代码。
- gRPC 协议的服务描述是通过 proto 文件来定义接口的,然后再使用 protoc 来生成不同语言平台的客户端和服务端代码,从而具备跨语言服务调用能力。
- 在描述接口定义时,IDL 文件需要对接口返回值进行详细定义。
- 如果接口返回值的字段比较多,并且经常变化时,采用 IDL 文件方式的接口定义就不太合适了。
- 一方面可能会造成 IDL 文件过大难以维护,另一方面只要 IDL 文件中定义的接口返回值有变更,都需要同步所有的服务消费者都更新,管理成本就太高了。
- 具体采用哪种服务描述方式是根据实际情况决定的。
- 通常情况下,如果只是企业内部之间的服务调用,并且都是 Java 语言的话,选择 XML 配置方式是最简单的。
- 如果企业内部存在多个服务,并且服务采用的是不同语言平台,建议使用 IDL 文件方式进行描述服务。
- 如果还存在对外开放服务调用的情形的话,使用 RESTful API 方式则更加通用。