Net 组件介绍

-

        Net 组件是基于AIO(NIO.2)的一套TCP/UDP的网络框架,且只提供异步接口。org.redkale.net 是所有网络协议服务的基础包。RedKale内置HTTP和远程模式Service依赖的SNCP(Service Node Communicate Protocol)协议的实现包。RedKale启动的<server>节点服务都是基于Net组件实现的协议服务。

+

        Net 组件是基于AIO(NIO.2)的一套TCP/UDP的网络框架,且只提供异步接口。org.redkale.net 是所有网络协议服务的基础包。RedKale内置HTTP和远程模式Service依赖的SNCP(Service Node Communicate Protocol)协议的实现包。RedKale启动的<server>节点服务都是基于Net组件实现的协议。下面详细介绍 HTTP服务SNCP协议

HTTP 服务

        RedKale自实现的HTTP服务接口并不遵循Java EE规范JSR 340(Servlet 3.1),RedKale提倡的是HTTP+JSON的服务接口方式因此没有实现JSP规范,HTTP+JSON服务接口几乎适合所有类型的客户端(PC应用程序、PC Web、微信H5、移动APP、移动Web)开发。其与JSR 340(Servlet 3.1)的主要区别如下:
@@ -38,7 +38,109 @@                 7、内置WebSocket的集群与组功能,且提供伪WebSocket连接功能。
                8、HttpResponse只能异步输出。

-

        HttpRequest 方法如下:

+

        编写RedKale的HttpServlet与 JSR 340中的javax.servlet.http.HttpServlet 基本相同,只需继承 org.redkale.net.http.HttpServlet, Redkale也提供了更友好的基类 org.redkale.net.http.BasedHttpServlet, 比较好的习惯是一个项目先定义一个项目级的BaseServlet类,这样方便以后加入类似javax.servlet.Filter的功能。

+         一个典型的BaseSerlvet实现: +

+
public class BaseSerlvet extends org.redkale.net.http.BasedHttpServlet {
+
+    protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
+
+    protected final boolean fine = logger.isLoggable(Level.FINE);
+
+    @Resource //[RedKale内置资源]  进程的启动时间
+    protected long serverCreateTime;
+
+    @Resource //[RedKale内置资源]
+    protected JsonConvert jsonConvert;
+
+    @Resource //[RedKale内置资源]
+    protected JsonFactory jsonFactory;
+
+    //[RedKale内置资源], 当前进程的根目录,字段类型可以是 String、java.io.File、java.nio.file.Path
+    @Resource(name = "APP_HOME")
+    protected File home;
+
+    //[RedKale内置资源], 当前Http Server的web页面的根目录,字段类型可以是 String、java.io.File、java.nio.file.Path
+    @Resource(name = "SERVER_ROOT")
+    protected File webroot;
+
+    @Resource
+    private UserService service;
+
+    //在调用authenticate之前调用, 返回false表示请求不合法
+    //该方法可以用于判断请求源是否合法或加入一些全局的拦截操作
+    @Override
+    public boolean preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
+        if (!request.getHeader("User-Agent", "").contains("RedKale-Agent")) {  //只用移动APP的接口可以判断User-Agent是否正确
+            response.addHeader("retcode", "10001");
+            response.addHeader("retmessage", "User-Agent error");
+            response.setStatus(201);
+            response.finish("{'success':false, 'message':'User-Agent error, must be RedKale-Agent'}");
+            return false;
+        }
+        //可以加上一些统计操作
+        if (fine) response.setRecycleListener((req, resp) -> {  //记录处理时间太长的请求操作
+                long e = System.currentTimeMillis() - request.getCreatetime();
+                if (e > 500) logger.fine("耗时居然用了 " + e + " 毫秒. 请求为: " + request);
+            });
+        return true;
+    }
+
+    //方法标记为@AuthIgnore 的将不会调用authenticate方法
+    //一般用于判断用户的登录态, 返回false表示鉴权失败
+    //moduleid值来自 @WebServlet.moduleid()  用于定义模块ID; actionid值自来@WebAction.actionid() 用于定义操作ID; 需要系统化的鉴权需要定义这两个值
+    @Override
+    public boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException {
+        UserInfo user = (UserInfo) request.getAttribute("_current_userinfo");
+        if (user != null) return true;  //已经判断过了
+        String sessionid = request.getSessionid(false);
+        if (sessionid == null) return false; //没有sessionid表示没有登录
+        user = service.current(sessionid);
+        if (user != null) request.setAttribute("_current_userinfo", user);
+        return user != null; //存在用户表示登录态正常
+    }
+}
+

        继承BasedHttpServlet的子类可以使用其自带的鉴权、请求分支、缓存等功能, 一个典型的操作用户的HttpServlet: +

