Rayson API 框架分析系列之2:API服务开发

本系列之1: 简介
本系列之2: API服务开发👈
本系列之3: RSON序列化格式
本系列之4: RPC调度原理
本系列之5: NIO实现原理
本系列之6: 客户端动态代理原理
本系列之7: 注解处理器(APT)原理


本文介绍使用 Rayson框架来进行基于API(Application-Programming-Interface)的服务的开发过程,并用几个例子来进行说明。

基础概念

API协议

Rayson直接使用Java接口来定义API(Application-Programming-Interface),Rayson框架把它的API接口定义称为协议(Protocol)
以下是一个协议定义的例子:

package org.rayson.demo.simple.api;
@Service
public interface SimpleProtocol extends Protocol {
    public String echo(String msg) throws IOException, RpcException;
    public byte[] echo2(byte[] msg) throws IOException, RpcException;
}

说明:

  • 这个JAVA Interface定义了一个名字叫org.rayson.demo.simple.api.SimpleProtocol的协议。
  • 接口(Interface)必须扩展(extends)接口org.rayson.api.Protocol,这种限定主要是为了明确该Interface是一种Rayson协议。
  • 接口(Interface)必须使用@org.rayson.api.annotation.Service进行注解,这种限定主要是为了使得Rayson的APT(Annotation Processing Tool)工具能够对该接口(Interface)进行处理,生成对应的API元数据(MetaData),参考 本系列之7: 注解处理器(APT)原理
  • 本协议定义了两个对外API,分别是echo(String)echo2(byte[]),对应相应JAVA接口(Interface)的两个方法(Method)。
  • 方法(Method)的参数和返回值必须是可序列化的,关于Rayson支持的可序列化类型,请参考本系列之3: RSON序列化格式
  • 每个方法(Method)必须抛出(throws)两个异常:java.io.IOExceptionorg.rayson.api.exception.RpcException。其中,java.io.IOException用于捕获网络层异常;org.rayson.api.exception.RpcException用于捕获业务层的异常。

API服务

在Rayson中,对API协议(一个Java Interface)的实现(Implementation)称为API服务(Service)。
下面是一个API服务的例子:

public class SimpleService implements SimpleProtocol {
    @Override
    public String echo(final String msg) throws RpcException {
        return msg;
    }
    @Override
    public byte[] echo2(byte[] msg) throws RpcException {
        return msg;
    }
}

可见,API服务在形式上实际上是一个实现了API协议所对应的Java接口的Java实体类,在这个类上实现了API协议所定义的业务逻辑。下文会通过具体的例子说明如何初始化服务并且把服务注册到Rayson服务器中去。

过滤器(Filter)

由于API服务是通过实现协议中所定义的Java方法(Method)来实现API的业务逻辑的,然而在实际的API服务开发中,仅仅靠传递Java方法的参数是不能满足通用的需求的。例如下文JWT实现的示例中,就难以在处理API请求过程中仅仅通过方法参数来处理JWT认证。

为此,Rayson框架提供一种称为过滤器(Filter)的组件,为在处理API请求中执行额外的操作提供机制。

  • 过滤器的基础接口是org.rayson.api.Filter
  • Rayson框架的服务器端和客户端皆支持过滤器。
  • 多个同类型的过滤器可以链接起来形成过滤器链。
  • 过滤器链是一种责任链(Chain of Responsibility)模式的实现,每种过滤器类型均有对应的过滤器链接口。
  • 通常,一个过滤器有一个类似如下的方法接口:
public void doFilter(Request request, Response response, FilterChain chain);
  • 开发者可以在过滤器实现中对RPC过滤逻辑进行链式的处理。

总共有两类过滤器:

  • HTTP 协议层面的过滤器。这类过滤器主要在HTTP协议层面对API的请求和响应过程中执行过滤操作。它又分为两种:
    • org.rayson.api.server.HttpServerFilter 服务器端HTTP协议层面的过滤器。
    • org.rayson.api.client.HttpClientFilter 客户端HTTP协议层面的过滤器。
  • Java 对象层面的过滤器。这类过滤器主要在Java对象层面对API的请求和响应过程中执行过滤操作。它又分为两种:
    • org.rayson.api.server.ServiceFilter 服务器端Java对象层面的过滤器。
    • org.rayson.api.client.ClientFilter 客户端Java对象层面的过滤器。

下文同样会通过具体的例子说明如何在开发中使用过滤器。

RPC上下文

在Rayson服务器端,上文所述的过滤器可以拦截RPC请求执行额外的业务逻辑,但是却无法把中间处理结果传递给API服务。为此,Rayson提供称为 RPC上下文的工具用于给滤器传递中间结果给API服务。

  • RPC上下文的接口为org.rayson.api.server.RpcContext,详细接口请参考源代码
  • 开发者可以通过设置RpcContext的属性的方法setAttribute(String name, Object value)来给API服务传递数据。
  • API服务的实现中,开发者可以通过RpcContext获取属性的方法getAttribute(String name)来获取过滤器传递过来的数据。
  • 具体代码示例请参考本文高级示例部分。

