netty實現高性能文件服務器,通用文件服務組件(Netty實現版本)

本文所述文件服務組件在筆者此前一篇文章中已有闡述(基于netty的文件上傳下載組件),不過本文將基于之前這個實現再次進行升級改造,利用基于注解的方式進行自動裝配。

1. 簡介

1.1 Netty簡介

Netty是一個異步事件驅動的網絡應用程序框架,用于快速開發可維護的高性能協議服務器和客戶端。關于其詳細的介紹可以參考Netty官方網站。

Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.

Netty主要特點:

Unified API for various transport types - blocking and non-blocking socket(統一API)

Based on a flexible and extensible event model which allows clear separation of concerns(事件模型)

Highly customizable thread model - single thread, one or more thread pools such as SEDA(線程模型)

True connectionless datagram socket support (since 3.1)(無連接數據報文Socket支持)

1.2 組件功能介紹

該組件基于netty3.6.3實現,具有如下功能:文件上傳,文件替換,文件刪除,如果是圖片的話,還可以生成縮略圖等功能。使用簡單,只需要引入commons-doc-client-netty,即可以實現文件的以上操作。

本組件分為三個module,分別為:

commons-doc-server-netty:Netty實現文件服務組件的服務端

commons-doc-common:Netty文件服務組件公共組件

commons-doc-client-netty:Netty文件服務組件的客戶端

2. 服務端

2.1 功能簡介

服務端組件實現以下功能:文件上傳,文件替換,文件刪除,如果是圖片的話,還可以生成縮略圖等功能。代碼結構如下圖所示,

1460000019853363?w=649&h=796

所有的文件服務都是基于接口DocServerProcessor進行的,主要有以下幾個實現類:

UploadDocServerHandler實現文件上傳服務

ReplaceDocServerHandler實現文件替換服務

DeleteDocServerHandler實現文件刪除服務

CreateThumbPictureServerHandler實現創建圖片縮略圖服務

2.2 實現步驟

具體實現步驟以文件上傳為例。

首先 org.fortune.doc.server.support.DocServerHandler類會持續監聽客戶端的請求,如果是文件處理動作,則會進入messageReceived方法進行相應的處理邏輯。該類定義了以下成員變量:

//http請求

private HttpRequest request;

//是否需要斷點續傳作業

private boolean readingChunks;

//接收到的文件內容

private final StringBuffer responseContent = new StringBuffer();

//解析收到的文件

private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); //16384L

//post請求的解碼類,它負責把字節解碼成Http請求。

private HttpPostRequestDecoder decoder;

//請求參數

private RequestParam requestParams = new RequestParam();

該方法實現中,如果文件大小小于chunked的最小值,則直接進行文件上傳操作。否則,需要進行分塊處理。然后進行文件上傳操作。

文件大小小于1k的操作:

if (request.isChunked()) { //說明還沒有請求完成,繼續

this.readingChunks = true;

LOGGER.info("文件分塊操作....");

} else {

LOGGER.info("文件大小小于1KB,文件接收完成,直接進行相應的文件處理操作....");

//請求完成,則接收請求參數,進行初始化請求參數

RequestParamParser.parseParams(this.decoder, this.requestParams);

//根據請求參數進行相應的文件操作

LOGGER.info("文件處理開始....requestParams參數解析:{}",requestParams);

String result = DocServerHandlerFactory.process(this.requestParams);

LOGGER.info("文件處理結束....FileServerHandlerFactory處理結果:{}",result);

this.responseContent.append(result);

//給客戶端響應信息

writeResponse(e.getChannel());

e.getFuture().addListener(ChannelFutureListener.CLOSE);

}

需要分塊處理操作:

HttpChunk chunk = (HttpChunk) e.getMessage();

try {

//chunk.getContent().capacity();

LOGGER.info("文件分塊操作....文件大小:{} bytes",chunk.getContent().capacity());

this.decoder.offer(chunk);

} catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {

e1.printStackTrace();

this.responseContent.append(e1.getMessage());

writeResponse(e.getChannel());

Channels.close(e.getChannel());

return;

}

if (chunk.isLast()) {

//文件末尾

this.readingChunks = false;

LOGGER.info("到達文件內容的末尾,進行相應的文件處理操作....start");

RequestParamParser.parseParams(this.decoder, this.requestParams);

LOGGER.info("文件處理開始....requestParams參數解析:{}",requestParams);

String result = DocServerHandlerFactory.process(this.requestParams);

LOGGER.info("文件處理結束....FileServerHandlerFactory處理結果:{}",result);

this.responseContent.append(result);

//給客戶端響應信息

writeResponse(e.getChannel());

e.getFuture().addListener(ChannelFutureListener.CLOSE);

LOGGER.info("到達文件內容的末尾,進行相應的文件處理操作....end");

}

以上操作主要有兩個注意點:

請求參數的解析工作(根據HttpDataType進行相應參數的賦值操作)

根據解析的參數進行相應的文件處理操作(根據文件操作類型,選擇相應的處理句柄進行文件處理)

3. 客戶端

3.1 功能簡介

客戶端組件主要提供對外訪問服務端組件的接口,提供以下接口:文件上傳,文件替換,文件刪除,如果是圖片的話,還可以生成縮略圖等功能。代碼結構如下:

1460000019853364?w=595&h=713

org.fortune.doc.client.DocClient類是對外提供接口的工具類,具有以下主要方法:

uploadFile 文件上傳,對應文件處理句柄類為:org.fortune.doc.client.handler.UploadDocClientHandler

deleteFile 刪除服務端文件,對應文件處理句柄類為:org.fortune.doc.client.handler.DeleteDocClientHandler

replaceFile 替換服務端文件,對應文件處理句柄類為:org.fortune.doc.client.handler.ReplaceDocClientHandler

createThumbPicture 生成縮略圖,對應文件處理句柄類為:org.fortune.doc.client.handler.CreateThumbPictureClientHandler

3.2 實現步驟

實現步驟以上傳文件為例,其他類似實現。直接上代碼:

/**

* 文件上傳

* @param file 需要上傳的文件

* @param fileName 文件名稱

* @param thumbMark 是否需要生成縮略圖

* @return

* @author:landyChris

*/

public static String uploadFile(File file, String fileName,

boolean thumbMark) {

DocClientPipelineFactory clientPipelineFactory = new DocClientPipelineFactory();

//輔助類。用于幫助我們創建NETTY服務

ClientBootstrap bootstrap = createClientBootstrap(clientPipelineFactory);

String strThumbMark = Constants.THUMB_MARK_NO;

if (thumbMark) {

strThumbMark = Constants.THUMB_MARK_YES;

}

//具體處理上傳文件邏輯

uploadFile(bootstrap, DocClientContainer.getInstance().getHost(),

DocClientContainer.getInstance().getPort(), file, fileName, strThumbMark,

DocClientContainer.getInstance().getUserName(),

DocClientContainer.getInstance().getPassword());

Result result = clientPipelineFactory.getResult();

if ((result != null) && (result.isSuccess())) {

return result.getFilePath();

}

return null;

}

具有三個參數,前面幾行代碼都是很一些netty的初始化工作,具體看一個私有方法uploadFile,如下代碼所示:

private static void uploadFile(ClientBootstrap bootstrap, String host,

int port, File file, String fileName, String thumbMark,

String userName, String pwd) {

//1.構建uri對象

URI uri = getUri(host, port);

//2.連接netty服務端

ChannelFuture future = bootstrap.connect(new InetSocketAddress(host,

port));

//3.異步獲取Channel對象

Channel channel = future.awaitUninterruptibly().getChannel();

if (!future.isSuccess()) {

future.getCause().printStackTrace();

bootstrap.releaseExternalResources();

return;

}

//4.初始化文件上傳句柄對象

AbstractDocClientHandler handler = new UploadDocClientHandler(host, uri,

file, fileName, thumbMark, userName, pwd);

//5.獲取Request對象

HttpRequest request = handler.getRequest();

//6.獲取Http數據處理工廠

HttpDataFactory factory = getHttpDataFactory();

//7.進行數據的包裝處理,主要是進行上傳文件所需要的參數的設置,此時調用的句柄是具體的UploadFileClientHandler對象

HttpPostRequestEncoder bodyRequestEncoder = handler

.wrapRequestData(factory);

//8.把request寫到管道中,傳輸給服務端

channel.write(request);

//9.做一些關閉資源的動作

if (bodyRequestEncoder.isChunked()) {

channel.write(bodyRequestEncoder).awaitUninterruptibly();

}

bodyRequestEncoder.cleanFiles();

channel.getCloseFuture().awaitUninterruptibly();

bootstrap.releaseExternalResources();

factory.cleanAllHttpDatas();

}

