Tomcat7源码分析.doc

上传人:豆**** 文档编号:33509383 上传时间:2022-08-11 格式:DOC 页数:34 大小:1.52MB
返回 下载 相关 举报
Tomcat7源码分析.doc_第1页
第1页 / 共34页
Tomcat7源码分析.doc_第2页
第2页 / 共34页
点击查看更多>>
资源描述

《Tomcat7源码分析.doc》由会员分享,可在线阅读,更多相关《Tomcat7源码分析.doc(34页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、-作者xxxx-日期xxxxTomcat7源码分析【精品文档】目 录一、背景2二、Tomcat源码目录结构3三、Tomcat体系结构4四、Tomcat源码解析51. Tomcat的启动流程72. Tomcat一次完整请求的处理流程123. Tomcat的关闭流程184. Tomcat的Connector组件185. Tomcat运行过程中的线程概况及线程模型206. Tomcat的类加载机制257、Tomcat所涉及的设计模式32一、背景Tomcat作为JavaWeb领域的Web容器,目前在淘宝上也使用的也非常广泛,现在基本上所有线上业务系统都是部署在Tomcat上的。为了对平时开发的Web系

2、统有更深入的理解,于是开始仔细研究了Tomcat的源码。大家都知道Servlet规范是Java领域中为服务端编程制定的规范,对于我们开发者只是关注了Servlet规范中提供的编程组件(ServletContextListener,Filer,Servlet)等,但是规范中还有一些我们经常使用的接口(ServletContext,ServletRequest,ServletResponse,FilterChain)等都是由Tomcat去实现的,并且我们开发者实现的编程组件只是被Tomcat去回调而已。所以看Tomcat源码实现也有助于我们更好的理解Servlet规范及系统如何在容器中运行(一些开

3、源的MVC框架如Struts2,Webx,SpringMVC本质无非就是这个)。二、Tomcat源码目录结构 三、Tomcat体系结构仔细查看下图(网络上描述Tomcat架构比较清晰的一张图),不难发现其中的Connector组件以及与Container组件是Tomcat的核心。一个Server可以有多个Service,而一个Service可以包含了多个Connector组件和一个Engine容器组件,一个Engine可以由多个虚拟主机Host组成,每一个Host下面又可以由多个Web应用Context构成,每一个的Context下面可以包含多个Wrapper(Servlet的包装器)组成。T

4、omcat将Engine,Host,Context,Wrapper统一抽象成Container。一个抽象的Container模块可以包含各种服务。例如,Manager管理器(Session管理),Pipeline管道(维护管道阀门Value)等。Lifecycle接口统一定义了容器的生命周期,通过事件机制实现各个容器间的内部通讯。而容器的核心接口Container的抽象实现中定义了一个Pipeline,一个Manager,一个Realm以及ClassLoader统一了具体容器的实现规范。连接器(Connector)组件的主要任务是为其所接收到的每一个请求(可以是HTTP协议,也可以AJP协议)

5、,委托给具体相关协议的解析类ProtocolHandler,构造出Request 对象和Response 对象。然后将这两个对象传送给容器(Container)进行处理。容器(Container)组件收到来自连接器(Connector)的Request 和Response对象后,负责调用Filter,最后调用Servlet的service 方法(进入我们开发的Web系统中)。四、Tomcat源码解析Servlet规范由一组用 Java编程语言编写的类和接口组成。Servlet规范为服务端开发人员提供了一个标准的 API以及为服务器厂商制定了相关实现规范,开发人员只需要关心Servlet规范中的

6、编程组件(如Filter,Servlet等),其他规范接口由第三方服务器厂商(如Tomcat)去实现,三者的关系如下图,Servlet规范之于Tomcat的关系,也类似于JDBC规范与数据库驱动的关系,本质就是一套接口和一套实现的关系。对于一个Web服务器主要需要做的事情,个人认为基本由以下组件组成:TCP连接管理 - 请求处理线程池管理 - HTTP协议解析封装 - Servlet规范的实现,对编程组件的回调 - MVC框架,Web系统业务逻辑。Tomcat的源码及功能点因为实在过于庞大,下面的源码解析不可能全部功能模块都涉及到,接下来我们主要会根据Tomcat的启动、请求处理、关闭三个流程

7、来梳理Tomcat的具体实现,尽可能把各个模块的核心组件都展示出来。最后还会分析下Tomcat的Connector组件及Tomcat运行过程中的线程概况及线程模型及Tomcat的类加载机制及Tomcat所涉及的设计模式。Servlet规范和Web应用及Web容器间的关系 Tomcat对Servlet规范的实现1. Tomcat的启动流程 Tomcat在启动时的重点功能如下:初始化类加载器:主要初始化Tomcat加载自身类库的StandardClassLoader。解析配置文件:使用Digester组件解析Tomcat的server.xml,初始化各个组件(包含各个web应用,解析对应的web.

8、xml进行初始化)。初始化Tomcat的各级容器Container,当然最后会初始我们Web应用(我们熟悉的Listener,Filter,Servlet等初始化等在这里完成)。初始化连接器Connector:初始化配置的Connector,以指定的协议打开端口,等待请求。不管是是通过脚本bootstrap.sh启动还是通过Eclipse中启动,Tomcat的启动流程是在org.apache.catalina.startup.Bootstrap类的main方法中开始的,启动时这个类的核心代码如下所示:从以上的代码中,可以看到在Tomcat启动的时候,执行了三个关键方法即init、load、和s

9、tart。后面的两个方法都是通过反射调用org.apache.catalina.startup.Catalina的同名方法完成的,所以后面在介绍时将会直接转到Catalina的同名方法。首先分析一下Bootstrap的init方法,在该方法中将会初始化一些全局的系统属性、初始化类加载器、通过反射得到Catalina实例,在这里我们重点看一下初始化类加载器的initClassLoaders()方法:在以上的代码中,我们可以看到初始化了StandardClassLoader类加载器,这个类加载器详细的会在后面关于Tomcat类加载器机制中介绍。然后我们进入Catalina的load方法:在以上的代

10、码中,关键的任务有两项即使用Digester组件按照给定的规则解析server.xml、调用Server的init方法,而Server的init方法中,会发布事件并调用各个Service的init方法,从而级联完成各个组件的初始化。每个组件的初始化都是比较有意思的,但是我们限于篇幅先关注Tomcat各级容器的初始化及Connector的初始化,这可能是最值得关注的地方。首先看下StandardService的startInternal方法, 核心代码如下:启动Tomcat各级容器会依次先启动StandardEngine - StandardHost - StandardContext(代表一个

11、WebApp应用),因为我们比较关心我们的Web应用是在哪里被初始化回调的,所以重点看下StandardContext的startInternal()方法,核心代码如下:Tomcat的各级容器初始化完成后,就开始对Connector的初始化,接着看Connector的initInternal方法,核心代码如下:在Http11Protocol的init方法中,核心代码如下:我们看到最终的初始化方法都会调到JIoEndpoint的bind方法,网络初始化和对请求的最初处理都是通过该类及其内部类完成的,后续的内容会详细阐述这个JioEndpoint:在上面的代码中,我们可以看到此时初始化了一个Ser

12、verSocket对象,用来监听绑定端口的请求。紧接着我们来看看JioEndpoint的start()方法,核心代码如下:从以上的代码,可以看到,如果没有在server.xml中声明Executor的话,将会使用内部的一个容量为200的线程池用来后续的请求处理。并且按照参数acceptorThreadCount的设置,初始化线程来接受请求。而Acceptor就是正在接受请求并会分派给请求处理线程池:从这里我们可以看到,Acceptor已经可以接收Socket请求了,并可以调用processSocket方法来对请求进行处理。至此,Tomcat的组件启动初始化完成,等待请求的到来。2. Tomca

13、t一次完整请求的处理流程Tomcat一次完整请求处理的重点功能如下:接收Socket请求,把请求转发到线程池,由线程池分配一个线程来处理。获取Socket数据包之后,解析HTTP协议,翻译成Tomcat内部的Request和Response对象,再映射相应Container。Request和Response对象进入Tomcat中的Container容器的各级Piepline管道去流式执行,最终会流到StandardWrapperValve这个阀门。在StandardWrapperValve这个阀门中,把当前请求的Filter及Servlet封装成FilterChain, 最终执行FilterC

14、hain, 回调Web应用,完成响应。JIoEndpoint的Acceptor线程在接收到用户的请求之后,调用processSocket方法。该方法主要是从Executor请求处理线程池中获取一个线程,然后启用一个新线程执行Socket请求,JIoEndpoint的processSocket()方法的核心代码如下:接着请求处理线程池会起一个线程来处理请求来执行SocketProcessor,而SocketProcessor的run()方法中会调用Http11ConnectionHandler的process方法,SocketProcessor的run()方法核心代码如下:在Http11Conn

15、ectionHandler中会根据当前请求的协议类型去创建相应的协议处理器,我们这里分析的是HTTP协议,所以会创建Http11Processor去执行process()方法,拿到Socket数据包后解析生成Tomcat内部的Request对象与Response对象。其中Request对象只是解析Header部分内容,请求参数等做延迟处理,接着就开始调用CoyoteAdapter类进入容器处理,Http11Processor的process方法核心代码如下:紧接着CoyoteAdapter会调用Container容器的Pipeline管道一步一步的对Request,Response对象处理。T

16、omcat容器主要由4层组成,通过管道的模式将各个层的功能进行了独立,也有利于对各层插入需要的功能。如果需要,只要在server.xml中加入Value元素的配置即可完成扩展功能,CoyoteAdapter的service方法核心代码如下:StandardEngine容器默认配置了StandardEngineValve阀门。它主要做负责选择相应的Host去处理请求,StandardEngineValve的invoke()方法的核心代码如下: StandardHost默认情况下配置了ErrorReportValve阀门与StandardHostValue阀门。ErrorReportValve用于

17、处理错误日志。StandardHostValue则选择相应的Context容器,并且把当前Web应用的WebappClassLoader设置为线程上下文类加载器,保证我们的Web应用中拿到的当前线程类加载器为此应用的类加载器,StandardHostValve的invoke()方法的核心代码如下: StandardContext默认情况下配置了StandardContextValve阀门。对于WEB-INF与META-INF目录禁止访问的控制,之后获取一个此请求的Wrapper容器,StandardContextValve的invoke()方法核核心代码如下: StandardWrapper容

18、器默认情况下配置了StandardWrapperValve阀门,主要是找到当前请求的需要拦截的过滤器Filter及初始化当前请求的Servlet对象,最终会封装在FilterChain对象中,责任链方式执行,StandardWrapperValve的invoke()方法的核心代码如下:最后就会进入ApplicationFilterChain,这个也是Tomcat管道处理请求的最后节点,在ApplicationFilterChain中会依次调用Web应用的Filter,然后最终调用当前请求映射的Servlet,ApplicationFilterChain的doFilter()方法核心代码如下:3

19、. Tomcat的关闭流程Tomcat的关闭流程和Tomcat启动流程类似,都是之前已经启动的组件依次关闭销毁,所以在这里就不进行详细分析了。4. Tomcat的Connector组件前面也已经经介绍过,Connector组件是Service容器中的一部分。它主要是接收,解析HTTP请求,然后调用本Service下的相关Servlet。由于Tomcat从架构上采用的是一个分层结构,因此根据解析过的HTTP请求,定位到相应Servlet也是一个相对比较复杂的过程,整个Connector实现了从接收Socket到调用Servlet的全过程。先来看一下Connector的功能逻辑:接收Socket;

20、从Socket获取数据包,并解析成HttpServletRequest对象;从Engine容器开始走调用流程,经过各层Valve,最后调用Servlet完成业务逻辑;返回Response,关闭Socket。熟悉的80端口不必说了。protocol这里是指这个Connector支持的协议。针对HTTP协议而言,这个属性可以配置的值有:org.apache.coyote.http11.Http11Protocol ?BIO实现org.apache.coyote.http11.Http11NioProtocol ?NIO实现配置HTTP/1.1和org.apache.coyote.http11.Ht

21、tp11Protocol的效果是一样的,因此Connector的HTTP协议实现缺省是支持BIO的。无论是BIO还是NIO都是实现一个org.apache.coyote.ProtocolHandler接口,因此如果需要定制化,也必须实现这个接口。接下来再来看看默认状态下HTTP Connector的架构及其消息流。可以看见Connector中三大块Http11ProtocolMapperCoyoteAdapterHttp11Protocol类完全路径org.apache.coyote.http11.Http11Protocol,这是支持HTTP的BIO实现。Http11Protocol包含了J

22、IoEndpoint对象及Http11ConnectionHandler对象。Http11ConnectionHandler对象维护了一个Http11Processor对象池,Http11Processor对象会调用CoyoteAdapter完成HTTP Request的解析和分派。JIoEndpoint维护了两个线程,Acceptor请求接收线程及Executor请求处理线程池。Acceptor是接收Socket,然后从Executor请求处理线程池中找出空闲的线程处理socket,如果Executor请求处理线程池没有空闲线程,请求会被放入阻塞队列等待有空闲线程。Mapper类完全路径or

23、g.apache.tomcat.util.http.mapper.Mapper,此对象维护了一个从Host到Wrapper的各级容器的快照。它主要是为了,当HTTP Request被解析后,能够将HTTP Request绑定到相应的Host,Context,Wrapper(Servlet),进行业务处理。CoyoteAdapter类完全路径org.apache.catalina.connector.CoyoteAdapter,此对象负责将http request解析成HttpServletRequest对象,之后绑定相应的容器,然后从Engine开始逐层调用Valve直至该Servlet。比如

24、根据Request中的JSESSIONID绑定服务器端的相应Session。这个JSESSIONID按照优先级或是从Request URL中获取,或是从Cookie中获取,然后再Session池中找到相应匹配的Session对象,然后将其封装到HttpServletRequest对象,所有这些都是在CoyoteAdapter中完成的。看一下将Request解析为HttpServletRequest对象后,开始调用Servlet的代码;connector.getContainer().getPipeline().getFirst().invoke(request,response);Connec

25、tor的拿到的容器就是StandardEngine,代码的可读性很强,获取StandardEngine的Pipeline,然后从第一个Valve开始调用逻辑。5. Tomcat运行过程中的线程概况及线程模型Tomcat在运行过程中会涉及到很多线程,主要的线程有Tomcat启动时的main主线程(Java进程启动时的那个线程), 子容器启动与关闭线程池(用来启动和关闭子容器),Tomcat后台线程(Tomcat内置的后台线程,比如用来热部署Web应用), 请求接收线程(用来接收请求的线程), 请求处理线程池(用来处理请求的线程)。理解了Tomcat运行过程中的主要线程,有助于我们理解整个系统的线

26、程模型。下面是每个线程的源码及功能的详细分析:(1)main主线程:即从main开始执行的线程,在启动Catalina之后就一直在Server的await方法中等待关闭命令,如果未接收到关闭命令就一直等待下去。main线程会一直阻塞着等待关机命令。启动过程:Bootstrap.main Catalina.start Catalina.await() StandardServer.await()(2)子容器启动与关闭线程:ContainerBase的protectedThreadPoolExecutorstartStopExecutor容器线程池执行器处理子容器的启动和停止在ContainerB

27、ase.initInternal()方法中进行初始化作用:在ContainerBase.startInternal()方法中启动子容器,在ContainerBase.stopInternal()方法中停止子容器,即处理子容器的启动和停止事件。子容器线程池创建过程:StandardService.initInternal ContainerBase.initInternal newThreadPoolExecutor()启动过程:StandardService.startInternal startStopExecutor.submit(newStartChild(Child)关闭过程:Stop

28、Child(Child) execute(ftask) addIfUnderCorePoolSize(command)addIfUnderCorePoolSize内容如下:(3)容器的后台处理线程:ContainerBackgroundProcessor是ContainerBase的一个内部类。启动过程:StandardService.startInternal ContainerBase.startInternal ContainerBase.threadStart()(4)请求接收线程:即运行Acceptor的线程,启动Connector的时候产生启动过程:StandardService.

29、startInternal Connector.startInternal() protocolHandler.start() AbstractProtocol.start() endpoint.start() AbstractEndpoint.Start() JioEndpoint.startInternal() JIoEndpoint.startAcceptorThreads()startAcceptorThreads内容如下:(5)请求处理线程:即运行SocketProcessor的线程,从请求处理线程池中产生,.AbstractEndpoint的成员变量privateExecutore

30、xecutor用来处理请求,.JioEndpoint(继承了AbstractEndpoint)在方法startInternal()中调用createExecutor()来创建executor为线程池对象ThreadPoolExecutor请求处理线程池创建过程:StandardService.startInternal Connector.startInternal protocolHandler.start AbstractProtocol.start endpoint.start AbstractEndpoint.Start JIoEndpoint.startInternal JIoEnd

31、point.createExecutor newThreadPoolExecutor()启动过程:Acceptor.run()/请求接收线程接收到请求后processSocket(socket)getExecutor().execute(newSocketProcessor(wrapper)/交给请求处理线程处理(command,0,TimeUnit.MILLISECONDS)super.execute(command) addIfUnderCorePoolSize(command)addIfUnderCorePoolSize内容如下:至此SocketProcessor.run运行起来了,这个

32、线程负责处理请求。大家都知道Java中的IO模型分阻塞式的IO模型(BIO)及非阻塞式的IO模型(NIO),Tomcat中的对请求接收网络IO模块中也同样实现了基于上述两种IO的线程模型,具体的实现如下:Tomcat基于阻塞式IO(BIO)的线程模型序列图如下:Tomcat基于非阻塞式IO(NIO)的线程模型序列图如下:6. Tomcat的类加载机制主流的JavaWeb服务器,如(Tomcat,Jetty,WebLogic)都实现了自己定义的类加载器(一般不止一个),因为Web服务器一般要解决如下几个问题:1、部署在同一个服务器上的两个Web应用程序所使用的Java类库可以实现相互隔离。2、部

33、署在同一个服务器上的两个应用程序所使用的Java类库可以相互共享。3、服务器需要尽可能地保证自身的安全不受部署的Web应用程序的影响。为了解决这几个问题,Tomcat实现了如下类加载器的层级结构:Tomcat7运行时类的加载说明:1)Bootstrap Class Loader是JVM的内核由C实现的,加载了JVM的核心包rt.jar。rt.jar中的所有类执行其class的getClassLoader()方法都将返回null,例如Integer.class.getClassLoader()。2)Extension Class Loader主要加载了JVM扩展包中相关的jar包。例如运行下列代

34、码将System.out.println(ZipPath.class.getClassLoader();将得到如下的运行结果:sun.misc.Launcher$ExtClassLoader。3)System Class Loader加载CLASSPATH相关的类,例如在Tomcat的Bootstrap的main方法中执行System.out.println(Bootstrap.class.getClassLoader();则将得到:sun.misc.Launcher$AppClassLoader。4)Common Class Loader,Tomcat7中的CATALINA_HOME/lib

35、下的jar包。注意Tomcat在启动文件中将启动时配置了-classpath %CATALINA_HOME%libcatalina.jar因此catalina.jar中的类虽然指定使用类加载器Common Class Loader,但是按JVM的委托加载原则System.out.println(Bootstrap.class.getClassLoader();得到的类加载器是:sun.misc.Launcher$AppClassLoader。5)Webapp Class Loader, 主要负责加载Context容器中的所有的类。实际上该加载器提供了参数delegateLoad供用户设定是否使

36、用parent-first加载。默认该值为false,默认用parent-last加载。出于安全性的考虑对于核心类WebappClassLoader是不允许加载的。包括:java.,javax.servlet.jsp.jstl,javax.servlet.,javax.el。Tomcat类加载器的相关类图:Tomcat类加载器的相关源代码分析1、ClassLoader的load方法1)在该方法中,首先检查是否已经加载了该类,这里有个问题JVM如何判断一个类是否被加载过的?这里涉及到了类的命名空间问题。在JAVA中判断一个类是否相同不仅看类名是否相同,还要看其类加载器是否相同。同一个类可以被不同