+
@WebServlet({"/user/*"}) //拦截所有 /user/ 开头的请求
+public class UserServlet extends BaseSerlvet {
+
+    @Resource
+    private UserService service;
+
+    //登录操作 
+    @AuthIgnore  //登录操作不要判断登录态,所以需要标记为@AuthIgnore,不会调用 BaseSerlvet.authenticate 方法
+    //因为WebAction的判断规则用的是String.startsWith,所以WebAction.url不能用正则表达式,只能是getRequestURI的前缀
+    //且同一个HttpServlet类内的所有WebAction不能存在包含关系, 如 /user/myinfo 和 /user/myinforecord 不能存在同一HttpServlet中。
+    @WebAction(url = "/user/login")
+    public void login(HttpRequest req, HttpResponse resp) throws IOException {
+        LoginBean bean = req.getJsonParameter(LoginBean.class, "bean"); //获取参数
+        RetResult<UserInfo> result = service.login(bean); //登录操作, service内部判断bean的合法性
+        resp.finishJson(result); //输出结果
+    }
+
+    //获取当前用户信息
+    //未登录的请求会被BaseSerlvet.authenticate方法拦截,因此能进入该方法说明用户态存在
+    @WebAction(url = "/user/myinfo")
+    public void myinfo(HttpRequest req, HttpResponse resp) throws IOException {
+        UserInfo user = service.current(req.getSessionid(false));
+        //或者使用 UserInfo user = req.getAttribute("_current_userinfo"); 因为BaseSerlvet.authenticate方法已经将UserInfo注入到_current_userinfo属性中
+        resp.finishJson(user);  //输出用户信息
+    }
+
+    //获取指定用户ID的用户信息, 请求如: /user/username/43565443
+    @AuthIgnore
+    // 默认缓存时间是15秒,BasedHttpServlet会将每个进入该方法的请求的响应结果缓存15秒,缓存命中时不会再进入该方法,过期会清空。
+    // @HttpCacheable 必须配合  @AuthIgnore 使用, 因为跟当前用户有关的请求一般不适合所有用户请求。
+    @HttpCacheable(timeout = 30)  //有效期30秒
+    @WebAction(url = "/user/userinfo/")
+    public void userinfo(HttpRequest req, HttpResponse resp) throws IOException {
+        UserInfo user = service.findUserInfo(Integer.parseInt(req.getRequstURILastPath()));
+        resp.finishJson(user);  //输出用户信息
+    }
+}
+ +

  . HttpRequest 对象

public class HttpRequest {
 
     //获取请求方法 GET、POST等
@@ -212,7 +314,7 @@
 }
-

        HttpResponse 方法如下:

+

  . HttpResponse 对象

public class HttpResponse {
 
     //设置状态码
@@ -300,7 +402,8 @@
     public void finish(File file) throws IOException;
 }
-

        WebSocket 方法如下:

+ +

  . WebSocket 对象

public abstract class WebSocket {
 
     //发送消息体, 包含二进制/文本  返回结果0表示成功,非0表示错误码