主要有以下實現步驟:

構建uri對象

連接netty服務端

異步獲取Channel對象

初始化文件上傳句柄對象

獲取Request對象

獲取Http數據處理工廠

進行數據的包裝處理,主要是進行上傳文件所需要的參數的設置,此時調用的句柄是具體的UploadFileClientHandler對象

把request寫到管道中,傳輸給服務端

做一些關閉資源的動作

具體細節實現請參考github上的代碼。如果各位讀者喜歡的話,可以加個star哈。

4. 操作指引

該文件服務組件的使用需要分為兩個部分,一個是服務端配置與啟動,一個是客戶端的配置與啟動。

4.1 服務端配置與啟動

4.1.1 配置

服務端的配置采用yml文件的配置,更加的簡潔明了,主要的注意點是文件存放位置的配置,在開發過程中,可以有兩種方式配置:

Idea自啟動方式:如果采用此種方式則需要把rootPath配置到工程路徑下(target目錄),如下所示:

# 在idea中執行的話,需要配置target目錄下的打包文件

rootPath: C:\03_code\idea_workspace\fortune-commons\commons-doc-server-netty\target\commons-doc-server-netty\ #上傳文件的根目錄,實際工作環境按照實際情況更改即可

打包后在tomcat獨立啟動方式

# 也可以單獨把打包后的war包拷貝到tomcat webapp目錄下直接運行也可以

rootPath: C:\05_webserver\apache-tomcat-8.5.42\webapps\doc-server #上傳文件的根目錄,實際工作環境按照實際情況更改即可

4.1.2 啟動

本文采用的是idea自啟動方式,則需要配置一下tomcat路徑,以及引入相應的module即可,如下圖所示:

1460000019853365?w=1614&h=1010

需要注意的是,在Deployment頁簽,需要配置該項目訪問的Application Context,否則有可能啟動后出現404的情況。如下圖所示:

1460000019853366?w=1115&h=797

配置完成即可啟動文件服務組件,如下圖即為啟動信息日志:

....

2019-07-20 23:48:56.174 [RMI TCP Connection(3)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mvcViewResolver'

2019-07-20 23:48:56.182 [RMI TCP Connection(3)-127.0.0.1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'viewResolver'

2019-07-20 23:48:56.208 [RMI TCP Connection(3)-127.0.0.1] INFO org.fortune.commons.core.help.BeanInitializeCompletedListener - spring conf 容器初始化完畢..處理啟動之后事件--start

2019-07-20 23:48:56.212 [RMI TCP Connection(3)-127.0.0.1] INFO org.fortune.doc.server.DocServerContainer - 加入賬戶:fortune

2019-07-20 23:48:56.212 [RMI TCP Connection(3)-127.0.0.1] INFO org.fortune.doc.server.DocServerContainer - 加入賬戶:fortune0

2019-07-20 23:48:56.213 [RMI TCP Connection(3)-127.0.0.1] INFO org.fortune.doc.server.DocServerContainer - 加入默認賬戶:Account{userName='default_account', password='lyx', rootPath='C:\05_webserver\apache-tomcat-8.5.42\bin\', level=1, thumbHeight=20, thumbWidth=20}

2019-07-20 23:48:56.296 [RMI TCP Connection(3)-127.0.0.1] INFO org.fortune.commons.core.help.BeanInitializeCompletedListener - spring conf 容器初始化完畢..處理啟動之后事件--end

2019-07-20 23:48:56.302 [RMI TCP Connection(3)-127.0.0.1] DEBUG org.springframework.jndi.JndiTemplate - Looking up JNDI object with name [java:comp/env/spring.liveBeansView.mbeanDomain]

2019-07-20 23:48:56.303 [RMI TCP Connection(3)-127.0.0.1] DEBUG org.springframework.jndi.JndiLocatorDelegate - Converted JNDI name [java:comp/env/spring.liveBeansView.mbeanDomain] not found - trying original name [spring.liveBeansView.mbeanDomain]. javax.naming.NameNotFoundException: Name [spring.liveBeansView.mbeanDomain] is not bound in this Context. Unable to find [spring.liveBeansView.mbeanDomain].

2019-07-20 23:48:56.303 [RMI TCP Connection(3)-127.0.0.1] DEBUG org.springframework.jndi.JndiTemplate - Looking up JNDI object with name [spring.liveBeansView.mbeanDomain]

2019-07-20 23:48:56.304 [RMI TCP Connection(3)-127.0.0.1] DEBUG org.springframework.jndi.JndiPropertySource - JNDI lookup for name [spring.liveBeansView.mbeanDomain] threw NamingException with message: Name [spring.liveBeansView.mbeanDomain] is not bound in this Context. Unable to find [spring.liveBeansView.mbeanDomain].. Returning null.

2019-07-20 23:48:56.307 [RMI TCP Connection(3)-127.0.0.1] INFO org.springframework.web.context.ContextLoader - Root WebApplicationContext initialized in 2216 ms

2019-07-20 23:48:56.311 [RMI TCP Connection(3)-127.0.0.1] DEBUG org.springframework.web.filter.CharacterEncodingFilter - Filter 'characterEncodingFilter' configured for use

2019-07-20 23:48:56.320 [RMI TCP Connection(3)-127.0.0.1] INFO org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'doc-server'

....

然后就可以訪問如下頁面,顯示如下:

1460000019853367?w=779&h=165

不過我們配置的netty端口是9999,我們試著訪問一下,

1460000019853368?w=1480&h=963

此時我們就可以不依賴客戶端,直接在網頁端進行文件上傳的測試操作了。輸入賬戶,密碼,選擇相應的文件即可上傳,上傳成功的返回頁面如下:

1460000019853369?w=1919&h=502

后臺控制臺打印出的日志信息如下:

2019-07-20 23:52:12.833 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....

2019-07-20 23:52:12.835 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:1024 bytes

2019-07-20 23:52:12.849 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:2048 bytes

2019-07-20 23:52:12.849 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:2048 bytes

2019-07-20 23:52:12.850 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:3072 bytes

2019-07-20 23:52:12.850 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:4096 bytes

2019-07-20 23:52:12.850 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:6144 bytes

2019-07-20 23:52:12.852 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:8192 bytes

2019-07-20 23:52:12.852 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:8192 bytes

2019-07-20 23:52:12.852 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:3072 bytes

2019-07-20 23:52:12.853 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:8192 bytes

2019-07-20 23:52:12.853 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:7168 bytes

2019-07-20 23:52:12.853 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:8192 bytes

2019-07-20 23:52:12.854 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:8192 bytes

2019-07-20 23:52:12.854 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:6144 bytes

2019-07-20 23:52:12.856 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:8157 bytes

2019-07-20 23:52:12.862 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件分塊操作....文件大小:0 bytes

2019-07-20 23:52:12.862 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 到達文件內容的末尾,進行相應的文件處理操作....start

2019-07-20 23:52:12.865 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件處理開始....requestParams參數解析:

NETTY WEB Server

===================================

UserName=fortune

pwd=fortune

action=uploadFile

fileContentType=image/jpeg

fileSize=81 KB

getform=POST

Send=Send

2019-07-20 23:52:12.867 [New I/O worker #1] INFO org.fortune.doc.server.handler.factory.DocServerHandlerFactory - 進行文件上傳操作....

2019-07-20 23:52:12.869 [New I/O worker #1] INFO org.fortune.doc.server.handler.UploadDocServerHandler - --srcFileName--psb.jpg

2019-07-20 23:52:12.871 [New I/O worker #1] INFO org.fortune.doc.server.handler.UploadDocServerHandler - 文件上傳成功,保存路徑為:fortune\l\190720235212_1027.jpg,真實路徑為:C:\03_code\idea_workspace\fortune-commons\commons-doc-server-netty\target\commons-doc-server-netty\/fortune\l\190720235212_1027.jpg

2019-07-20 23:52:12.871 [New I/O worker #1] DEBUG org.fortune.doc.server.handler.UploadDocServerHandler - 生成縮略圖

2019-07-20 23:52:12.872 [New I/O worker #1] INFO org.fortune.doc.server.handler.UploadDocServerHandler - 生成縮略圖的名稱為:190720235212_1027_thumb.jpg,路徑為:C:\03_code\idea_workspace\fortune-commons\commons-doc-server-netty\target\commons-doc-server-netty\/fortune\l\190720235212_1027_thumb.jpg

2019-07-20 23:52:13.130 [New I/O worker #1] DEBUG org.fortune.doc.server.handler.factory.DocServerHandlerFactory - 執行結果:{"action":"uploadFile","code":1,"filePath":"fortune\\l\\190720235212_1027.jpg","msg":"文件上傳成功","success":true}

2019-07-20 23:52:13.130 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 文件處理結束....FileServerHandlerFactory處理結果:{"action":"uploadFile","code":1,"filePath":"fortune\\l\\190720235212_1027.jpg","msg":"文件上傳成功","success":true}

2019-07-20 23:52:13.133 [New I/O worker #1] INFO org.fortune.doc.server.support.DocServerHandler - 到達文件內容的末尾,進行相應的文件處理操作....end

再觀察一下netty 代碼中的target目錄下就有上傳的文件了,如下所示:

1460000019853370?w=624&h=249

我們可以通過如下地址進行訪問,

http://localhost:8080/doc-server/fortune//l//190720235212_1027.jpg,關于如何得到圖片地址,則在客戶端工具類中有相應的方法獲取。

4.2 客戶端配置與啟動

4.2.1 配置

客戶端的配置比較簡單,也是采用yml文件方式配置如下:

#上傳成功后,在需要訪問文件web工程中配置以下服務器地址,正確格式為:

# ${host}/${port}/${appName}/${path}

# appName為遠程文件服務app名稱,比如doc-server

# path為文件上傳后服務端返回的文件相對路徑

#http://localhost:8080/doc-server/fortune//p//190629082821_8300.jpg

upload:

server:

port: 9999 #需要配置的是遠程文件服務器netty服務端口號

host: 127.0.0.1

access: #訪問文件

port: 8080 #訪問文件 web 服務端口號

domainName: doc-server #訪問文件 web 服務應用名稱

userName: fortune

password: fortune

需要注意的是,文件訪問的端口號跟遠程文件服務器netty對應的端口是不一樣的,這點需要特別注意。在調用文件服務返回的路徑的時候,需要用到服務端訪問文件的地址,進而訪問相應的文件內容。可通過方法 org.fortune.doc.client.DocClientContainer#getDocServerUrl得到相應的服務端地址,再拼接上返回的相對路徑,即可得到文件的完整地址了。

4.2.2 啟動/調用

客戶端單元測試用例如下:

/**

* @author: landy

* @date: 2019/5/30 23:37

* @description:

*/

@RunWith(SpringJUnit4ClassRunner.class) //調用Spring單元測試類

@ContextConfiguration(classes = {

SettingsConfiguration.class, // common settings configuration

DocClientConfiguration.class,

ApplicationContextHelperConfiguration.class

}) //加載Spring配置文件

public class DocClientTest {

@Test

public void test() {

DocClient.uploadFile(new File("C:\\06_temp\\psb.jpg"), "psb.jpg",false);

}

}

同樣的服務端也會顯示出上述相似的日志信息,

2019-07-21 00:04:26.704 [New I/O worker #4] INFO org.fortune.doc.server.handler.UploadDocServerHandler - --srcFileName--psb.jpg

2019-07-21 00:04:26.705 [New I/O worker #4] INFO org.fortune.doc.server.handler.UploadDocServerHandler - 文件上傳成功,保存路徑為:fortune\w\190721000426_6348.jpg,真實路徑為:C:\03_code\idea_workspace\fortune-commons\commons-doc-server-netty\target\commons-doc-server-netty\/fortune\w\190721000426_6348.jpg

2019-07-21 00:04:26.705 [New I/O worker #4] DEBUG org.fortune.doc.server.handler.factory.DocServerHandlerFactory - 執行結果:{"action":"uploadFile","code":1,"filePath":"fortune\\w\\190721000426_6348.jpg","msg":"文件上傳成功","success":true}

2019-07-21 00:04:26.705 [New I/O worker #4] INFO org.fortune.doc.server.support.DocServerHandler - 文件處理結束....FileServerHandlerFactory處理結果:{"action":"uploadFile","code":1,"filePath":"fortune\\w\\190721000426_6348.jpg","msg":"文件上傳成功","success":true}

2019-07-21 00:04:26.706 [New I/O worker #4] INFO org.fortune.doc.server.support.DocServerHandler - 到達文件內容的末尾,進行相應的文件處理操作....end

2019-07-21 00:05:50.539 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.DispatcherServlet - GET "/doc-server/fortune//w//190721000426_6348.jpg", parameters={}

2019-07-21 00:05:50.540 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.handler.SimpleUrlHandlerMapping - Mapped to org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler@4fcd8ee1

2019-07-21 00:05:50.542 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK

觀察服務端target目錄下,又多了一個文件,顯示如下,

1460000019853371?w=606&h=294

跟日志信息打印出來的路徑是一致的,我們可以通過返回的地址信息進行訪問文件,

1460000019853372?w=1317&h=967

5. 常見問題

5.1 Maven依賴問題

如果某些jar包無法下載的話,可以手動下載然后自己手動執行maven命令安裝到本地倉庫即可(Then, install it using the command)。

mvn install:install-file -DgroupId=com.fasterxml.jackson.core -DartifactId=jackson-core -Dversion=2.9.9.1 -Dpackaging=jar -Dfile=/path/to/file

或者直接利用遠程倉庫地址進行安裝也可( Alternatively, if you host your own repository you can deploy the file there: ),

mvn deploy:deploy-file -DgroupId=com.fasterxml.jackson.core -DartifactId=jackson-core -Dversion=2.9.9.1 -Dpackaging=jar -Dfile=/path/to/file -Durl=[url] -DrepositoryId=[id]

5.2 Tomcat 版本問題

如果使用Tomcat7則會出現以下問題,經過對比發現,需要采用Tomcat8以上版本即可,JDK版本需要為1.8+。

七月 23, 2019 11:25:52 下午 org.apache.catalina.startup.ContextConfig processAnnotationsJar

嚴重: Unable to process Jar entry [module-info.class] from Jar [jar:file:/C:/03_code/idea_workspace/fortune-commons/commons-doc-server-netty/target/commons-doc-server-netty/WEB-INF/lib/asm-7.0.jar!/] for annotations

org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 19

at org.apache.tomcat.util.bcel.classfile.Constant.readConstant(Constant.java:133)

at org.apache.tomcat.util.bcel.classfile.ConstantPool.(ConstantPool.java:60)

at org.apache.tomcat.util.bcel.classfile.ClassParser.readConstantPool(ClassParser.java:209)

at org.apache.tomcat.util.bcel.classfile.ClassParser.parse(ClassParser.java:119)

at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2104)

at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:1980)

at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1946)

at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1931)

at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1325)

at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:878)

at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:369)

at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)

at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)

at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5173)

at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)

at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)

at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)

at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:633)

at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1553)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)

at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)

at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)

at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:622)

at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:569)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)

at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)

at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)

at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)

at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)

at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)

at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)

at java.security.AccessController.doPrivileged(Native Method)

at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1408)

at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)

at sun.rmi.transport.Transport$1.run(Transport.java:200)

at sun.rmi.transport.Transport$1.run(Transport.java:197)

at java.security.AccessController.doPrivileged(Native Method)

at sun.rmi.transport.Transport.serviceCall(Transport.java:196)

at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)

at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)

at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)

at java.security.AccessController.doPrivileged(Native Method)

at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

at java.lang.Thread.run(Thread.java:748)

23:25:53,087 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]

23:25:53,088 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/394457.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/394457.shtml
英文地址,請注明出處:http://en.pswp.cn/news/394457.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode343. 整數拆分(動態規劃)

給定一個正整數 n,將其拆分為至少兩個正整數的和,并使這些整數的乘積最大化。 返回你可以獲得的最大乘積。 示例 1: 輸入: 2 輸出: 1 解釋: 2 1 1, 1 1 1。 解題思路 組成整數兩個數可以進一步拆分,所以可以運用到動態規劃&#xff0c…

愛前端2018全棧大前端_啟動2018年前端工具調查

愛前端2018全棧大前端by Ashley Watson-Nolan通過阿什利沃森-諾蘭 啟動2018年前端工具調查 (Launching the Front-End Tooling Survey 2018) The Front-End Tooling Survey is back to gather input and shed some light on the tooling habits across the web development in…