37、的类加载器所加载,并且认为是不同的。该问题可以分解下面两个方面看。a) 单一加载原则:在加载器链中,一个类只会被链中的某一个加载器加载一次。而不会被重复加载。实现类的共享,Tomcat多个应用,如果需要共享一些jar包,那么只需要交给commonClassLoader加载,那么所有的应用就可以共享这些类。b) 可见性原则:父加载器加载的类,子加载器是可以访问的。而自加载器所加载的类,父加载器无法访问。不同加载器链之间其是相互不可见,无法访问的。实现隔离,Tomcat就是应用该特性,为每一个Context容器创建一个WebappClassLoader类加载器对象,从而实现了应用间的相互隔离。应用

38、间的类是不可见的所以无法相互访问。2) 如果步骤一中无缓存,查看该类父加载器,如果存在那么委托给付加载器。如果没有父加载器那么认为BootstrapClassLoader是其父加载器,委托进行加载。3)如果父加载器无法加载则抛出ClassNotFoundException,调用抽象方法findClass方法。4)此处的resolveClass方法指的是上文类加载过程中连接的第三步操作。resolve该类的形式引用等等。2、类URLClassLoader的findClass方法该方法的核心是加载获取Java类字节码文件的字节流,然后调用父类的defineClass方法完成类的构造过程。defin

