分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 软件开发

Netty URL路由方案探讨

发布时间:2023-09-06 02:16责任编辑:顾先生关键词:暂无标签

最近在用Netty做开发,需要提供一个http web server,供调用方调用。采用Netty本身提供的HttpServerCodec handler进行Http协议的解析,但是需要自己提供路由。

最开始是通过对Http method及uri 采用多层if else 嵌套判断的方法路由到真正的controller类:

String uri = request.uri();HttpMethod method = request.method();if (method == HttpMethod.POST) { ???if (uri.startsWith("/login")) { ???????//url参数解析,调用controller的方法 ???} else if (uri.startsWith("/logout")) { ???????//同上 ???}} else if (method == HttpMethod.GET) { ???if (uri.startsWith("/")) { ???} else if (uri.startsWith("/status")) { ???}}

在只需提供loginlogoutAPI时,代码可以完成功能,可是随着API的数量越来越多,需要支持的方法及uri越来越多,else if 越来越多,代码越来越复杂。

在阿里开发手册中也提到过:

因此首先考虑采用状态设计模式及策略设计模式重构。

状态模式

状态模式的角色:

  • state状态
    表示状态,定义了根据不同状态进行不同处理的接口,该接口是那些处理内容依赖于状态的方法集合,对应实例的state类
  • 具体的状态
    实现了state接口,对应daystate和nightstate
  • context
    context持有当前状态的具体状态的实例,此外,他还定义了供外部调用者使用的状态模式的接口。

首先我们知道每个http请求都是由method及uri来唯一标识的,所谓路由就是通过这个唯一标识定位到controller类的中的某个方法。

因此把HttpLabel作为状态

@Data@AllArgsConstructorpublic class HttpLabel { ???private String uri; ???private HttpMethod method;}

状态接口:

public interface Route { ???/** ????* 路由 ????* ????* @param request ????* @return ????*/ ???GeneralResponse call(FullHttpRequest request);}

为每个状态添加状态实现:

public void route() { ???//单例controller类 ???final DemoController demoController = DemoController.getInstance(); ???Map<HttpLabel, Route> map = new HashMap<>(); ???map.put(new HttpLabel("/login", HttpMethod.POST), demoController::login); ???map.put(new HttpLabel("/logout", HttpMethod.POST), demoController::login);}

接到请求,判断状态,调用不同接口:

public class ServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { ???@Override ???public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { ???????String uri = request.uri(); ???????GeneralResponse generalResponse; ???????if (uri.contains("?")) { ???????????uri = uri.substring(0, uri.indexOf("?")); ???????} ???????Route route = map.get(new HttpLabel(uri, request.method())); ???????if (route != null) { ???????????ResponseUtil.response(ctx, request, route.call(request)); ???????} else { ???????????generalResponse = new GeneralResponse(HttpResponseStatus.BAD_REQUEST, "请检查你的请求方法及url", null); ???????????ResponseUtil.response(ctx, request, generalResponse); ???????} ???}}

使用状态设计模式重构代码,在增加url时只需要网map里面put一个值就行了。

类似SpringMVC路由功能

后来看了 JAVA反射+运行时注解实现URL路由 发现反射+注解的方式很优雅,代码也不复杂。

下面介绍Netty使用反射实现URL路由。

路由注解:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface RequestMapping { ???/** ????* 路由的uri ????* ????* @return ????*/ ???String uri(); ???/** ????* 路由的方法 ????* ????* @return ????*/ ???String method();}

扫描classpath下带有@RequestMapping注解的方法,将这个方法放进一个路由Map:Map<HttpLabel, Action<GeneralResponse>> httpRouterAction,key为上面提到过的Http唯一标识 HttpLabel,value为通过反射调用的方法:

@Slf4jpublic class HttpRouter extends ClassLoader { ???private Map<HttpLabel, Action<GeneralResponse>> httpRouterAction = new HashMap<>(); ???private String classpath = this.getClass().getResource("").getPath(); ???private Map<String, Object> controllerBeans = new HashMap<>(); ???@Override ???protected Class<?> findClass(String name) throws ClassNotFoundException { ???????String path = classpath + name.replaceAll("\\.", "/"); ???????byte[] bytes; ???????try (InputStream ins = new FileInputStream(path)) { ???????????try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { ???????????????byte[] buffer = new byte[1024 * 5]; ???????????????int b = 0; ???????????????while ((b = ins.read(buffer)) != -1) { ???????????????????out.write(buffer, 0, b); ???????????????} ???????????????bytes = out.toByteArray(); ???????????} ???????} catch (Exception e) { ???????????throw new ClassNotFoundException(); ???????} ???????return defineClass(name, bytes, 0, bytes.length); ???} ???public void addRouter(String controllerClass) { ???????try { ???????????Class<?> cls = loadClass(controllerClass); ???????????Method[] methods = cls.getDeclaredMethods(); ???????????for (Method invokeMethod : methods) { ???????????????Annotation[] annotations = invokeMethod.getAnnotations(); ???????????????for (Annotation annotation : annotations) { ???????????????????if (annotation.annotationType() == RequestMapping.class) { ???????????????????????RequestMapping requestMapping = (RequestMapping) annotation; ???????????????????????String uri = requestMapping.uri(); ???????????????????????String httpMethod = requestMapping.method().toUpperCase(); ???????????????????????// 保存Bean单例 ???????????????????????if (!controllerBeans.containsKey(cls.getName())) { ???????????????????????????controllerBeans.put(cls.getName(), cls.newInstance()); ???????????????????????} ???????????????????????Action action = new Action(controllerBeans.get(cls.getName()), invokeMethod); ???????????????????????//如果需要FullHttpRequest,就注入FullHttpRequest对象 ???????????????????????Class[] params = invokeMethod.getParameterTypes(); ???????????????????????if (params.length == 1 && params[0] == FullHttpRequest.class) { ???????????????????????????action.setInjectionFullhttprequest(true); ???????????????????????} ???????????????????????// 保存映射关系 ???????????????????????httpRouterAction.put(new HttpLabel(uri, new HttpMethod(httpMethod)), action); ???????????????????} ???????????????} ???????????} ???????} catch (Exception e) { ???????????log.warn("{}", e); ???????} ???} ???public Action getRoute(HttpLabel httpLabel) { ???????return httpRouterAction.get(httpLabel); ???}}

通过反射调用controller 类中的方法:

@Data@RequiredArgsConstructor@Slf4jpublic class Action<T> { ???@NonNull ???private Object object; ???@NonNull ???private Method method; ???private boolean injectionFullhttprequest; ???public T call(Object... args) { ???????try { ???????????return (T) method.invoke(object, args); ???????} catch (IllegalAccessException | InvocationTargetException e) { ???????????log.warn("{}", e); ???????} ???????return null; ???}

ServerHandler.java处理如下:

 //根据不同的请求API做不同的处理(路由分发)Action<GeneralResponse> action = httpRouter.getRoute(new HttpLabel(uri, request.method()));if (action != null) { ???if (action.isInjectionFullhttprequest()) { ???????ResponseUtil.response(ctx, request, action.call(request)); ???} else { ???????ResponseUtil.response(ctx, request, action.call()); ???}} else { ???//错误处理 ???generalResponse = new GeneralResponse(HttpResponseStatus.BAD_REQUEST, "请检查你的请求方法及url", null); ???ResponseUtil.response(ctx, request, generalResponse);}

测试结果如下:

完整代码在 https://github.com/morethink/Netty-Route

Netty URL路由方案探讨

原文地址:https://www.cnblogs.com/morethink/p/9710918.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved