ActiveMQ源码解析(二):聊聊客户端和broker的通讯
来源:互联网 发布:淘宝我的爱车 编辑:程序博客网 时间:2024/05/16 10:29
ActiveMQ支持以下几种通讯协议:
协议 备注HTTP/HTTPS基于http协议TCP默认协议UDP 性能更好,但不可靠SSL 安全链接NIO基于tcp,使用异步非阻塞方式使性能得到提升,具有更好的扩展性VM如果客户端和代理运行在同一个vm中就直接通讯不占用网络带宽对SSL的支持主要是在https包中,它主要扩展了keberos认证协议。tcp包和udp包与http类似类似提供了对响应的协议的支持
HTTP
主要的代码在http包中,它依赖于apache httpclient,首先来看看客户端类HttpClientTransport,主要介绍它的三个方法,oneway()、run()和start().
oneway:简单的一次http post请求传输数据,这里顺带科普一下,activemq中connector负责broker与broker之间的通讯,transport负责broker和客户端之间的通讯。
public void oneway(Object command) throws IOException { //如果已经关闭抛出异常 if (isStopped()) { throw new IOException("stopped."); } //post请求 HttpPost httpMethod = new HttpPost(getRemoteUrl().toString()); //注册请求头 configureMethod(httpMethod); //格式化需要传递的数据 String data = getTextWireFormat().marshalText(command); //utf8解码 byte[] bytes = data.getBytes("UTF-8"); //如果允许压缩数据则压缩数据传送 if (useCompression && canSendCompressed && bytes.length > minSendAsCompressedSize) { //输出流 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); //gzip输出流 GZIPOutputStream stream = new GZIPOutputStream(bytesOut); //向流中写入数据 stream.write(bytes); stream.close(); //添加头部信息 httpMethod.addHeader("Content-Type", "application/x-gzip"); //打印日志 if (LOG.isTraceEnabled()) { LOG.trace("Sending compressed, size = " + bytes.length + ", compressed size = " + bytesOut.size()); } bytes = bytesOut.toByteArray(); } ByteArrayEntity entity = new ByteArrayEntity(bytes); httpMethod.setEntity(entity); HttpClient client = null; HttpResponse answer = null; try { //创建客户端 client = getSendHttpClient(); HttpParams params = client.getParams(); HttpConnectionParams.setSoTimeout(params, soTimeout); answer = client.execute(httpMethod); int status = answer.getStatusLine().getStatusCode(); if (status != HttpStatus.SC_OK) { throw new IOException("Failed to post command: " + command + " as response was: " + answer); } if (command instanceof ShutdownInfo) { try { stop(); } catch (Exception e) { LOG.warn("Error trying to stop HTTP client: "+ e, e); } } } catch (IOException e) { throw IOExceptionSupport.create("Could not post command: " + command + " due to: " + e, e); } finally { if (answer != null) { EntityUtils.consume(answer.getEntity()); } } }
public void run() { //打印日志 if (LOG.isTraceEnabled()) { LOG.trace("HTTP GET consumer thread starting: " + this); } //客户端 HttpClient httpClient = getReceiveHttpClient(); URI remoteUrl = getRemoteUrl(); while (!isStopped() && !isStopping()) { httpMethod = new HttpGet(remoteUrl.toString()); configureMethod(httpMethod); HttpResponse answer = null; try { answer = httpClient.execute(httpMethod); int status = answer.getStatusLine().getStatusCode(); if (status != HttpStatus.SC_OK) { if (status == HttpStatus.SC_REQUEST_TIMEOUT) { LOG.debug("GET timed out"); try { Thread.sleep(1000); } catch (InterruptedException e) { onException(new InterruptedIOException()); Thread.currentThread().interrupt(); break; } } else { onException(new IOException("Failed to perform GET on: " + remoteUrl + " as response was: " + answer)); break; } } else { receiveCounter++; DataInputStream stream = createDataInputStream(answer); Object command = getTextWireFormat().unmarshal(stream); if (command == null) { LOG.debug("Received null command from url: " + remoteUrl); } else { //消费消息 doConsume(command); } stream.close(); } } catch (IOException e) { onException(IOExceptionSupport.create("Failed to perform GET on: " + remoteUrl + " Reason: " + e.getMessage(), e)); break; } finally { if (answer != null) { try { EntityUtils.consume(answer.getEntity()); } catch (IOException e) { } } } } }
start方法:发送一个http head检查broker是否能连通和一个http options请求确认是否支持压缩传输
protected void doStart() throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("HTTP GET consumer thread starting: " + this); } HttpClient httpClient = getReceiveHttpClient(); URI remoteUrl = getRemoteUrl(); HttpHead httpMethod = new HttpHead(remoteUrl.toString()); configureMethod(httpMethod); // Request the options from the server so we can find out if the broker we are // talking to supports GZip compressed content. If so and useCompression is on // then we can compress our POST data, otherwise we must send it uncompressed to // ensure backwards compatibility. HttpOptions optionsMethod = new HttpOptions(remoteUrl.toString()); ResponseHandler<String> handler = new BasicResponseHandler() { @Override public String handleResponse(HttpResponse response) throws HttpResponseException, IOException { for(Header header : response.getAllHeaders()) { if (header.getName().equals("Accepts-Encoding") && header.getValue().contains("gzip")) { LOG.info("Broker Servlet supports GZip compression."); canSendCompressed = true; break; } } return super.handleResponse(response); } }; try { httpClient.execute(httpMethod, new BasicResponseHandler()); httpClient.execute(optionsMethod, handler); } catch(Exception e) { throw new IOException("Failed to perform GET on: " + remoteUrl + " as response was: " + e.getMessage()); } super.doStart(); }
start
protected void doStart() throws Exception { //创建jetty服务器端 createServer(); //如果连接器不存在则创建连接器,工厂方法 if (connector == null) { connector = socketConnectorFactory.createConnector(server); } URI boundTo = bind(); ServletContextHandler contextHandler = new ServletContextHandler(server, "/", ServletContextHandler.SECURITY); ServletHolder holder = new ServletHolder(); //后面会介绍HttpTunnelServlet holder.setServlet(new HttpTunnelServlet()); contextHandler.addServlet(holder, "/"); contextHandler.setAttribute("acceptListener", getAcceptListener()); contextHandler.setAttribute("wireFormat", getWireFormat()); contextHandler.setAttribute("transportFactory", transportFactory); contextHandler.setAttribute("transportOptions", transportOptions); //AMQ-6182 - disabling trace by default configureTraceMethod((ConstraintSecurityHandler) contextHandler.getSecurityHandler(), httpOptions.isEnableTrace()); addGzipHandler(contextHandler); server.start(); // Update the Connect To URI with our actual location in case the configured port // was set to zero so that we report the actual port we are listening on. int port = boundTo.getPort(); int p2 = getConnectorLocalPort(); if (p2 != -1) { port = p2; } setConnectURI(new URI(boundTo.getScheme(), boundTo.getUserInfo(), boundTo.getHost(), port, boundTo.getPath(), boundTo.getQuery(), boundTo.getFragment())); }
服务器端和客户端都讲完了,http包中还剩下一个HttpSpringEmbededTunnelServlet,它的父类是HttpEmbededTunnelServlet,再往上是HttpTunnelServlet。接下来从HttpTunnelServlet开始,依次讲讲doGet()方法、doPost()方法。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // lets return the next response Command packet = null; int count = 0; try { //阻塞式的传输通道 BlockingQueueTransport transportChannel = getTransportChannel(request, response); if (transportChannel == null) { return; } //从阻塞队列中取出数据 packet = (Command)transportChannel.getQueue().poll(requestTimeout, TimeUnit.MILLISECONDS); //获取输出流 DataOutputStream stream = new DataOutputStream(response.getOutputStream()); wireFormat.marshal(packet, stream); count++; } catch (InterruptedException ignore) { } if (count == 0) { response.setStatus(HttpServletResponse.SC_REQUEST_TIMEOUT); } }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { InputStream stream = request.getInputStream(); String contentType = request.getContentType(); if (contentType != null && contentType.equals("application/x-gzip")) { stream = new GZIPInputStream(stream); } // Read the command directly from the reader, assuming UTF8 encoding Command command = (Command) wireFormat.unmarshalText(new InputStreamReader(stream, "UTF-8")); if (command instanceof WireFormatInfo) { WireFormatInfo info = (WireFormatInfo) command; if (!canProcessWireFormatVersion(info.getVersion())) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Cannot process wire format of version: " + info.getVersion()); } } else { //根据clientId从transport容器中获取transport,clientId是在创建HtppClientTransport时Gennerator生成的 BlockingQueueTransport transport = getTransportChannel(request, response); if (transport == null) { return; } if (command instanceof ConnectionInfo) { ((ConnectionInfo) command).setTransportContext(request.getAttribute("javax.servlet.request.X509Certificate")); } transport.doConsume(command); } }
小结
transport中的http包中包含了客户端、服务器端。这里创建的server其实是一个jetty服务器,绑定80端口,它有个HttpTunnelServlet,消息存放在其中的BlockingQueueTransport中,doGet用于从服务端获取消息,doPost用于通知响应的listener消费消息。
VM
主要的类是VMTransportServer,它创建了一个VMTransport做为客户端,另一个作为服务器端,当Server启动时会创建客户端,然后和server注册连接在一起相互通讯,它主要的作用就是用在broker和客户端都在一个vm中的情况,可以减少网络开销提高效率
总结
transport包提供了对客户端和broker的多种通讯方式的支持,具体使用哪种通讯协议由用户在配置文件中在transportconnector节点配置。
0 0
- ActiveMQ源码解析(二):聊聊客户端和broker的通讯
- ActiveMQ源码解析(五):聊聊activemq的broker集群
- ActiveMQ源码解析(一):聊聊broker
- ActiveMQ源码分析(三):聊聊broker到broker的通讯
- ActiveMQ源码解析(四):聊聊消息的可靠传输机制和事务控制
- 客户端和服务端通讯的N种方式(二)
- 客户端和服务端通讯的N种方式(二)
- 客户端和服务端通讯的N种方式(二)
- RocketMQ源码解析-Broker的HA实现
- RocketMQ源码解析-Broker的消息存储
- ActiveMQ之三:启动ActiveMQ的Broker
- ActiveMQ完整的java客户端例子(源码)
- activemq--MASTER SLAVE+BROKER CLUSTER 实践(二)
- 关于c++(客户端)和JAVA(服务端)的TCP通讯(基于stomp协议)(二)
- kafka源码解析之四Broker的模块组成
- kafka源码解析之三Broker的启动
- TCP(二)客户端和服务器通讯
- 消息中间件 activeMQ的源码分析 之 TCP通讯机制
- 四元数法在计算机图形学中的应用
- 数据库-----数据库连接池
- Arrayfire常用的那几招(引用于葵花宝典)
- React Native es6继承(Component例子)
- 浅谈协方差矩阵
- ActiveMQ源码解析(二):聊聊客户端和broker的通讯
- animation简单动画
- WCF——两个解决方案引用WCF服务
- java工具类之配置文件的读取
- linux面试c语言编程----------把一个字符串倒序,如“abcd”倒序后变为“dcba”
- IIS负载均衡-Application Request Route详解第五篇:使用ARR来配置试点项目
- Android自定义View的正确流程
- 文件系统分析总结
- 第16周项目——阅读程序3