39、eClass是由JVM实现的,不允许被覆写,因此用户类文件就必须遵循JVM的文件规范才能被正确的解析。3、WebappClassLoader重写了ClassLoader的loadClass方法4、WebappClassLoader的findClass()方法findClassInternal()方法findResourceInternal()方法5、Web应用中经常使用到的线程上下文类加载器的在Tomcat中的设置实现在Web应用中我们经常用到线程上下文类加载器,拿到的肯定是当前WebApp的WebAppClassLoader。ClassLoader classLoader=Thread.cu

40、rrentThread().getContextClassLoader();我们用到的线程上下文类加载器其实是在StandardHostValve的invoke()方法中被设置的。7、Tomcat所涉及的设计模式Tomcat虽然代码比较庞大,但是整体还是设计的比较优雅,特别是很多组件化的设计思路,其中涉及到的一些常用的设计模式值得我们学习及借鉴:责任链模式Tomcat中有两个地方比较明显的使用了责任链模式,一、Tomcat中的ApplicationFilterChain实现了Filter拦截和实际Servlet的请求,是典型的责任链模式。其他开源框架中类似的设计还有Struts2中的Defau

41、ltActionInvocation实现Interceptor拦截和Action的调用。Spring AOP中ReflectiveMethodInvocation实现MethodInceptor方法拦截和target的调用。二、Tomcat中的Pipeline-Valve模式也是责任链模式的一种变种,从Engine到Host再到Context一直到Wrapper都是通过一个链来传递请求。观察者模式Tomcat通过LifecycleListener对组件生命周期组件Lifecycle进行监听就是典型的观察者模式,各个组件在其生命期中会有各种各样行为,而这些行为都会触发相应的事件,Tomcat就是

