1.Apache Calcite系列(五):数据库驱动实现
2.jetty、tomcat源码解读?
3.每次对jsp的请求都要将jsp转换为servlet吗?
4.Java教程:dubbo源码解析-网络通信
5.ç®è¿°ä¸ä¸Javaä¸çweb容å¨ï¼ä¸¾å 个ä¾åä¹è¡
6.美团动态线程池思路框架(DynamicTp)之动态调整Tomcat、Jetty、Undertow线程池参数篇
Apache Calcite系列(五):数据库驱动实现
Avatica,作为Apache Calcite的子项目,提供了实现JDBC和ODBC标准数据库驱动的pc软件源码能力。通过这个项目,开发者可以构建自定义数据库的Java驱动,或代理非JDBC、ODBC标准的数据库,而无需修改上层服务代码。本文将探讨Avatica的实现原理。
在探讨Avatica实现细节之前,需要了解其架构。Avatica采用典型的RPC架构,分为客户端和服务端两个部分。客户端将JDBC相关操作如建立连接、执行请求等通过RPC协议发送给服务端,服务端执行这些操作并返回结果。Avatica的核心概念有三个:连接、Statement和查询执行。
接下来,我们将通过一个DEMO展示Avatica的使用方法。DEMO中,我们将使用Avatica框架访问MySQL数据库。代码包括客户端测试代码和服务器端代码。客户端首先建立连接,创建Statement,然后执行查询,最后打印结果。
在建立连接的过程中,代码调用Driver的dapper 源码connect方法,实际上由AvaticaFactory创建连接,Avatica的Driver通过此方法创建AvaticaConnection。连接创建时,指定连接驱动、URL、元数据等信息。元数据使用RemoteMeta,它连接实际数据库并代理数据。
客户端发送建立连接请求后,服务端处理逻辑通过Jetty网络服务实现。Jetty收到请求后,将请求交给AbstractAvaticaHandler处理。Handler内部处理流程包括:调用LocalService,LocalService再调用Meta处理连接请求,Meta根据请求内容选择合适的数据库驱动建立连接。
创建Statement的过程与建立连接类似,通过远程请求实现。服务端创建Statement后,返回Statement ID给客户端,客户端通过ID执行后续操作。创建StatementHandle的过程也是远程请求,服务端创建Statement并返回。
执行查询的过程与创建Statement类似,服务端根据Statement ID查找Statement,执行查询并返回结果。
源码解读方面,主要关注几个关键目录:org.apache.calcite.avatica根目录下的JDBC框架代码,包括Connection、Statement、ResultSet等实现;org.apache.calcite.avatica.remote包下的Service定义、请求处理Handler以及RemoteMeta;Server端的帮助源码org.apache.calcite.avatica.jdbc目录定义代理其他数据源的类,如JdbcMeta和JdbcResultSet;org.apache.calcite.avatica.server目录定义服务端Handler和启动服务。
核心类包括消息类Handler、Service接口、Meta接口和Driver类。Handler负责处理网络请求,Service接口处理请求和响应,Meta接口处理JDBC规范操作请求,Driver类提供了实现Avatica框架的Driver。
总结来说,Avatica通过构建RPC架构,实现JDBC和ODBC标准数据库驱动的自定义和代理,简化了数据库访问过程。通过理解和使用Avatica框架,开发者可以灵活地构建和管理数据库驱动,满足不同场景的需求。
jetty、tomcat源码解读?
我们部署Web服务在Tomcat服务器中,探讨了从HTTP请求到springmvc组件中DispatcherServlet的访问路径。Tomcat核心组件详解
在Tomcat体系中,Server组件作为整个服务器的管理核心,包含服务管理、端口监听等功能。每个Service组件则负责接收客户端消息与处理请求,包含多个连接器和一个容器。连接器负责网络连接,容器则用于处理请求与响应。连接器与容器之间通过标准的ServletRequest和ServletResponse进行通信。连接器Connector组件
连接器实现了网络连接和应用层协议处理,设计了EndPoint、Processor和Adapter三个组件,它们之间通过抽象接口交互,生鲜源码封装变化,提高复用性和降低耦合度。ProtocolHandler接口封装了网络通信和应用层协议解析,具体实现类如HttpNioProtocol和AjpNioProtocol对应不同的协议和通信模型。EndPoint
EndPoint作为通信端点,实现Socket通信,是TCP/IP协议的抽象。在具体实现中,如NioEndpoint和Nio2Endpoint,包含Acceptor和SocketProcessor,用于监听连接请求和处理Socket请求,SocketProcessor将请求提交到线程池Executor中。Processor
Processor负责解析应用层协议,如HTTP/AJP,将Socket请求解析为Tomcat Request对象,并通过Adapter提交到容器处理。Adapter
Adapter用于适配Tomcat Request与标准的ServletRequest,将Tomcat Request转换为可由容器处理的ServletRequest,调用容器的Service方法。Tomcat调用DispatcherServlet流程图
在部署了Web服务的Tomcat服务器中,HTTP请求通过连接器到达Processor,进行协议解析,生成Tomcat Request。此请求通过Adapter转换为标准的ServletRequest,传递给容器。容器按照配置加载Web应用,找到DispatcherServlet,启动服务。在DispatcherServlet中,请求流程进一步处理,实现业务逻辑,webgame源码最终生成响应,通过Adapter和Processor返回给客户端。每次对jsp的请求都要将jsp转换为servlet吗?
在处理动态网页请求时,如ASP、ASP.NET、JSP、PHP等,每次客户端对JSP的请求确实需要将其转换为Servlet。这是因为,JSP本质上是一种模板引擎,用于生成动态网页内容。它的源代码首先会被JSP引擎编译为Servlet,即一个Java类,这个过程发生在服务器端。Servlet作为Java的Web应用组件,能够执行Java代码,处理客户端请求并生成响应结果。因此,为了使JSP能够运行服务器端代码并生成动态网页内容,其源代码必须先转换为Servlet。
当用户请求一个JSP页面时,Web服务器(如Tomcat、Jetty等)接收到请求后,会调用JSP引擎来处理该请求。JSP引擎首先解析JSP页面的HTML和脚本元素,然后将这些元素转换为一个Java类,这个过程即编译阶段。在编译过程中,JSP引擎会检查JSP页面中是否存在脚本元素,并将它们转换为Java代码。然后,这个Java类会被JVM解释执行,生成动态内容,并最终以HTML格式返回给客户端浏览器。
简而言之,每次对JSP的请求都要将其转换为Servlet,这是因为JSP本身不具备直接执行服务器端代码的能力。通过将JSP源代码转换为Servlet,Web服务器能够执行Java代码,处理动态请求并生成响应内容。这一过程确保了动态网页能够根据用户请求生成个性化、动态的网页内容,从而实现丰富的Web应用功能。
Java教程:dubbo源码解析-网络通信
在之前的内容中,我们探讨了消费者端服务发现与提供者端服务暴露的相关内容,同时了解到消费者端通过内置的负载均衡算法获取合适的调用invoker进行远程调用。接下来,我们聚焦于远程调用过程,即网络通信的细节。
网络通信位于Remoting模块中,支持多种通信协议,包括但不限于:dubbo协议、rmi协议、hessian协议、ty进行网络通讯,NettyClient.doOpen()方法中可以看到Netty的相关类。序列化接口包括但不限于:Serialization接口、Hessian2Serialization接口、Kryo接口、FST接口等。
序列化方式如Kryo和FST,性能往往优于hessian2,能够显著提高序列化性能。这些高效Java序列化方式的引入,可以优化Dubbo的序列化过程。
在配置Dubbo RPC时,引入Kryo和FST非常简单,只需在RPC的XML配置中添加相应的属性即可。
关于服务消费方发送请求,Dubbo框架定义了私有的RPC协议,消息头和消息体分别用于存储元信息和具体调用消息。消息头包括魔数、数据包类型、消息体长度等。消息体包含调用消息,如方法名称、参数列表等。请求编码和解码过程涉及编解码器的使用,编码过程包括消息头的写入、序列化数据的存储以及长度的写入。解码过程则涉及消息头的读取、序列化数据的解析以及调用方法名、参数等信息的提取。
提供方接收请求后,服务调用过程包含请求解码、调用服务以及返回结果。解码过程在NettyHandler中完成,通过ChannelEventRunnable和DecodeHandler进一步处理请求。服务调用完成后,通过Invoker的invoke方法调用服务逻辑。响应数据的编码与请求数据编码过程类似,涉及数据包的构造与发送。
服务消费方接收调用结果后,首先进行响应数据解码,获得Response对象,并传递给下一个处理器NettyHandler。处理后,响应数据被派发到线程池中,此过程与服务提供方接收请求的过程类似。
在异步通信场景中,Dubbo在通信层面为异步操作,通信线程不会等待结果返回。默认情况下,RPC调用被视为同步操作。Dubbo通过CompletableFuture实现了异步转同步操作,通过设置异步返回结果并使用CompletableFuture的get()方法等待完成。
对于异步多线程数据一致性问题,Dubbo使用编号将响应对象与Future对象关联,确保每个响应对象被正确传递到相应的Future对象。通过在创建Future时传入Request对象,可以获取调用编号并建立映射关系。线程池中的线程根据Response对象中的调用编号找到对应的Future对象,将响应结果设置到Future对象中,供用户线程获取。
为了检测Client端与Server端的连通性,Dubbo采用双向心跳机制。HeaderExchangeClient初始化时,开启两个定时任务:发送心跳请求和处理重连与断连。心跳检测定时任务HeartbeatTimerTask确保连接空闲时向对端发送心跳包,而ReconnectTimerTask则负责检测连接状态,当判定为超时后,客户端选择重连,服务端采取断开连接的措施。
ç®è¿°ä¸ä¸Javaä¸çweb容å¨ï¼ä¸¾å 个ä¾åä¹è¡
ç®åå¸åºä¸å¸¸ç¨çå¼æºJava Web容å¨æTomcatãResinåJettyãå ¶ä¸Resinä»V3.0åéè¦è´ä¹°æè½ç¨äºåä¸ç®çï¼èå ¶ä»ä¸¤ç§åæ¯çº¯å¼æºçãå¯ä»¥åå«ä»ä»ä»¬çç½ç«ä¸ä¸è½½ææ°çäºè¿å¶å åæºä»£ç ã
ä½ä¸ºWeb容å¨ï¼éè¦æ¿åè¾é«ç访é®éï¼è½å¤åæ¶ååºä¸åç¨æ·ç请æ±ï¼è½å¤å¨æ¶å£ç¯å¢ä¸ä¿æè¾é«ç稳å®æ§åå¥å£®æ§ãå¨HTTPæå¡å¨é¢åï¼Apache HTTPDçæçæ¯æé«çï¼ä¹æ¯æ为稳å®çï¼ä½å®åªè½å¤çéæ页é¢ç请æ±ï¼å¦æéè¦æ¯æå¨æ页é¢è¯·æ±ï¼åå¿ é¡»å®è£ ç¸åºçæ件ï¼æ¯å¦mod_perlå¯ä»¥å¤çPerlèæ¬ï¼mod_pythonå¯ä»¥å¤çPythonèæ¬ã
ä¸é¢ä»ç»çä¸ä¸Web容å¨ï¼é½æ¯ä½¿ç¨Javaç¼åçHTTPæå¡å¨ï¼å½ç¶ä»ä»¬é½å¯ä»¥åµå°Apacheä¸ä½¿ç¨ï¼ä¹å¯ä»¥ç¬ç«ä½¿ç¨ãåæå®ä»¬å¤ç客æ·è¯·æ±çæ¹æ³æå©äºäºè§£Javaå¤çº¿ç¨å线ç¨æ± çå®ç°æ¹æ³ï¼ä¸ºè®¾è®¡å¼ºå¤§çå¤çº¿ç¨æå¡å¨æ好åºç¡ã
Tomcatæ¯ä½¿ç¨æ广çJava Web容å¨ï¼åè½å¼ºå¤§ï¼å¯æ©å±æ§å¼ºãææ°çæ¬çTomcatï¼5.5.ï¼ä¸ºäºæé«ååºé度åæçï¼ä½¿ç¨äºApache Portable Runtimeï¼APRï¼ä½ä¸ºæåºå±ï¼ä½¿ç¨äºAPRä¸å å«Socketãç¼å²æ± çå¤ç§ææ¯ï¼æ§è½ä¹æé«äºãAPRä¹æ¯Apache HTTPDçæåºå±ãå¯æ³èç¥ï¼åå±äºASFï¼Apache Software Foundationï¼ä¸çæåï¼äºè¡¥äºç¨çæ åµè¿æ¯å¾å¤çï¼è½ç¶ä½¿ç¨äºä¸åçå¼åè¯è¨ã
Tomcat ç线ç¨æ± ä½äºtomcat-util.jaræ件ä¸ï¼å å«äºä¸¤ç§çº¿ç¨æ± æ¹æ¡ãæ¹æ¡ä¸ï¼ä½¿ç¨APRçPoolææ¯ï¼ä½¿ç¨äºJNIï¼æ¹æ¡äºï¼ä½¿ç¨Javaå®ç°çThreadPoolãè¿éä»ç»çæ¯ç¬¬äºç§ãå¦ææ³äºè§£APRçPoolææ¯ï¼å¯ä»¥æ¥çAPRçæºä»£ç ã
ThreadPoolé»è®¤å建äº5个线ç¨ï¼ä¿åå¨ä¸ä¸ªç»´ç线ç¨æ°ç»ä¸ï¼å建æ¶å°±å¯å¨äºè¿äºçº¿ç¨ï¼å½ç¶å¨æ²¡æ请æ±æ¶ï¼å®ä»¬é½å¤çâçå¾ âç¶æï¼å ¶å®å°±æ¯ä¸ä¸ªwhile循ç¯ï¼ä¸åççå¾ notifyï¼ãå¦ææ请æ±æ¶ï¼ç©ºé²çº¿ç¨ä¼è¢«å¤éæ§è¡ç¨æ·ç请æ±ã
å ·ä½ç请æ±è¿ç¨æ¯ï¼ æå¡å¯å¨æ¶ï¼å建ä¸ä¸ªä¸ç»´çº¿ç¨æ°ç»ï¼maxThreadï¼ä¸ªï¼ï¼å¹¶å建空é²çº¿ç¨(minSpareThreadsï¼5个)éæ¶çå¾ ç¨æ·è¯·æ±ã å½æç¨æ·è¯·æ±æ¶ï¼è°ç¨ threadpool.runIt(ThreadPoolRunnable)æ¹æ³ï¼å°ä¸ä¸ªéè¦æ§è¡çå®ä¾ä¼ ç»ThreadPoolä¸ãå ¶ä¸ç¨æ·éè¦æ§è¡çå®ä¾å¿ é¡»å®ç°ThreadPoolRunnableæ¥å£ã ThreadPoolé¦å æ¥æ¾ç©ºé²ç线ç¨ï¼å¦ææåç¨å®è¿è¡è¦æ§è¡ThreadPoolRunnableï¼å¦æ没æ空é²çº¿ç¨å¹¶ä¸æ²¡æè¶ è¿maxThreadsï¼å°±ä¸æ¬¡æ§å建minSpareThreads个空é²çº¿ç¨ï¼å¦æå·²ç»è¶ è¿äºmaxThreadsäºï¼å°±çå¾ ç©ºé²çº¿ç¨äºãæ»ä¹ï¼è¦æ¾å°ç©ºé²ç线ç¨ï¼ä»¥ä¾¿ç¨å®æ§è¡å®ä¾ãæ¾å°åï¼å°è¯¥çº¿ç¨ä»çº¿ç¨æ°ç»ä¸ç§»èµ°ã æ¥çå¤éå·²ç»æ¾å°ç空é²çº¿ç¨ï¼ç¨å®è¿è¡æ§è¡å®ä¾ï¼ThreadPoolRunnableï¼ã è¿è¡å®ThreadPoolRunnableåï¼å°±å°è¯¥çº¿ç¨éæ°æ¾å°çº¿ç¨æ°ç»ä¸ï¼ä½ä¸ºç©ºé²çº¿ç¨ä¾åç»ä½¿ç¨ã
ç±æ¤å¯ä»¥çåºï¼Tomcatç线ç¨æ± å®ç°æ¯æ¯è¾ç®åçï¼ThreadPool.javaä¹åªæè¡ä»£ç ãç¨ä¸ä¸ªä¸ç»´æ°ç»ä¿å空é²ç线ç¨ï¼æ¯æ¬¡ä»¥ä¸ä¸ªè¾å°æ¥ä¼ï¼5个ï¼å建空é²çº¿ç¨å¹¶æ¾å°çº¿ç¨æ± ä¸ã使ç¨æ¶ä»æ°ç»ä¸ç§»èµ°ç©ºé²ç线ç¨ï¼ç¨å®åï¼åâå½è¿âç»çº¿ç¨æ± ã
美团动态线程池思路框架(DynamicTp)之动态调整Tomcat、Jetty、Undertow线程池参数篇
动态线程池框架(DynamicTp)的adapter模块,作为第三方组件线程池管理的适配器,旨在使如Tomcat、Jetty和Undertow等Web服务器内置的线程池具备动态参数调整、监控告警等增强功能。通过该模块,用户可利用Spring的事件机制监听并管理这些第三方组件的线程池,实现与核心模块的解耦。
adapter模块已成功接入SpringBoot内置的三大WebServer,包括Tomcat、Jetty和Undertow的线程池管理。通过监听机制,动态Tp框架能够及时响应这些组件的线程池变化,提供实时监控和灵活调整策略。
具体实现上,针对Tomcat、Jetty和Undertow的线程池管理,需要深入理解其内部处理流程。这些组件并未直接使用Java Util Concurrency(JUC)提供的线程池实现,而是自定义了线程池或扩展了JUC的实现,如Tomcat就采用了自定义的ThreadPoolExecutor类,通过继承或扩展JUC的抽象类来定制线程池行为。
以Tomcat为例,其内部线程池的实现中,继承自JUC原生ThreadPoolExecutor或其抽象类AbstractExecutorService。在执行任务时,Tomcat首先调用父类方法处理,然后根据任务队列类型(如TaskQueue)和线程池当前状态(如线程数、提交任务数、队列状态)进行一系列复杂判断,以决定是否创建新线程、添加任务至队列或执行拒绝策略。这种设计使得Tomcat能够高效管理请求,同时优化资源利用,避免过度创建线程导致的性能下降。
Jetty和Undertow的内部线程池实现原理与Tomcat类似,均基于JUC框架进行定制,以满足其特定的性能优化和扩展需求。通过分析这些组件的源码,可以深入了解其线程池管理策略,为后续性能调优提供宝贵信息。
动态线程池框架(DynamicTp)的引入,为Web服务器性能调优提供了强大的工具,允许用户动态调整线程池参数,提升系统响应速度和资源利用率。使用DynamicTp框架,用户可以更灵活地管理第三方组件的线程池,实现业务与开源贡献的双赢。
欢迎使用DynamicTp框架,探索更多性能优化的可能性。下期将分享在使用过程中遇到的Tomcat版本不一致导致的监控线程停滞问题,通过这一案例深入理解ScheduledExecutorService的运行机制。敬请期待。
如需交流或合作,请联系我,期待与您一起成长:
微信:yanhom
公众号:CodeFox