开发示例

这部分通过具体的例子介绍如何使用Rayson框架开发API服务。
总共有三个示例:

  • Hello World 示例介绍如何开发一个最简单的服务。
  • JWT 示例介绍如何开发一个支持JWT认证的服务。
  • 高级示例介绍如何开发一个较为复杂的服务。

每个例子一般分为4个部分:

  • 协议,描述API协议的定义。
  • 服务,描述API服务的实现代码。
  • 服务器端,描述服务器端的处理代码。
  • 客户端,描述客户端的处理代码。

对于例子的代码,如果是自明的一般不再做说明。
这些例子的源代码均可以在rayson.demo项目中找到。

Hello World 示例

  • 协议
@Service
public interface SimpleProtocol extends Protocol {
    public String echo(String msg) throws IOException, RpcException;
    public byte[] echo2(byte[] msg) throws IOException, RpcException;
}
  • 服务
public class SimpleService implements SimpleProtocol {
    @Override
    public String echo(final String msg) throws IOException {
        return msg;
    }
    @Override
    public byte[] echo2(byte[] msg) throws IOException, RpcException {
        return msg;
    }
}
  • 服务器端
public static void main(final String[] args) throws Exception {
    final RaysonServer server = new RaysonServer(8080);
    server.registerService(new SimpleService());
    server.start();
}

代码说明:

  1. 在8080端口启动一个Rayson服务器实例。
  2. 初始化服务SimpleService的一个实例。
  3. 注册服务实例到服务器实例。
  4. 启动服务器。
  • 客户端
public static void main(final String[] args) throws Exception {
    final RaysonServerAddress serverAddr = new RaysonServerAddress("localhost", 8080);
    SimpleProtocol simpleProtocol = Rayson.createProxy(serverAddr, SimpleProtocol.class);
    try {
        String echoMsg = simpleProtocol.echo("Hello World");
        System.out.println(echoMsg);
    } catch (IOException e) {
        System.err.println("Network error occurred");
    } catch (RpcException e) {
        System.err.println("Invoking RPC got logic error: error_code: " + e.getCode() + " error_message: " + e.getMessage());
    }
}

代码说明:

  1. 使用Rayson客户端org.rayson.client.Rayson,以服务器地址和协议接口(Interface)作为参数创建一个动态代理。
  2. 执行动态代理的方法#echo(String),传入“Hello World”。
  3. 动态代理执行RPC请求并返回执行结果“Hello World”。

关于动态代理,请参考本系列之6: 客户端动态代理原理

JWT 示例

本示例介绍如何使用Rayson框架实现一个支持JWT认证的服务。为了在请求中进行认证流程的处理,本例子使用了过滤器。

  • 协议
@Service
public interface JwtProtocol extends Protocol {
    public String echo(String msg) throws IOException, RpcException;
}
  • 服务
public class JwtService implements JwtProtocol {
    @Override
    public String echo(final String msg) throws IOException {
        return msg;
    }
}
  • 服务器端

首先,定义一个HTTP协议层的过滤器:

public class ServerAuthHttpFilter implements HttpServerFilter {
    @Override
    public void doFilter(HttpServerRequest request, HttpServerResponse response, HttpFilterChain chain) {
        // 从HTTP请求中提取JWT Token,并进行verify。详细代码请查看demo项目源代码。
    }
    //省略部分代码
}

其次,启动服务器:

public static void main(final String[] args) throws Exception {
    final RaysonServer server = new RaysonServer(8080);
    server.registerService(new JwtService());
    server.addHttpFilter(ServerAuthHttpFilter.class);
    server.start();
}

注意,server.addHttpFilter(ServerAuthHttpFilter.class);这行代码把以上HTTP过滤器添加到服务器实例中去。

  • 客户端

首先,定义一个HTTP协议层的过滤器:

public class ClientAuthHttpFilter implements HttpClientFilter {
    @Override
    public void doFilter(HttpClientRequest request, HttpClientResponse response, HttpFilterChain chain) throws ClientFilterException {
        // 往HTTP请求中添加JWT Token。详细代码请查看demo项目源代码。
    }
    //省略部分代码
}

其次,启动客户端:

public static void main(final String[] args) throws Exception {
    final RaysonServerAddress serverAddr = new RaysonServerAddress("localhost", 8080);
    ProxyConfig config = new ProxyConfig();
    config.addHttpFilter(ClientAuthHttpFilter.class);
    JwtProtocol testProtocol;
    testProtocol = Rayson.createProxy(serverAddr, JwtProtocol.class, config);
    System.out.println(testProtocol.echo("Hello World"));
}

