高性能 RESTful 框架 EasyRest-NAS

Apache
Java
跨平台
2018-05-02
Louie_L

EasyRest-NAS

English doc

EasyRest 与 Netty, Akka 和 Spring 的整合.

  • 这是一个为快速开发而设计的高性能RESTful框架,极易搭建集群和使用分布式。你可以完全专注在你的业务逻辑上。

  • 不需要 Tomcat,不需要 web.xml 配置,只需一个有 main 函数的 jar 包,你就能拥有一个完美的分布式系统。

  • 你可以不知道 Netty,也可以不知道 akka,甚至不熟悉 Spring,但仍然可以使用该框架。

快速开始:

  • REST接口定义

@BindURL("/rest/{TENANT}/stock")
public interface StockInfoRest {

    @Post("/personal/{USER_ID}/favorite/{CODE}")
    void addFavorite(String TENANT, String USER_ID, String CODE, long time);

    @Post
    ResponseEntity addStocks(int userNumber, String userName, List<Stock> stockList);

    @Get("/personal/{USER_ID}/favorite/list")
    List<Stock> getStockList(String USER_ID);

}
  • 使用@Service注解,将该类交给Spring生成bean并管理,该框架可以和spring无缝对接使用。

@Service
public class StockInfoRestController implements StockInfoRest {

    @Override
    public void addFavorite(String TENANT, String USER_ID, String CODE, long time) {
        System.out.println(TENANT + " " + USER_ID + " " + CODE + " " + time);
    }

    @Override
	@AllDefined
    public ResponseEntity addStocks(int userNumber, String userName, List<Stock> stockList) {
        return ResponseEntity.buildOkResponse(Lists.asList(userNumber, userName, new List[]{stockList}));
    }

    @Override
    public List<Stock> getStockList(String USER_ID) {
        return Lists.newArrayList(new Stock(100000, "stock1"), new Stock(100001, "stock2"), new Stock(100002, "stock3"));
    }
}
  • 主函数类,用于启动以及配置。

public class Example {

    public static void main(String[] args) {
        EasyRest easyRest = new EasyRest("classpath:MyExampleApplicationContext.xml");
        easyRest.startup("EasyRestServer");
    }

}
  • 一个基础的spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <context:component-scan base-package="com.example">
    </context:component-scan>
    <context:annotation-config/>
</beans>
  • @BindURL("/rest/{TENANT}/stock") 会绑定该类监听 "/rest/{TENANT}/stock" 路径的请求。

  • @AllDefined 会要求该方法所有的参数在请求中都被赋予非空值,如果检测到有null,框架将会直接拒绝这个请求。可以减少用户的空值判断。

  • @Service 这个是spring的annotation,将交给spring生成bean并管理。

  • ResponseEntity 是一个通用的返回格式,你能将所有格式的数据放进去。(你也可以不使用这个,直接返回任何你想返回的格式)

  • 如果你有很多其他的spring配置文件,你可以这样启动EasyRest:

EasyRest easyRest = new EasyRest("classpath:MyApplicationContext-01.xml", "classpath:MyApplicationContext-02.xml"...);
  • 所有你想暴露的数据接口,EasyRest都会自动检测到,你只需要启动server。

easyRest.startup("EasyRestServer");

接口调用示例

  • 函数 1

@Post("/personal/{USER_ID}/favorite/{CODE}")
void addFavorite(String TENANT, String USER_ID, String CODE, long time);

调用地址:

http://127.0.0.1:8080/rest/100000001/stock/personal/001/favorite/100001

Content-Type is 'application/json'

请求内容:

{"time":1524827542}

控制台输出:

100000001 001 100001 1524827542

收到的响应内容:

{
    "code": "1",
    "message": "ok"
}
  • 函数 2

@Post
@AllDefined
ResponseEntity addStocks(int userNumber, String userName, List<Stock> stockList);

调用地址:

http://127.0.0.1:8080/rest/100000001/stock/addStocks

Content-Type is 'application/json'

请求内容:

