实现基于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应 ...
随机推荐
- Codeforces Numbers 题解
这题只需要会10转P进制就行了. PS:答案需要约分,可以直接用c++自带函数__gcd(x,y). 洛谷网址 Codeforces网址 Code(C++): #include<bits/std ...
- JQuery对于动态生成的标签绑定事件失效
JQuery对整个html文档进行dom操作后,我们要想动态绑定事件,有两种方法 1.在进行dom操作时,在标签中写上onclick="afun()" 2.利用document的操 ...
- npm install 时间很长解决方案
国外镜像站很慢,所以我们可以更换为国内的镜像站 首先可以get命令查看registry npm congfig get registry 如果你没有变更果regustry你的结果应该会是这样的 也就是 ...
- 常见MySQL数据库语句
##############Author: Fan ################# (1)数据库 # 查看所有的数据库 SHOW DATABASES ; # 创建一个数据库 ...
- 有了 CompletableFuture,使得异步编程没有那么难了!
本文导读: 业务需求场景介绍 技术设计方案思考 Future 设计模式实战 CompletableFuture 模式实战 CompletableFuture 生产建议 CompletableFutur ...
- 手把手带你利用Ribbon实现客户端的负载均衡
之前的文章<SpringCloud搭建注册中心与服务注册>介绍了注册中心的搭建和服务的注册,本文将介绍下服务消费者通过Ribbon调用服务实现负载均衡的过程. 本文目录 一.Ribbon服 ...
- Mysql高手系列 - 第22篇:深入理解mysql索引原理,连载中
Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能. 欢迎大家加我微信itsoku一起交流java.算法.数据库相关技术. 这是Mysql系列第22篇. 背景 使用mys ...
- 【百度地图】如何去掉百度LOGO
只需要一步就解决问题,在CSS里加入一段代码即可: .anchorBL{display:none;}
- Web页面解析过程(浅)
web页面流程 域名解析DNS 域名解析:把域名指向网络空间IP,让人们通过简单的域名访问Web网站的服务. DNS:域名系统 DNS服务器:记录着域名及其对应的IP地址 解析域名: 浏览器中输入 ...
- Java BIO及实现
发现日常学习过的知识不久就会遗忘,在此只是整理并记录一下学习笔记,做个回忆,并方便以后查阅,若有错误,欢迎指正 网络模型:TCP/IP网络模型是从OSI七层模型中演化来的,osi模型分为物理层,数据链 ...