注意,config.addHttpFilter(ClientAuthHttpFilter.class);这行代码把以上HTTP过滤器添加到客户端动态代理实例中的配置中去。

这样,在这个API服务例子中,客户端和服务器端都会在请求协议JwtProtocol所定义的服务的时候,执行JWT认证的操作流程。在服务器端,如果一个请求不满足JWT认证的要求,就会被过滤器拒绝并返回异常响应给客户端。
详细代码请查看rayson.demo项目源代码。

高级示例

本示例介绍如何使用Rayson框架的特性实现一个较为高级和复杂的API服务,在这个例子中,演示了如下特性:

  1. 使用复杂可序列化对象作为API参数。
  2. 使用多个过滤器。
  3. 使用注解配置API服务。
  4. 使用了RPC上下文来传递RPC请求的用户信息。
  • 协议
@Service
public interface AdvancedProtocol extends Protocol {
    public String echo(String msg) throws IOException, RpcException;
    public SerialObject echo2(SerialObject msg) throws IOException, RpcException;
}

其中,SerialObject是复杂的可序列化对象,关于Rayson支持的可序列化对象,请参考本系列之3: RSON序列化格式

  • 服务
@ServiceConfig(filters = { ServerLogFilter.class })
public class AdvancedService implements AdvancedProtocol {
    @Override
    public String echo(final String msg) throws IOException {
        String userId = (String) RpcContext.getContext().getAttribute("userId");
        // Ignore get user information
        return msg;
    }
    @Override
    public SerialObject echo2(SerialObject msg) throws IOException, RpcException {
        return msg;
    }
}

注意代码String userId = (String) RpcContext.getContext().getAttribute("userId");从RPC上下文获取了从下文中的过滤器ServerAuthHttpFilter传递过来的userId的属性。
另外,注解@org.rayson.api.annotation.ServiceConfig用于配置API服务,上面的例子表示服务配置了一个过滤器ServerLogFilter。这个过滤器在服务器端对服务请求进行日志记录,具体代码从略,请参考源代码。

  • 服务器端

    首先,定义两个过滤器:

    • 一个Java对象层面的过滤器,就是上文提到的ServerLogFilter。这个过滤器通过注解的方式被配置到服务中去。
    • 一个HTTP协议层面的过滤器ServerAuthHttpFilter,这个过滤器负责从客户端的HTTP请求中校验权限。

ServerAuthHttpFilter的代码如下:

public class ServerAuthHttpFilter implements HttpServerFilter {
    @Override
    public void doFilter(HttpServerRequest request, HttpServerResponse response, HttpFilterChain chain) {
        System.out.println("Recived HTTP request " + request);
        HttpHeader header = request.getHeader("apiKey");
        // Get user id from apiKey header.
        String userId = getUserId(header);
        RpcContext.getContext().setAttribute("userId", userId);
        chain.doFilter(request, response);
    }
    // 忽略部分代码
}

注意代码RpcContext.getContext().setAttribute("userId", userId);设置了RPC上下文的userId的属性。

其次,启动服务器:

public static void main(final String[] args) throws Exception {
    final RaysonServer server = new RaysonServer(8080);
    server.registerService(new AdvancedService());
    server.addHttpFilter(ServerAuthHttpFilter.class);
    server.start();
}

注意,server.addHttpFilter(ServerAuthHttpFilter.class);这行代码把以上HTTP过滤器添加到服务器实例中去。

  • 客户端

首先,定义两个过滤器:

  1. 一个Java对象层面的过滤器ClientLogFilter。这个过滤器在客户端对服务请求进行日志记录,具体代码从略,请参考源代码。
  2. 一个HTTP协议层面的过滤器ClientAuthHttpFilter,这个过滤器负责把认证信息添加到HTTP请求中。具体代码从略,请参考源代码。

其次,启动客户端:

public static void main(final String[] args) throws Exception {
    final RaysonServerAddress serverAddr = new RaysonServerAddress("localhost", 8080);
    ProxyConfig config = new ProxyConfig();
    config.addFilter(ClientLogFilter.class);
    config.addHttpFilter(ClientAuthHttpFilter.class);

    AdvancedProtocol testProtocol;
    testProtocol = Rayson.createProxy(serverAddr, AdvancedProtocol.class, config);
    System.out.println(testProtocol.echo("Hello World"));

    SerialObject msg = new SerialObject();
    msg.setIntField(12345);
    msg.setStrField("12345");

    SerialObject response = testProtocol.echo2(msg);
    System.out.println(response);
    if (!response.equals(msg)) {
        System.err.println("Message " + msg + " not equals to the response " + response);
    }
}

注意,config.addFilter(ClientLogFilter.class);config.addHttpFilter(ClientAuthHttpFilter.class);这两行代码把以上HTTP过滤器添加到客户端动态代理实例中的配置中去。


The End

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值