实现基于netty的web框架,了解一下
上一篇写了,基于netty实现的rpc的微框架,其中详细介绍netty的原理及组件,这篇就不过多介绍
这篇实现基于netty的web框架,你说netty强不强,文中有不对的地方,欢迎大牛指正
先普及几个知识点
@sharable
标注一个channel handler可以被多个channel安全地共享。
ChannelHandlerAdapter还提供了实用方法isSharable()。如果其对应的实现被标注为Sharable,那么这个方法将返回true,
表示它可以被添加到多个ChannelPipeline中。 因为一个ChannelHandler可以从属于多个ChannelPipeline,所以它也可以绑定到多个ChannelHandlerContext实例。
用于这种用法的ChannelHandler必须要使用@Sharable注解标注;否则,试图将它添加到多个ChannelPipeline时将会触发异常。
显而易见,为了安全地被用于多个并发的Channel(即连接),这样的ChannelHandler必须是线程安全的。
AtomicInteger:这个类的存在是为了满足在高并发的情况下,原生的整形数值自增线程不安全的问题,在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。AtomicInteger为什么能够达到多而不乱,处理高并发应付自如呢?
这是由硬件提供原子操作指令实现的,这里面用到了一种并发技术:CAS。在非激烈竞争的情况下,开销更小,速度更快
TimeUnit:
TimeUnit是Java.util.concurrent包下面的一个类。它提供了两大功能:
1)提供了可读性更好的线程暂停操作,通常用来替换Thread.sleep();
2)提供了便捷方法用于把时间转换成不同单位,如把秒转换成毫秒;
TimeUnit.MINUTES.sleep(4); // sleeping for 4 minutes Thread.sleep(4*60*1000);
项目的目录结构
上代码,分享一些关键的代码,后续的giuhub上的demo的注释很详细
//Netty 事件回调类
@Sharable
public class MessageCollector extends ChannelInboundHandlerAdapter {
private final static Logger LOG = LoggerFactory.getLogger(MessageCollector.class);
//业务线程池
private ThreadPoolExecutor[] executors;
private RequestDispatch requestDispatch;
//业务队列最大值
private int requestsMaxInflight=1000; public MessageCollector(int workerThreads,RequestDispatch dispatch){
//给业务线程命名
ThreadFactory factory =new ThreadFactory() {
AtomicInteger seq=new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
Thread thread =new Thread(r);
thread.setName("http-"+seq.getAndIncrement());
return thread;
}
};
this.executors=new ThreadPoolExecutor[workerThreads];
for(int i=0;i<workerThreads;i++){
ArrayBlockingQueue queue=new ArrayBlockingQueue<Runnable>(requestsMaxInflight);
////闲置时间超过30秒的线程就自动销毁
this.executors[i]=new ThreadPoolExecutor(1,1,
30, TimeUnit.SECONDS, queue,factory,new CallerRunsPolicy());
} this.requestDispatch=dispatch;
} public void closeGracefully(){
//优雅一点关闭,先通知,再等待,最后强制关闭
for (int i=0;i<executors.length;i++){
ThreadPoolExecutor executor=executors[i];
try {
executor.awaitTermination(10,TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
executor.shutdownNow();
}
} @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//客户端来了一个新的连接
LOG.info("connection comes {}",ctx.channel().remoteAddress());
} @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//客户端走了一个
LOG.info("connection leaves {}",ctx.channel().remoteAddress());
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest){
FullHttpRequest req= (FullHttpRequest) msg;
CRC32 crc32=new CRC32();
crc32.update(ctx.hashCode());
int idx =(int) (crc32.getValue()%executors.length);
//用业务线程处理消息
this.executors[idx].execute(() ->{
requestDispatch.dispatch(ctx,req);
});
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//此处可能因为客户机器突发重启
//也可能客户端连接时间超时,后面的REadTimeoutHandle抛出异常
//也可能消息协议错误,序列化异常
ctx.close();
}
}
HttpServer
public class HttpServer {
private final static Logger LOG= LoggerFactory.getLogger(HttpServer.class);
private String ip;
private int port; //端口
private int ioThreads; //IO线程数,用于处理套接字读写,由Netty内部管理
private int workerThreads; //业务线程数,专门处理http请求,由我们本省框架管理
private RequestDispatch requestDispatch;//请求配发器对象 public HttpServer() {
} public HttpServer(String ip, int port, int ioThreads,
int workerThreads, RequestDispatch requestDispatch) {
this.ip = ip;
this.port = port;
this.ioThreads = ioThreads;
this.workerThreads = workerThreads;
this.requestDispatch = requestDispatch;
}
//用于服务端,使用一个ServerChannel接收客户端的连接,
// 并创建对应的子Channel
private ServerBootstrap bootstrap;
//包含多个EventLoop
private EventLoopGroup group;
//代表一个Socket连接
private Channel serverChannel;
//
private MessageCollector collector; public void start(){
bootstrap=new ServerBootstrap();
group=new NioEventLoopGroup(ioThreads);
bootstrap.group(group);
collector=new MessageCollector(workerThreads,requestDispatch);
bootstrap.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline=socketChannel.pipeline();
//如果客户端60秒没有任何请求,就关闭客户端连接
pipeline.addLast(new ReadTimeoutHandler(10));
//客户端和服务器简单的编解码器:HttpClientCodec和HttpServerCodec。
//ChannelPipelien中有解码器和编码器(或编解码器)后就可以操作不同的HttpObject消息了;但是HTTP请求和响应可以有很多消息数据,
// 你需要处理不同的部分,可能也需要聚合这些消息数据
pipeline.addLast(new HttpServerCodec());
//通过HttpObjectAggregator,Netty可以聚合HTTP消息,
// 使用FullHttpResponse和FullHttpRequest到ChannelPipeline中的下一个ChannelHandler,这就消除了断裂消息,保证了消息的完整
pipeline.addLast(new HttpObjectAggregator(1 << 30)); // max_size = 1g
//允许通过处理ChunkedInput来写大的数据块
pipeline.addLast(new ChunkedWriteHandler());
//将业务处理器放到最后
pipeline.addLast(collector);
}
});
} public void stop() {
// 先关闭服务端套件字
serverChannel.close();
// 再斩断消息来源,停止io线程池
group.shutdownGracefully();
// 最后停止业务线程
collector.closeGracefully();
} }
RequestDispatcherImpl 是请求派发器,用于将收到的HTTP请求对象扔给响应的RequestHandler进行处理。
public class RequestDispatcherImpl implements RequestDispatch {
private final static Logger LOG = LoggerFactory.getLogger(RequestDispatcherImpl.class); private String contextRoot;
private Router router;
private Map<Integer, WebExceptionHandler> exceptionHandlers = new HashMap<>();
private WebExceptionHandler defaultExceptionHandler = new DefaultExceptionHandler(); private WebTemplateEngine templateEngine = new WebTemplateEngine() {
}; static class DefaultExceptionHandler implements WebExceptionHandler { @Override
public void handle(ApplicationContext ctx, AbortException e) {
if (e.getStatus().code() == ) {
LOG.error("Internal Server Error", e);
}
ctx.error(e.getContent(), e.getStatus().code());
} } public RequestDispatcherImpl(Router router) {
this("/", router);
} public RequestDispatcherImpl(String contextRoot, Router router) {
this.contextRoot = CurrentUtil.normalize(contextRoot);
this.router = router;
} public RequestDispatcherImpl templateRoot(String templateRoot) {
this.templateEngine = new FreemarkerEngine(templateRoot);
return this;
} public String root() {
return contextRoot;
} public RequestDispatcherImpl exception(int code, WebExceptionHandler handler) {
this.exceptionHandlers.put(code, handler);
return this;
} public RequestDispatcherImpl exception(WebExceptionHandler handler) {
this.defaultExceptionHandler = handler;
return this;
}
@Override
public void dispatch(ChannelHandlerContext channelCtx, FullHttpRequest req) {
ApplicationContext ctx = new ApplicationContext(channelCtx, contextRoot, templateEngine);
try {
this.handleImpl(ctx, new Request(req));
} catch (AbortException e) {
this.handleException(ctx, e);
} catch (Exception e) {
this.handleException(ctx, new AbortException(HttpResponseStatus.INTERNAL_SERVER_ERROR, e));
} finally {
req.release();
}
} private void handleException(ApplicationContext ctx, AbortException e) {
WebExceptionHandler handler = this.exceptionHandlers.getOrDefault(e.getStatus().code(), defaultExceptionHandler);
try {
handler.handle(ctx, e);
} catch (Exception ex) {
this.defaultExceptionHandler.handle(ctx, new AbortException(HttpResponseStatus.INTERNAL_SERVER_ERROR, ex));
}
} private void handleImpl(ApplicationContext ctx, Request req) throws Exception {
if (req.decoderResult().isFailure()) {
ctx.abort(, "http protocol decode failed");
}
if (req.relativeUri().contains("./") || req.relativeUri().contains(".\\")) {
ctx.abort(, "unsecure url not allowed");
}
if (!req.relativeUri().startsWith(contextRoot)) {
throw new AbortException(HttpResponseStatus.NOT_FOUND);
}
req.popRootUri(contextRoot);
router.handle(ctx, req);
}
}
项目github位置
https://github.com/developerxiaofeng/WebFrameByNetty.git
实现基于netty的web框架,了解一下的更多相关文章
- 这样基于Netty重构RPC框架你不可能知道
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365天原创计划”第5天. 今天呢!灯塔君跟大家讲: 基于Netty重构RPC框架 一.CyclicBarrier方法说明 1. ...
- DIY一些基于netty的开源框架
几款基于netty的开源框架,有益于对netty的理解和学习! 基于netty的http server框架 https://github.com/TogetherOS/cicada 基于netty的即 ...
- Netty高性能web框架
框架背景: 前期为公司项目做全链路压测,发现公司跑到tomcat上的服务,即使是最简单的方法QPS也就到3000左右,后期查询发现可能和tomcat的业务逻辑有关. 因为以前在项目开发中用netty做 ...
- 基于 CSS 的 Web 框架 CJSS
CJSS 是一个基于 CSS 的 Web 框架,所有效果都在 CSS 文件中生效,可以在 CSS 中使用它添加更多功能,或者构建一个完整的页面. 使用方法: HTML 想要使用某个组件,在 CSS 文 ...
- 基于Netty重构RPC框架
下面的这张图,大概很多小伙伴都见到过,这是Dubbo 官网中的一张图描述了项目架构的演进过程.随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在 ...
- 《Java 编写基于 Netty 的 RPC 框架》
一 简单概念 RPC: ( Remote Procedure Call),远程调用过程,是通过网络调用远程计算机的进程中某个方法,从而获取到想要的数据,过程如同调用本地的方法一样. 阻塞IO :当阻塞 ...
- java编写基于netty的RPC框架
一 简单概念 RPC:(Remote Procedure Call),远程调用过程,是通过网络调用远程计算机的进程中某个方法,从而获取到想要的数据,过程如同调用本地的方法一样. 阻塞IO:当阻塞I/O ...
- 基于netty实现rpc框架-spring boot服务端
demo地址 https://gitee.com/syher/grave-netty RPC介绍 首先了解一下RPC:远程过程调用.简单点说就是本地应用可以调用远程服务器的接口.那么通过什么方式调用远 ...
- Django视频教程 - 基于Python的Web框架(全13集)
Django是由Python驱动的开源模型-视图-控制器(MVC)风格的Web应用程序框架,使用Django可以在即可分钟内快速开发一个高品质易维护数据库驱动的应用程序.下面是一大坨关于Django应 ...
随机推荐
- Scrapy项目 - 数据简析 - 实现豆瓣 Top250 电影信息爬取的爬虫设计
一.数据分析截图(weka数据分析截图 ) 本例实验,使用Weka 3.7对豆瓣电影网页上所罗列的上映电影信息,如:标题.主要信息(年份.国家.类型)和评分等的信息进行数据分析,Weka 3.7数据分 ...
- Spring 梳理 - 开启并配置 Spring MVC 的方法
传统web.xm中配置两个上下文+两个context对应的xml+两个上下文bean分别手动配置 传统web.xm中配置两个上下文+两个context对应的xml+<mvc:annotation ...
- java8 两个时间比较
比如在15:30:30之前: LocalTime.now().isBefore(LocalTime.of(15, 30,30)) 或15:30:30之后 LocalTime.now().isAfter ...
- 2017CCSP-01 五子棋 简单模拟
题面和数据 对于每个空格,先定义一个变量cnt=0,表示空格填充后对五子数量的变化,四个方向进行两边搜索,设对于每个方向连续的白棋子分别为\(wa\),\(wb\),有以下几种情况. \(wa> ...
- request.getPathInfo()的作用
项目中使用该方法来获取URL,但不知道获取的URL的起始: 如:http://localhost:8080/web_hrs/Action/这里可以是任何东西 ---->request.getP ...
- Http协议Content-Length详解
前言 http协议是互联网中最重要的协议之一,虽然看上去很简单,但是实际中经常遇到问题,我们就已经遇到好几次了.有长连接相关的,有报文解析相关的.对http协议不能一知半解,必须透彻理解才行.本文通过 ...
- session,cookie,sessionStorage,localStorage的相关设置以及获取删除
一.cookie 什么是 Cookie? "cookie 是存储于访问者的计算机中的变量.每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie.你可以使用 JavaScrip ...
- 寻找子串位置<codevs>
KMP板子题; 如果不会可以参考其他算法书 代码: #include<iostream> #include<stdio.h> #include<stdlib.h> ...
- Java自动化测试框架-02 - TestNG之理论实践 - 纸上得来终觉浅,绝知此事要躬行(详细教程)
理论 TestNG,即Testing, NextGeneration,下一代测试技术,是一套根据JUnit 和NUnit思想而构建的利用注释来强化测试功能的一个测试框架,即可以用来做单元测试,也可以用 ...
- 数据存储检索之B+树和LSM-Tree
作为一名应用系统开发人员,为什么要关注数据内部的存储和检索呢?首先,你不太可能从头开始实现一套自己的存储引擎,往往需要从众多现有的存储引擎中选择一个适合自己应用的存储引擎.因此,为了针对你特定的工作负 ...