{"userNumber":1, "userName":"Louie", "stockList":[{"code":100001, "name":"stock1"}, {"code":100002, "name":"stock2"}]}

响应内容:

{
    "code": "1",
    "data": [
        1,
        "Louie",
        [
            {
                "code": 100001,
                "name": "stock1"
            },
            {
                "code": 100002,
                "name": "stock2"
            }
        ]
    ]
}

这个函数有一个 @AllDefined 的注解,所以如果任何参数的值为null,比如:&ldquo;UserName&rdquo;,那么响应结果将会如下:

{
    "code": "-1",
    "message": "Failed",
    "data": {
        "errorType": "ParameterNotFoundException",
        "errorMessage": "userName is not defined."
    }
}
  • 函数 3

@Get("/personal/{USER_ID}/favorite/list")
List<Stock> getStockList(String USER_ID);

调用地址:

http://127.0.0.1:8080/rest/100000001/stock/personal/001/favorite/list

响应内容:

[
    {
        "code": 100000,
        "name": "stock1"
    },
    {
        "code": 100001,
        "name": "stock2"
    },
    {
        "code": 100002,
        "name": "stock3"
    }
]
  • 对于 content type, 'multipart/form-data' 也是支持的.

  • 框架支持分布式服务,并且十分容易搭建.

分布式服务示例

所有的代码都在 Example 的模块中

代码结构

- Example-Distributed-Service-1

	- example-service-1-api
	
	- example-service-1-main
	

- Example-Distributed-Service-2

	- example-service-2-api
	
	- example-service-2-main


- Example-Distributed-Service-Model

Example-Distributed-Service-1 会收到请求,然后会调用 Example-Distributed-Service-2 的服务去创建一个 People,然后将这个 People 做为响应数据返回出去。

Example-Distributed-Service-Model

  • People 类

public class People {

    private String name;
    private int age;
    private long birthday;
    private List<String> skills;
    private People boss;

    public People(String name, int age, long birthday, List<String> skills, People boss) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
        this.skills = skills;
        this.boss = boss;
    }

}

Example-Distributed-Service-1

example-service-1-api

  • 接口定义

@BindURL("/service1")
public interface Service1 {

    @Post
    @AllDefined
    ResponseEntity createPeople(String name, int age, long birthday, List<String> skills, People boss);

}

example-service-1-main

  • 接口实现

@Service
public class Service1Impl implements Service1 {

    @Override
    public ResponseEntity createPeople(String name, int age, long birthday, List<String> skills, People boss) {
        Service2 service2 = EasyRestServiceLookup.lookup(Service2.class);
        return ResponseEntity.buildOkResponse(service2.getPeople(name, age, birthday, skills, boss));
    }

}

EasyRestServiceLookup 有一个静态方法 lookup. 你能使用这个函数获得任何交给 EasyRest,或者spring 的bean实例,包括在其他服务器上的实例,你都能直接调用。

  • 主函数

public class Startup {

	private static String systemName = "example-service-1";

    public static void main(String[] args) throws IOException {
        EasyRestDistributedServiceBind.loadConfiguration(Startup.class.getClassLoader().getResourceAsStream("services-mapping-01.json"));
        EasyRest easyRest = new EasyRest("classpath:MyExampleApplicationContext-01.xml");
        easyRest.startup(systemName, new NettyInit(8001));
    }

}

EasyRestDistributedServiceBind.loadConfiguration(Startup.class.getClassLoader().getResourceAsStream("services-mapping-01.json")); 将会为框架载入服务映射的关系配置文件。

  • akka 配置文件: application.conf

akka {
  	actor {
    	provider = "akka.remote.RemoteActorRefProvider"
  	}
    remote {
        transport = "akka.remote.netty.NettyRemoteTransport"
        netty {
            tcp {
                hostname = "127.0.0.1"
                port = 2551
            }
        }
    }
}

Akka 系统会检测到这个配置文件,然后在指定的端口监听远程请求。

  • 分布式服务映射关系表:(services-mapping-01.json)

{
  "self": {
    "akkaSystemName": "example-service-1",
    "host": "127.0.0.1",
    "port": "2551"
  },
  "services" : [
    {
      "akkaSystemName": "example-service-1",
      "host": "127.0.0.1",
      "port": "2551"
    },
    {
      "akkaSystemName": "example-service-2",
      "host": "127.0.0.1",
      "port": "2552"
    }
  ]
}

服务映射关系表只需要2个字段: Self 记录本地的服务器信息. Services 是一个数组,记录所有的服务器信息,包括自己本身.

字段 akkaSystemName 的值必须和主函数中 systemName 的值一致!!!

*一个基本的spring配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <context:component-scan base-package="com.example">
    </context:component-scan>
    <context:annotation-config/>
</beans>

Example-Distributed-Service-2

example-service-2-api

  • 接口定义

@BindURL("/service2")
public interface Service2 {

    @Get
    People getPeople(String name, int age, long birthday, List<String> skills, People boss);

}

example-service-2-main

  • 接口实现

@Service
public class Service2Impl implements Service2 {

    @Override
    public People getPeople(String name, int age, long birthday, List<String> skills, People boss) {
        return new People(name, age, birthday, skills, boss);
    }
}
  • 主函数

public class Startup {

	private static String systemName = "example-service-2";

    public static void main(String[] args) throws IOException {
        EasyRestDistributedServiceBind.loadConfiguration(Startup.class.getClassLoader().getResourceAsStream("services-mapping-02.json"));
        EasyRest easyRest = new EasyRest("classpath:MyExampleApplicationContext-02.xml");
        easyRest.startup(systemName, new NettyInit(8002));
    }

}
  • akka 配置文件: application.conf

akka {
  	actor {
    	provider = "akka.remote.RemoteActorRefProvider"
  	}
    remote {
        transport = "akka.remote.netty.NettyRemoteTransport"
        netty {
            tcp {
                hostname = "127.0.0.1"
                port = 2552
            }
        }
    }
}
  • 分布式服务映射表:(services-mapping-02.json,该service并未依赖其他service,所以其实可以不用加载该配置文件)

{
  "self": {
    "akkaSystemName": "example-service-2",
    "host": "127.0.0.1",
    "port": "2552"
  },
  "services" : [
    {
      "akkaSystemName": "example-service-1",
      "host": "127.0.0.1",
      "port": "2551"
    },
    {
      "akkaSystemName": "example-service-2",
      "host": "127.0.0.1",
      "port": "2552"
    }
  ]
}
  • 一个基本的spring配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <context:component-scan base-package="com.example">
    </context:component-scan>
    <context:annotation-config/>
</beans>

启动 Service 1 和 Service 2.

当你在两边的控制台分别看到如下的日志:

[example-service-1-akka.actor.default-dispatcher-5] INFO com.easyrest.utils.LogUtils - From com.easyrest.actors.remote.RemoteServiceExchangeActor: Service mapping init success.
[example-service-1-akka.actor.default-dispatcher-5] INFO com.easyrest.utils.LogUtils - example-service-2 is running on the port 8001.
[example-service-2-akka.actor.default-dispatcher-3] INFO com.easyrest.utils.LogUtils - From com.easyrest.actors.remote.RemoteServiceExchangeActor: Service mapping init success.
[example-service-2-akka.actor.default-dispatcher-3] INFO com.easyrest.utils.LogUtils - example-service-2 is running on the port 8002.

这表示两个service现在已经就绪了!

现在我们将通过rest call调用service1.

http://127.0.0.1:8001/service1/createPeople Content-Type:application/json Body: {"name":"Louie", "age":18, "birthday":763401600, "skills":["java", "netty", "akka", "spring"], "boss":{"name":"Louie_B", "age":18, "birthday":763401600}}

收到的响应内容:

{
    "code": "1",
    "data": {
        "name": "Louie",
        "age": 18,
        "birthday": 763401600,
        "skills": [
            "java",
            "netty",
            "akka",
            "spring"
        ],
        "boss": {
            "name": "Louie_B",
            "age": 18,
            "birthday": 763401600
        }
    }
}

That's work!

持续更新...

的码云指数为
超过 的项目
加载中

评论(1)

Louie_L
Louie_L 软件作者
希望大家能给一点建议~~ 谢谢~~

暂无资讯

暂无问答

OpenAPI接口的发布,文档,测试----Swagger+Dubbo Rest+Easyrest

背景 对外开放的接口,需要清晰的接口文档,方便客户端进行测试,目前restful风格的接口定义是最好理解,调用和测试的接口风格;服务提供端也需要一种简单的办法,把已有的服务接口发布为res...

2018/10/23 15:31
901
0
NAS实现类型对比:统一式、网关式和横向扩展式(Scale-out)

NAS主要有三种类型的实现:统一式、网关式和横向扩展式(Scale-out)。统一NAS使用统一的存储平台将基于NAS和基于SAN的数据访问合并,提供了可以同时管理二种环境的统一管理界面。网关NAS使用...

2014/11/24 14:45
48
0
为什么企业NAS终将被企业网盘取代?最大的原因有3点

企业NAS和企业网盘都属于企业文档的存储、共享工具。虽然企业NAS的发展历史远比企业网盘悠久,但长江后浪推前浪,企业NAS的风头正在被企业网盘赶超。 在企业云存储这件事上,为什么更多的用户...

01/18 16:45
8
0
企业私有云存储选NAS还是私有云盘?3分钟带你了解企业NAS和企业云盘的区别

曾经,那些喜欢鼓捣小电影的技术男都喜欢自己在家里搞一台NAS,在充分体验家庭私有云的乐趣之后,他们把NAS带到了企业,以方便同事间分享文件。 但他们忽略了一个基本的问题,跟家庭环境不同...

01/14 11:02
58
0
阿里云文件存储 NAS 使用教程

课程链接:阿里云文件存储 NAS 使用教程——阿里云大学 阿里云文件存储(Network Attached Storage,简称 NAS)是面向阿里云 ECS 实例、HPC 和 Docker 等计算节点的文件存储服务,提供标准的...

04/26 16:22
0
0
如何更改VNX Storage Processsor和Control Station管理地址

转载请在文首保留原文出处:EMC中文支持论坛 本文将列出更改VNX Storage Processsor 和Control Station管理IP地址之前的准备工作和详细步骤。

2014/11/26 10:21
40
0
阿里云文件存储 NAS 使用教程

阿里云文件存储(Network Attached Storage,简称 NAS)是面向阿里云 ECS 实例、HPC 和 Docker 等计算节点的文件存储服务,提供标准的文件访问协议,您无需对现有应用做任何修改,即可使用具...

2018/09/06 13:57
16
0
2015年最新关于DAS、NAS、SAN网络存储结构的区别与发展 | 燕麦企业云盘(OATOS)

数据爆炸时代,云存储已逐渐成为企业关注的焦点。今日获悉DAS、NAS、SAN这几个关于网络存储结构方面的专业术语,着实让人摸不着头脑。本文中燕麦企业云盘(OATOS企业网盘)将谈谈对于企业,哪...

2015/04/15 17:02
15
0
阿里云文件存储 NAS 使用教程

阿里云文件存储(Network Attached Storage,简称 NAS)是面向阿里云 ECS 实例、HPC 和 Docker 等计算节点的文件存储服务,提供标准的文件访问协议,您无需对现有应用做任何修改,即可使用具...

01/07 10:14
3
0
自己动手搭建NAS(一)|简介

NAS已经进入了很多家庭,在这个数据爆发的时代成为了很重要的设备。 相信很多人都曾遇到过手机存储空间已满、忘带U盘之类的状况。有了NAS,就可以随时随地上传下载文件,将文件安全地存在自己...

2018/08/16 02:36
399
0

没有更多内容

加载失败,请刷新页面

返回顶部
顶部