42、通过侦听这些事件达到对这些行为进行扩展的目的。在看组件的init和start过程中会看到大量如:lifecycle.fireLifecycleEvent(AFTER_START_EVENT,null);这样的代码,这就是对某一类型事件的触发,如果你想在其中加入自己的行为,就只用注册相应类型的事件即可。门面模式门面设计模式在 Tomcat 中有多处使用,在 Request 和 Response 对象封装中(RequestFacade,ResponseFacade)、ApplicationContext 到ApplicationContextFacade等都用到了这种设计模式。这种设计模式主要用在

43、一个大的系统中有多个子系统组成时,这多个子系统肯定要涉及到相互通信,但是每个子系统又不能将自己的内部数据过多的暴露给其它系统,不然就没有必要划分子系统了。每个子系统都会设计一个门面,把别的系统感兴趣的数据封装起来,通过这个门面来进行访问。模板方法模式模板方法模式是我们平时开发当中常用的一种模式,把通用的骨架抽象到父类中,子类去实现特地的某些步骤。Tomcat及Servlet规范API中也大量的使用了这种模式,比如Tomcat中的ContainerBase中对于生命周期的一些方法init,start,stop和Servlet规范API中的GenericServlet中的service抽象骨架模板方法均使用了模板方法模式。【精品文档】

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 高考资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com