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表示错误码