PHP 小數點保留兩位【轉】

最近在做統計這一塊內容&#xff0c;接觸關于數字的數據比較多&#xff0c; 用到了三個函數來是 數字保留小數后 N 位&#xff1b; 接下來簡單的介紹一下三個函數&#xff1a; 1、number_format echo number_format("5000000")."<br>"; echo number_…

華為杯數學建模2020獲獎名單_我校在2020年全國大學生數學建模競賽中再獲佳績(內附獲獎名單)...

# 近日&#xff0c;從全國大學生數學建模競賽組委會獲悉&#xff0c;我校在2020年全國大學生數學建模競賽中獲得5項國家二等獎。國家獎獲獎數量在全國農林院校中并列排名第二。在黑龍江省高校中位居第三名。1你的喜悅&#xff0c;我們的欣慰# 2020年全國大學生數學建模競賽于9月…

Python 面試總結

公司面試&#xff1a; 1&#xff0c;說說項目都用到了什么技術&#xff1f; 2&#xff0c;mysql索引的種類&#xff1f; 3&#xff0c;索引建多有什么不好&#xff1f; 4&#xff0c;mysql的引擎有什么&#xff1f; 5&#xff0c;redis是單線程還是多線程的? 6, redis的持久化…

醫療器械監管系統總結

算算時間畢業也兩個多月了&#xff0c;心也慢慢塌下來了&#xff0c;少了些許的浮躁&#xff0c;增加了些許的淡定&#xff0c;同時也添加了一些不好的習氣&#xff0c;“混”&#xff0c;混日子的混&#xff0c;生活慢慢的缺少了激情&#xff0c;伴隨著和她漸漸的疏遠。昨天聽…

fn映射 mac 鍵盤_【新鮮評測】高顏值、低延遲、多模式跨平臺辦公神器-米物藍牙鍵盤...

首先&#xff0c;要例行感謝糖紙眾測&#xff0c;感謝麻麻醬給我這次評測機會。大家可以微信搜索【糖紙】小程序免費體驗更多科技產品。小米旗下生態鏈公司米物最近出了一款藍牙雙模鍵盤&#xff0c;它作為一個85全鍵鍵盤&#xff0c;可以完成藍牙和USB轉換&#xff0c;以及PC和…

leetcode413. 等差數列劃分(動態規劃)

數組 A 包含 N 個數&#xff0c;且索引從0開始。數組 A 的一個子數組劃分為數組 (P, Q)&#xff0c;P 與 Q 是整數且滿足 0<P<Q<N 。 如果滿足以下條件&#xff0c;則稱子數組(P, Q)為等差數組&#xff1a; 元素 A[P], A[p 1], …, A[Q - 1], A[Q] 是等差的。并且 …

(轉發)python3用matplotlib繪圖出現中文亂碼的問題

from pandas import Series,DataFrame import matplotlib.pyplot as plt from pylab import mpl mpl.rcParams[font.sans-serif] [SimHei] 字體的選擇如下&#xff1a; 黑體 SimHei微軟雅黑 Microsoft YaHei微軟正黑體 Microsoft JhengHei新宋體 NSimSun新細明體 PMingLiU細明…

小程序 input 換行_小程序 input雙向數據綁定

小程序 雙向綁定數據&#xff08;單個&#xff09;<小程序 雙向綁定數據&#xff08;多個&#xff09;<

平面設計 前端_我如何在5個月內從平面設計師過渡到前端開發人員

平面設計 前端2017 was a bumpy yet exciting year for me. I left my graphic designer job in March, and entered the maze of the coding world. Five months later, I finally got a job as a front-end developer at Tenten.co.2017年對我來說是坎a而又令人興奮的一年。 …

logstash5.x改變

5.x版本 logstash中 elasticsearch插件的workers&#xff0c;無法配置大于1&#xff0c;會提示 This plugin uses the shared and doesnt need this option 這個的意思是進到logstash本身的配置文件pipeline.output.workers。 不要一開始就修改workers個數。這也許是一個誤區。…

Suricata的初始化腳本

見官網 https://suricata.readthedocs.io/en/latest/initscripts.html

jpa在自己創建表的是字段名不一致_用 數據透視表 完成 Excel多表合并

一般情況下&#xff0c;數據透視表只能匯總一個表格中的數據。即使使用多區域匯總&#xff0c;也只能對多表的單列內容進行數據匯總&#xff0c;而多列卻無法實現。前段時間也推送過Power Query的方法&#xff0c;但它有版本限制。而今天宏興會計培訓小編推送的是一個通用的多表…

python根據數據生成圖像_從三個numpy數組生成圖像數據

首先&#xff0c;您應該運行以下代碼&#xff1a;import numpy as npX np.asarray()Y np.asarray()Z np.asarray()Xu np.unique(X)Yu np.unique(Y)然后您可以應用以下任何一種方法。值得注意的是&#xff0c;即使數據沒有被分類(與目前接受的答案相反)&#xff0c;所有這些…

leetcode120. 三角形最小路徑和(動態規劃)

**給定一個三角形&#xff0c;找出自頂向下的最小路徑和。**每一步只能移動到下一行中相鄰的結點上。 相鄰的結點 在這里指的是 下標 與 上一層結點下標 相同或者等于 上一層結點下標 1 的兩個結點。 例如&#xff0c;給定三角形&#xff1a; [ [2], [3,4], [6,5,7], [4,1,…

Bootstrap教程:學習構建第一個Bootstrap 4網站

快速教程&#xff0c;可幫助您快速掌握最新版本的Bootstrap。 (A quick tutorial to get you up to speed with the latest version of Bootstrap.) In my opinion, the best way to learn a new technology is often to start building stuff from day one. This gives a sens…

使用棧實現隊列 Implement Queue using Stacks

為什么80%的碼農都做不了架構師&#xff1f;>>> 問題&#xff1a; Implement the following operations of a queue using stacks. push(x) -- Push element x to the back of queue.pop() -- Removes the element from in front of queue.peek() -- Get the front…

Java利用POI生成Excel強制換行

前一段時間在做一個學校排課系統時&#xff0c;有一個地方需要利用把課程表生成excel匯出給客戶&#xff0c;由于之前用excel都只是簡單的應用&#xff0c;在單元格里都是用自動換行&#xff0c;而這次可能需要用到手動強制換行。 于是我在網上找了一下&#xff0c;網上找到的文…

550什么意思_研報翻譯官第二期:帶你了解什么是CPI

歡迎收看“第二期”研報翻譯官&#xff0c;臨近年末&#xff0c;各類金融研報接踵而至&#xff0c;我們也常會看到GDP、CPI、PPI這類字眼。過年回家跟親戚朋友嘮嗑的時候&#xff0c;如果不扯上幾句CPI或PPI&#xff0c;都顯自己得不夠專業。聽你們吹牛&#xff0c;我炒菜都有勁…