跳至主要內容

WEB开发相关拓展

wangdx大约 28 分钟

项目地址open in new window

数据库连接池

传统 JDBC 问题分析

传统 JDBC 操作

动态 WEB 开发中数据库是最为重要的一项软件技术,很多动态数据的生成都是基于数据库完成加载的,但是在每一次用户请求时都需要每一个处理线程进行数据库驱动加载、数据库连接、数据库操作以及数据库关闭的处理

在每一次用户请求时除了数据库的操作不同之外,实际上对于驱动加载、数据库连接 以及数据库关闭的都是要重复进行的,而这些重复的操作都会带来有性能损耗问题,如果可以将所有数据库的连接对象保存在一个集合之中这样每一次进行数据库操作时都直接通过该集合获取连接对象,而关闭的时候是将连接放回到集合之中,减少数据库打开和关闭所造成的性能损耗。

数据库连接池简介

数据库连接池

数据库连接池(Database Connection Pool),将数据库的连接统一放在一个集合之中进行有效的管理,这样就可以避免重复的连接与关闭所造成的问题,所有的使用者在进行数据库操作之前都要通过这个集合对象获取一个数据库的连接实例,在使用完毕之后还需要将此连接放回到数据库之中,等待其它用户继续使用

数据库连接池与阻塞队列

利用集合存储的数据库连接,可以帮助系统固定数据库的连接数量,这样就可以防止无限制扩充所带来,但是这样一来就会固定数据库连接的个数,如果此时并发用户的访问量高于数据库连接数量时则就会出现无法获取到数据库连接的问题,为了解决此类情况,可以在数据库连接池前追加一个阻塞队列,所有的请求用户根据请求的先后顺序依次获取数据库连接对象,从而提升数据库连接池的性能

数据库连接池实现方式

  • 通过数据库连接池可以有效的提升数据库的处理性能,所以在实际的项目开发中几乎都会使用到此项技术,而对于数据库连接池的实现也有如下两种
    • 基于程序算法实现的连接池:可以通过一些较为成熟的组件,例如:C3P0、Druid 等,在程序代码中进行数据库连接池的实现,这样可以方便的实现项目的部署处理:
    • 基于容器实现的连接池:在 Tomcat、WebLogic、WebSphere 等著名的中间件中都提供有连接池的支持,只需要通过特定的方式启用配置即可。

配置 Tomcat 数据库连接池

Tomcat 数据源

Tomcat 从“4.1“版本之后开始支持连接池的配置,在 Tomcat 中可以直接通过配置的形式实现连接池的定义,当容器启动后会自动根据配置在数据库连接池中提供好若干个数据库连接而这个连接池都被“javax.sql.DataSource”数据源所管理而程序要想获取到此数据源,则必须通过 JNDI 进行查找

配置 Tomcat 数据源

在进行 Tomcat 数据源配置时,一般都是在每一个应用上进行配置,所以可以直接建立一个“META-INF/context.xm”配置文件,而后在此配置文件中定义数据库数据源的相关配置,同时还需要在 web.xml 配置文件中进行配置资源的引用

数据源访问

DataSource 对象查询

客户端如果要想获取到数据库连接池中的连接资源,那么首先就需要获得一个“javax.naming.Context”名称查询上下文接口对象实例,而后通过 lookup()方法查询绑定在 JNDI 结构中的 DataSource 接口实例,当用户获取了 DataSource 接口之后就可以获得对应的 Connection 连接对象,而当用户关闭数据库连接后实际上是将当前的使用连接归还到连接池之中,以便其他线程继续使用

1、

<%@ page pageEncoding="UTF-8" %>
<%@ page import="java.sql.*" %> <%-- 使用原始JDBC的开发包 --%>
<%@ page import="javax.naming.*" %> <%-- JNDI名称查找 --%>
<%@ page import="javax.sql.*" %> <%-- DataSource接口 --%>
<%! // 将所需要的JNDI名称定义为一个常量
    public static final String DATASOURCE_JNDI_NAME = "jdbc/yootk";
%>
<%
    Context context = new InitialContext(); // 初始化名称查找上下文
    // 该操作可以实现任意的资源查找,所以返回的对象类型为Object,自然需要强制性转型
    DataSource dataSource = (DataSource) context.lookup(DATASOURCE_JNDI_NAME);
    Connection connection = dataSource.getConnection(); // 获取数据库连接
%>
<h1><%=connection%></h1>
<%
    connection.close(); // 将数据库的连接归还到连接池之中
%>

2、
<%@ page pageEncoding="UTF-8" %>
<%@ page import="java.sql.*" %> <%-- 使用原始JDBC的开发包 --%>
<%@ page import="javax.naming.*" %> <%-- JNDI名称查找 --%>
<%@ page import="javax.sql.*" %> <%-- DataSource接口 --%>
<%! // 将所需要的JNDI名称定义为一个常量
    public static final String DATASOURCE_JNDI_NAME = "java:comp/env/jdbc/yootk";
%>
<%
    Context context = new InitialContext(); // 初始化名称查找上下文
    // 该操作可以实现任意的资源查找,所以返回的对象类型为Object,自然需要强制性转型
    DataSource dataSource = (DataSource) context.lookup(DATASOURCE_JNDI_NAME);
    Connection connection = dataSource.getConnection(); // 获取数据库连接
%>
<h1><%=connection%></h1>
<%
    connection.close(); // 将数据库的连接归还到连接池之中
%>

https 安全访问

https 简介

HTTP 请求安全问题

HTTP(Hypertexttransfer protocol)协议是 WEB 开发中所使用的核心通讯协议,然而传统的 HTTP 协议全部采用的是明文传输,这样所有的请求数据与回应数据都有可能被泄漏,甚至有恶意的用户也可以伪造 HTTP 请求,这样一来就会造成极大的安全隐患。

https

为了解决 HTTP 通讯中的安全缺陷问题,所以在 HTTP 协议的基础上推出了 HTTPS(Hypertext Transfer Protocol over Secure SocketLayer)传输协议,HTTPS 的安全基础是 SSL(Secure Socket Layer.安全套接字层),这样在每次通讯中都会进行加密数据的传输

SSL 与 TLS

HTTP 报文结构

在 HTTP 通讯协议中,所有请求的数据都是通过明文的形式进行传输,所以 HTTP 报文是由一行行简单的字符串所组成。由于其是纯文本数据内容,所以可以很方便的对其进行数据的读写

HTTP 与 HTTPS 协议模型

然而在通过 HTTPS 协议传输时,由于其使用了 SSL 进行处理所以所传输的内容就全部为加密数据信息,这样一来实际上就是在网络模型中追加了一个安全处理层,以保证数据可以实现正常的加密和解密处理

SSL 与 TLS

  • HTTP 与 HTTPS 之间最大的差别在于缺少了一个 SSL 处理层,而对于 SSL 处理实际上分为 SSL 与 TLS 两种:

    • SSL(Secure Socket Layer,安全套接字层)

      • 位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。SSL 通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL 记录协议和 SSL 握手协议。
    • TLS(Transport Layer Security,传输层安全协议)>用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS 记录协议和 TLS 握手协议

:::tips SSL 是 Netscape 开发的专门用户保护 Web 通讯的,目前版本为 3.0。最新版本的 TLS1.0 是 IETF(工程任务组)制定的一种新的协议,它建立在 SSL3.0 协议规范之上,是 SSL3.0 的后续版本。两者差别极小,可以理解为 SSL3.1,它是写入了 RFC(RequestForComments 互联网文件信息)的,如果没有特殊要求,可以简单的将 SSL 与 TLS 理解为同一协议。 :::

SSL 处理机制

在 SSL 协议中能够进行安全的传输之中都需要对发送和接收的数据进行加密和解密的操作处理,于是这就需要进行握手和记录的操作过程,为此设计两个协议:握手协议(确定加密是否统一)、记录协议(数据处理)

数字证书

数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息,使用的加密算法以及公开密钥的文件。以数字证书为核心的加密技术可以对网络上传输的信息进行加密和解密、数字签名和签名验证,确保网上传递信息的机密性完整性及交易的不可抵赖性。

CA 签发证书

身份认证是建立每一个 SSL 连接不可或缺的部分。比如,一个用户有可能和任何一方建立一个加密的通道,包括攻击者,除非可以确定通信的服务端是可信任的,否则,所有的加密(保密)工作都没有任何作用。而身份认证的方式就是通过证书以数字方式签名的声明,它将公钥与持有相应私钥的主体(个人、设备和服务)身份绑定在一起。通过在证书上签名,CA 可以核实与证书上公钥相应的私钥为证书所指定的主体所拥有。所有的证书由 CA 进行发布,但是证书需要逐层签发,而认证的流程就是签发的反向流程

SSL 握手与加密传输

SSL 安全通讯的关键在于数据的加密与解密,而这就需要获取相应的密钥于是客户端和服务器端在通讯前首先进行建立连接并交换核心参数,随后才可以使用加密通讯,而这样的过程称为握手(Handshake),当握手成功后就可以基于 SSL 实现加密数据传输

OpenSSL

OpenSSl

在实际的项目应用环境中,如果要想使用 HTTPS 安全通讯,那么一般都需要通过 CA(Certificate Authority、证书授权中心)进行证书申请与签发处理,但是在互联网上的 SSL 证书都是需要支付一定的服务费用,如果说现在是在一个小型的局域网内或者是个人爱好者想进行一些实验的话,就可以通过 OpenSSL 工具创建一个属于自己的私有 CA。

OpenSSL

  • OpenSSL 是目前最流行的 SSL 密码库工具,其提供了一个通用、健壮、功能完备的工具套件,用以支持 SSL/TLS 协议的实现。OpenSSL 整个软件包大概可以分成三个主要的功能部分:SSL 协议库 libssl、应用程序命令工具以及加密算法库 libcrypto。在 OpenSSL 处理中提供有如下三种数据的加密模式:

    • 对称加密:指加密和解密使用相同密钥的加密算法。对称加密算法的优点在于加解密的高速度和使用长密钥时的难破解性。常见的对称加密算法:DES3DES、DESX、AES、RC4、RC5、RC6 等:
    • 非对称加密:指加密和解密使用不同密钥的加密算法,也称为公私钥加密常见的非对称加密算法:RSA、DSA(数字签名用)等;Hash 算法:
    • Hash 算法它是一种单向算法,用户可以通过 Hash 算法对目标信息生成一段特定长度的唯一的 Hash 值,却不能通过这个 Hash 值逆向获得目标信息。”常见的 Hash 算法:MD2、MD4、MD5、SHA、SHA-1 等

证书管理

在对称加密中,通讯双方都各自拥有彼此之间的公钥信息,在每次进行数据传输前都会进行密钥的匹配,匹配成功后才可以获取到正确的数据项。每一次通过 CA 申请证书时都会生成一个,其中公钥是全网公开的信息,而所有密钥对(公钥和私钥)的通讯双方只有获取到公钥后才可以实现双方的数据加密和解密。私钥是每个企业自己管理的,当企业的私钥丢失后则需要向 CA 机构发出失效申请后实现证书吊销

证书签发

1、openssl version

2、192.168.190.128	muyan-yootk.com

3、mkdir -p /usr/local/muyan/{server,client}

4、tree /usr/local/muyan/

5、openssl genrsa -out /usr/local/muyan/cakey.pem 1024

6、openssl req -new -key /usr/local/muyan/cakey.pem -out /usr/local/muyan/cacert.csr -subj /CN=muyan-yootk.com

7、openssl x509 -req -days 3650 -sha1 -extensions v3_ca -signkey /usr/local/muyan/cakey.pem -in /usr/local/muyan/cacert.csr -out /usr/local/muyan/ca.cer

8、tree /usr/local/muyan/

9、openssl genrsa -aes256 -out /usr/local/muyan/server/server-key.pem 1024

10、openssl req -new -key /usr/local/muyan/server/server-key.pem -out /usr/local/muyan/server/server.csr -subj /CN=muyan-yootk.com

11、openssl x509 -req -days 3650 -sha1 -extensions v3_req -CA /usr/local/muyan/ca.cer -CAkey /usr/local/muyan/cakey.pem -CAserial /usr/local/muyan/server/ca.srl -CAcreateserial -in /usr/local/muyan/server/server.csr -out /usr/local/muyan/server/server.cer

12、openssl genrsa -aes256 -out /usr/local/muyan/client/client-key.pem 1024

13、openssl req -new -key /usr/local/muyan/client/client-key.pem -out /usr/local/muyan/client/client.csr -subj /CN=muyan-yootk.com

14、openssl pkcs12 -export -clcerts -name muyan-client -inkey /usr/local/muyan/client/client-key.pem -in /usr/local/muyan/client/client.cer -out /usr/local/muyan/client/client.p12

CA 证书申请流程

如果要想通过 OpenSSL 进行证书的签发,则首先需要创建一个 CA 根证书,而后利用这个根证书签发服务端(server)以及客户端(client)两个子证书

配置准备
1、【Windows】https证书一般都与指定的域名绑定在一起本次将在Windows系统中模拟一个“muyan-yootk.com”域名信息,直接在
“C:\Windows\System32\drivers\etc\hosts”配置文件中增加以下配置项,其中“192.168.190.128”是要进行服务配置的Linux主机地址。

  192.168.190.128muyan-yootk.com
2、【Linux】生成的证书需要进行本地存储,为了便于管理可以创建一个"/usr/local/muyan”的证书存储目录,并且在其下提供有两个子目录:server(服务端证书)、client(客广端证书)
mkdir -p /usr/osa/muyan/{server.client}

根证书
3、【Linux】生成一个长度为1024的CA密钥对
openssl genrsa -out /usr/local/muyan/cakey.pem 1024
4、【Linux】生成根证书签发申请:
openssl reg -new -key /usr/local/muyan/cakey.pem -out/usr/local/muyan/cacert.csr-subj/CN=muyan-yootk.com
5、【Linux】使用x509格式标准创建根证书签发,有效期为十年(3650天)
openssl x509 -reg -days 3650 -sha1 -extensions v3 ca -signkey /usr/local/muyan/cakey.pem -in /usr/local/muyan/cacert.csr -out/usr/local/muyan/ca.cer

服务端证书
6、【Linux】根证书创建完成后就可以进行服务端证书创建,首先生成服务器私钥,在私钥生成时需要设置一个服务端密钥的访问密码本次设置为“yootk.com”
openssl genrsa -aes256 -out /usr/local/muyan/server/server-key.pem
7、【Linux】生成服务器证书签发申请
openssl reg -new -key /usr/local/muyan/server/server-key.pem -out/usr/local/muyan/server/server.csr-subj/CN=muyan-yootk.com
8、【Linux】服务端证书签发
openssl x509 -reg -days 3650 -sha1 -extensions v3 reg -CA/usr/local/muyan/ca.cer -CAkey /usr/local/muyan/cakey.pem -CAserial /usr/local/muyan/server/ca.sr -CAcreateserial -in/usr/local/muyan/server/server.csr -out/usr/local/muyan/server/server.cer

客户端证书
9、【Linux】生成客户端私钥,随后需要设置一个私钥访问密码本次设置为“yootk.com'
openssl genrsa -aes256 -out /usr/local/muyan/client/client-key.pem1024
10、【Linux】生成客户端签发申请
openssl reg -new -key/usr/local/muyan/client/client-key.pem -out/usr/local/muyan/client/client.csr -subj/CN=muyan-yootk.com
11、【Linux】客户端证书签发
openssl x509 -reg -days 365 -sha1 -CA /usr/local/muyan/ca.cer -CAkey /usr/local/muyan/cakey.pem -CAserial/usr/local/muyan/server/ca.srl -in /usr/local/muyan/client/client.csrout /usr/local/muyan/client/client.cer

P12格式证书
12、【Linux】此时生成了一套标准的公共证书,但是如果要想将此证书在Java中使用,则建议生成一个p12(PublicKeyCryptography Standards #12,PKCS#12、公钥加密技术12号标准)证书(P12证书=CER证书 +密钥,可以防止证书丢失问题)
首先需要生成一个客户端的p12格式证书
openssl pkcs12 -export -clcerts -name muyan-client -inkey/usr/local/muyan/client/client-key.pem -in/usr/local/muyan/client/client.cer -out/usr/local/muyan/client/client.p12
13、【Linux】生成服务器端p12证书:
openssl pkcs12 -export -clcerts -name muyan-server -inkey/usr/local/muyan/server/server-key.pem -in/usr/local/muyan/server/server.cer -out/usr/local/muyan/server/server.p12

Tomcat 配置 https 证书

1、vi /usr/local/tomcat/conf/server.xml

2、
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150"
 scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" SSLEnabled="true" defaultSSLHostConfigName="muyan-yootk.com">
	<SSLHostConfig hostName="muyan-yootk.com">
		<Certificate certificateKeystoreFile="/usr/local/muyan/server/server.p12" certificateKeystorePassword="yootk.com"/>
	</SSLHostConfig>

</Connector>

3、vi /usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml

4、
<login-config> 					<!-- SSL认证授权配置 -->
	<auth-method>CLIENT-CERT</auth-method>
	<realm-name>Client Cert Users-only Area</realm-name>
</login-config>
<security-constraint>					<!-- SSL认证授权配置 -->    <web-resource-collection>
	<web-resource-name>SSL</web-resource-name>
		<url-pattern>/*</url-pattern>
	</web-resource-collection>
	<user-data-constraint>
		<transport-guarantee>CONFIDENTIAL</transport-guarantee>
	</user-data-constraint>
</security-constraint>

5、rm -rf /usr/local/tomcat/logs/*

6、netstat -nptl

7、firewall-cmd --zone=public --add-port=443/tcp --permanent

8、firewall-cmd --reload

9、https://muyan-yootk.com/

HttpClient 工具包

使用 Java 原生支持访问 WEB 程序

WEB 程序访问

所有的 WEB 应用程序都是基于 HTTP 协议的,这样一来除了可以通过浏览器使用之外,也可以通过 Java 原生程序进行 WEB 服务的访问

1package com.yootk.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/echo.action")
public class EchoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        String param = req.getParameter("msg"); // 参数名称为msg
        resp.getWriter().println("<h1>" + param + "</h1>");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

2、http://localhost/echo.action?msg=www.yootk.com

3package com.yootk.test;

import java.net.URL;
import java.util.Scanner;

public class AccessEchoServer {
    public static void main(String[] args) throws Exception {
        String requestUrl = "http://localhost/echo.action?msg=www.yootk.com";
        URL url = new URL(requestUrl); // 要发出GET请求
        // 当进行请求的发送之后,肯定需要有HTML的响应内容,所以需要打开输入流接收全部的数据
        Scanner scanner = new Scanner(url.openStream());
        scanner.useDelimiter("\n");
        while(scanner.hasNext()) {
            System.out.println(scanner.next());
        }
        scanner.close();
    }
}

HttpClient 基本使用

下载 HttpClient 组件

HttpClient 是 Apache HttpComponent 提供的开源项目,通过此组件可以提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议,如果要想获取 HttpClient 组件可以登录“http://hc.apache.org/”进行下载

1、http://hc.apache.org/

2package com.yootk.test.hc;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.io.entity.EntityUtils;

public class HttpClientGetDemo {
    public static void main(String[] args) throws Exception {
        String requestUrl = "http://localhost/echo.action?msg=www.yootk.com"; // 请求路径
        // 1、创建一个可以被关闭的HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 2、创建一个指定的请求模式,本次为GET模式
        HttpGet httpGet = new HttpGet(requestUrl); // 以GET模式发送HTTP请求
        // 3、进行HTTP请求的发送,请求发送之后一定会存在有响应信息
        CloseableHttpResponse response = httpClient.execute(httpGet); // 发送HTTP请求
        // 4、HTTP响应是分为两个部分:数据部分、头信息
        System.out.println("【获取响应数据】" + EntityUtils.toString(response.getEntity()));
        // 5、获取全部的头信息数据
        Header headers [] = response.getHeaders(); // 全部头信息
        for (Header header : headers) {
            System.out.println("\t|- 【头信息】name = " + header.getName() + "、value = " + header.getValue());
        }
        System.out.println("【HTTP状态码】" + response.getCode());
    }
}

3package com.yootk.test.hc;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicNameValuePair;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

public class HttpClientPostDemo {
    public static void main(String[] args) throws Exception {
        String requestUrl = "http://localhost/echo.action"; // 请求路径
        // 1、创建一个可以被关闭的HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 2、创建一个指定的请求模式,本次为POST模式
        HttpPost httpPost = new HttpPost(requestUrl); // 以POST模式发送HTTP请求
        // 3、如果要想进行POST请求发送,那么一定要进行请求参数的配置,需要配置集合
        List<NameValuePair> allParams = new ArrayList<>(); // 通过集合保存提交参数
        allParams.add(new BasicNameValuePair("msg", "沐言科技:www.yootk.com")); // 添加一个参数
        // 4、既然要通过POST发送请求,那么就需要进行编码的配置
        UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(allParams, Charset.forName("UTF-8"));
        // 5、将请求参数配置到POST请求之中
        httpPost.setEntity(urlEncodedFormEntity); // 所有的提交参数准备就绪
        // 6、进行HTTP请求的发送,请求发送之后一定会存在有响应信息
        CloseableHttpResponse response = httpClient.execute(httpPost); // 发送HTTP请求
        // 7、HTTP响应是分为两个部分:数据部分、头信息
        System.out.println("【获取响应数据】" + EntityUtils.toString(response.getEntity()));
        // 8、获取全部的头信息数据
        Header headers [] = response.getHeaders(); // 全部头信息
        for (Header header : headers) {
            System.out.println("\t|- 【头信息】name = " + header.getName() + "、value = " + header.getValue());
        }
        System.out.println("【HTTP状态码】" + response.getCode());
    }
}
package com.yix.test;

import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.apache.hc.core5.http.message.BasicNameValuePair;

import java.nio.charset.Charset;
import java.util.Arrays;

/**
 * @author wangdx
 */
public class HttpClientGetDemo {
    public static void main(String[] args) throws Exception {
        String requestUrl = "http://localhost:8080/echo.action?msg=www.yootk.com"; // 请求路径
        // 1、创建一个可以被关闭的HttpClient对象
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            ClassicHttpRequest httpGet = ClassicRequestBuilder.get(requestUrl).build();
            httpclient.execute(httpGet, response -> {
                System.out.println(response.getCode() + " " + response.getReasonPhrase());
                // 4、HTTP响应是分为两个部分:数据部分、头信息
                System.out.println("【获取响应数据】" + EntityUtils.toString(response.getEntity()));
                // 5、获取全部的头信息数据
                Header headers[] = response.getHeaders(); // 全部头信息
                for (Header header : headers) {
                    System.out.println("\t|- 【头信息】name = " + header.getName() + "、value = " + header.getValue());
                }
                System.out.println("【HTTP状态码】" + response.getCode());
                return null;
            });

            ClassicHttpRequest httpPost = ClassicRequestBuilder.post(requestUrl)
                    .setEntity(new UrlEncodedFormEntity(Arrays.asList(
                            new BasicNameValuePair("username", "vip"),
                            new BasicNameValuePair("msg", "沐言科技:www.yootk.com")), Charset.forName("UTF-8")))
                    .build();
            httpclient.execute(httpPost, response -> {
                // 7、HTTP响应是分为两个部分:数据部分、头信息
                System.out.println("【获取响应数据】" + EntityUtils.toString(response.getEntity()));
                // 8、获取全部的头信息数据
                Header headers[] = response.getHeaders(); // 全部头信息
                for (Header header : headers) {
                    System.out.println("\t|- 【头信息】name = " + header.getName() + "、value = " + header.getValue());
                }
                System.out.println("【HTTP状态码】" + response.getCode());
                return null;
            });
        }
    }
}

使用 HttpClient 上传文件

HttpClient 上传文件

使用 HttpClient 组件除了可以实现 HTTP 服务调用之外,也可以模拟表单实现二进制文件的上传处理,在服务端进行请求处理时可以直接利用 FileUpload 组件实现上传数据的接收。

1、

package com.yootk.servlet;

import com.yootk.common.util.ParameterUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/upload.action")
public class UploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        ParameterUtil parameterUtil = new ParameterUtil(req);
        String param = parameterUtil.getParameter("message"); // 参数名称为msg
        resp.getWriter().println("<h1>message = " + param + "</h1>");
        resp.getWriter().println("<h1>photo = " + parameterUtil.getParameter("photo") + "</h1>");
    }
}


2、

package com.yootk.test.hc;

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.entity.mime.FileBody;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.entity.mime.StringBody;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicNameValuePair;

import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

public class HttpClientUploadDemo {
    public static void main(String[] args) throws Exception {
        String requestUrl = "http://localhost/upload.action"; // 请求路径
        File file = new File("H:" + File.separator + "muyan_yootk.png"); // 要上传的文件
        // 1、创建一个可以被关闭的HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 2、创建一个指定的请求模式,本次为POST模式
        HttpPost httpPost = new HttpPost(requestUrl); // 以POST模式发送HTTP请求
        // 3、如果要是进行表单上传,则一定要提供有一个MIME的声明“multipart/form-data”
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.addPart("message", new StringBody("沐言科技:www.yootk.com", ContentType.create("text/plain", Charset.forName("UTF-8"))));
        builder.addPart("photo", new FileBody(file, ContentType.create("image/png"), file.getName()));
        // 4、将请求参数配置到POST请求之中
        httpPost.setEntity(builder.build()); // 所有的提交参数准备就绪
        // 6、进行HTTP请求的发送,请求发送之后一定会存在有响应信息
        CloseableHttpResponse response = httpClient.execute(httpPost); // 发送HTTP请求
        // 7、HTTP响应是分为两个部分:数据部分、头信息
        System.out.println("【获取响应数据】" + EntityUtils.toString(response.getEntity()));
        // 8、获取全部的头信息数据
        Header headers [] = response.getHeaders(); // 全部头信息
        for (Header header : headers) {
            System.out.println("\t|- 【头信息】name = " + header.getName() + "、value = " + header.getValue());
        }
        System.out.println("【HTTP状态码】" + response.getCode());
    }
}

//中文乱码传参更改
String message = "沐言科技:www.yootk.com";
String encodeValue = URLEncoder.encode(message, Charset.forName("UTF-8")); // 编码操作
System.out.println("【编码结果】" + encodeValue);
String value = URLDecoder.decode(encodeValue, Charset.forName("UTF-8")); // 解码操作
System.out.println("【解码结果】" + value);

https 协议访问

1、https://muyan-yootk.com/yootk/echo.action?msg=www.yootk.com

2、
package com.yootk.test.hc;

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.mime.FileBody;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.entity.mime.StringBody;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.ssl.TLS;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.ssl.TrustStrategy;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class HttpClientHTTPSDemo {
    public static void main(String[] args) throws Exception {
        // 1、当前的访问协议是HTTPS,所以需要做一个SSL上下文处理
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                X509Certificate cert = chain[0];
                return "CN=muyan-yootk.com".equalsIgnoreCase(cert.getSubjectDN().getName());
            }
        }).build();
        // 2、创建SSL连接工厂类
        SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create().setSslContext(sslContext).setTlsVersions(TLS.V_1_2).build();
        // 3、创建HttpClient连接管理类
        HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(sslSocketFactory).build();
        // 4、创建Http请求
        CloseableHttpClient httpClient =HttpClients.custom().setConnectionManager(connectionManager).build();
        // 5、设置请求路径
        String requestUrl = "https://muyan-yootk.com/yootk/upload.action";
        // 6、设置上传文件
        File file = new File("H:" + File.separator + "muyan_yootk.png"); // 要上传的文件
        // 7、创建一个指定的请求模式,本次为POST模式
        HttpPost httpPost = new HttpPost(requestUrl); // 以POST模式发送HTTP请求
        // 8、如果要是进行表单上传,则一定要提供有一个MIME的声明“multipart/form-data”
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.addPart("message", new StringBody("沐言科技:www.yootk.com", ContentType.create("text/plain", Charset.forName("UTF-8"))));
        builder.addPart("photo", new FileBody(file, ContentType.create("image/png"), file.getName()));
        // 9、将请求参数配置到POST请求之中
        httpPost.setEntity(builder.build()); // 所有的提交参数准备就绪
        // 10、进行HTTP请求的发送,请求发送之后一定会存在有响应信息
        CloseableHttpResponse response = httpClient.execute(httpPost); // 发送HTTP请求
        // 11、HTTP响应是分为两个部分:数据部分、头信息
        System.out.println("【获取响应数据】" + EntityUtils.toString(response.getEntity()));
        // 12、获取全部的头信息数据
        Header headers [] = response.getHeaders(); // 全部头信息
        for (Header header : headers) {
            System.out.println("\t|- 【头信息】name = " + header.getName() + "、value = " + header.getValue());
        }
        System.out.println("【HTTP状态码】" + response.getCode());
    }
}

FTP 通讯

FTP 简介

FTP 协议

FTP(File Transfer Protocol、文件传输协议)是 TCP/IP 协议中的一部分,属于应用层协议。使用 FTP 最主要的功能是对文件进行管理,所以在 FTP 内部对于文件支持有两种传输模式:文本模式(ASCI、默认)和二进制模式(Binary),通常文本文件使用 ASCI 模式,而对于图片、视频,声音、压缩等文件则会使用二进制的方式进行传输。

FTP 操作架构

在 FTP 协议中一般分为两个组成部分:FTP 服务器、FTP 客户端利用 FTP 服务器可以方便的实现文件的资源存储,而通过 FTP 客户端可以访问 FTP 服务器上存储的资源(上传、下载或者删除)

FTP 工作模式

  • 如果要进行 FTP 文件的管理,则客户端一定要与 FTP 服务器进行连接,在 FTP 中每一次通讯实际上都需要有两个连接存在,一个连接专门用于传输 FTP 命令、另外一个连接负责数据传送,所以在 FTP 中一般会支持两种不同的工作模式:一种是 Standard 模式(也被称为“PORT”模式),另外一种是 Passive(也被称为“PASV”模式),这两种模式的概念如下:

    • PORT 主动模式:当客户端与服务端连接后,客户端会打开一个新的本地端口,随后将此端口告诉给 FTP 服务端,这样 FTP 服务端就会主动的连接到 FTP 客户端公布的端口,随后进行数据传送。

    • PASV 被动模式:FTP 在定义的时候就公布了一个操作端口一般为 21 端口,这样当客户端连接之后会明确的知道该操作端口并且进行数据传送。

连接 FTP 服务器

FTPClient 工具类

为了便于开发者进行 FTP 服务操作,可以直接通过 ApacheCommons 子项目中提供的“commons-net"开发包中提供组件完成所需要功能的开发,如果要想进行 FP 的服务器连接则可以直接使用 FTPClient 程序类,在此类中提供了数据库连接用户登录以及获取状态码的操作方法

FTP 文件上传

FTP 文件上传

使用 FTP 最重要的一点就是可以通过 FTP 实现文件的保存,开发者可以根据自己的需要将文件上传到 FTP 服务器之中,但是在上传前雪要确认好上传文件的存储路径,如果该路径不存在则首先进行目录创建后再实现文件保存

1package com.yootk.test.ftp;

import com.yootk.common.util.ftp.FTPConnection;
import org.apache.commons.net.ftp.FTPClient;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class FTPUploadFile {
    public static void main(String[] args) throws Exception {
        String ftpPath = "/var/ftp/yootk/"; // 此目录为Linux已经存在的路径,但是子路径不存在
        FTPClient client = FTPConnection.getFTPClient(); // 获取FTP连接
        client.enterLocalPassiveMode(); // 采用被动模式
        client.setFileType(FTPClient.BINARY_FILE_TYPE); // 采用二进制传输
        client.setBufferSize(2048); // 设置上传的缓存大小
        if (!client.changeWorkingDirectory(ftpPath)) {  // 切换目录
            client.makeDirectory(ftpPath); // 目录的创建
        }
        File file = new File("h:" + File.separator + "muyan_yootk.png"); // 定义上传文件
        // 所有的文件是以二进制数据流的形式进行传输的,所以一定要通过InputStream获取一个输入流
        InputStream input = new FileInputStream(file); // 文件输入流
        String tempName = ftpPath + file.getName(); // FTP文件保存名称
        String ftpFileName = new String(tempName.getBytes("UTF-8"), "ISO-8859-1");
        // 在进行文件保存的需要明确配置存储文件的名称,同时利用输入流进行数据的传输
        if (client.storeFile(ftpFileName, input)) {
            System.out.println("FTP文件上传成功!");
        } else {
            System.out.println("FTP文件上传失败!");
        }
        input.close(); // 传输完成后关闭输入流
        client.logout(); // 注销
    }
}

2、

tree /srv/ftp/

FTP 文件下载

1package com.yootk.test.ftp;

import com.yootk.common.util.ftp.FTPConnection;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.*;

public class FTPDownloadFile {
    public static void main(String[] args) throws Exception {
        String ftpPath = "/var/ftp/yootk/"; // 此目录为Linux已经存在的路径,但是子路径不存在
        String fileName = "muyan_yootk.png"; // FTP文件名称
        FTPClient client = FTPConnection.getFTPClient(); // 获取FTP连接
        client.enterLocalPassiveMode(); // 采用被动模式
        client.setFileType(FTPClient.BINARY_FILE_TYPE); // 采用二进制传输
        client.setBufferSize(2048); // 设置上传的缓存大小
        if (client.changeWorkingDirectory(ftpPath)) { // 切换到要下载的FTP目录
            FTPFile[] files = client.listFiles(); // 获取全部的文件列表
            for (FTPFile file : files) {    // 迭代当前目录中的所有文件
                if (file.getName().trim().equalsIgnoreCase(fileName)) { // 找到了匹配的文件名称
                    // 为了可以区分出下载文件,本次进行一个强制性的命名
                    File downFile = new File("h:" + File.separator + "yootk_download.png");
                    // 要通过FTP下载文件,是需要利用输出流进行输出写入的
                    OutputStream output = new FileOutputStream(downFile); // 获取文件流
                    boolean flag = client.retrieveFile(new String(file.getName().getBytes("UTF-8"), "ISO-8859-1"), output);
                    output.flush();
                    output.close();
                    System.out.println(flag ? "文件下载成功!" : "文件下载失败!");
                }
            }
        }
        client.logout(); // 注销
    }
}


2package com.yootk.test.ftp;

import com.yootk.common.util.ftp.FTPConnection;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.*;

public class FTPDownloadFile {
    public static void main(String[] args) throws Exception {
        String ftpPath = "/var/ftp/yootk/"; // 此目录为Linux已经存在的路径,但是子路径不存在
        String fileName = "muyan_yootk.png"; // FTP文件名称
        FTPClient client = FTPConnection.getFTPClient(); // 获取FTP连接
        client.enterLocalPassiveMode(); // 采用被动模式
        client.setFileType(FTPClient.BINARY_FILE_TYPE); // 采用二进制传输
        client.setBufferSize(2048); // 设置上传的缓存大小
        if (client.changeWorkingDirectory(ftpPath)) { // 切换到要下载的FTP目录
            FTPFile[] files = client.listFiles(); // 获取全部的文件列表
            for (FTPFile file : files) {    // 迭代当前目录中的所有文件
                if (file.getName().trim().equalsIgnoreCase(fileName)) { // 找到了匹配的文件名称
                    // 为了可以区分出下载文件,本次进行一个强制性的命名
                    File downFile = new File("h:" + File.separator + "yootk_download.png");
                    // 要通过FTP下载文件,是需要利用输出流进行输出写入的
                    OutputStream output = new FileOutputStream(downFile); // 获取文件流
                    boolean flag = client.retrieveFile(new String(file.getName().getBytes("UTF-8"), "ISO-8859-1"), output);
                    output.flush();
                    output.close();
                    System.out.println(flag ? "文件下载成功!" : "文件下载失败!");
                    client.deleteFile(new String(file.getName().getBytes("UTF-8"), "ISO-8859-1"));
                }
            }
        }
        client.logout(); // 注销
    }
}


FTP 文件移动

FTP 文件移动

FTP 服务器长期使用后就会存在有大量的文件内容,为了便于归类管理,往往会将部分文件进行移动管理,而 FTPClient 可以直接实现文件的移动管理,只要通过重命名的方式即可实现

package com.yootk.test.ftp;

import com.yootk.common.util.ftp.FTPConnection;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class FTPMoveFile {
    public static void main(String[] args) throws Exception {
        String oldPath = "/var/ftp/yootk/"; // 文件存储的原始目录,该目录存在
        String newPath = "/var/ftp/muyan/"; // 文件存储的目标目录,该目录不存在
        String fileName = "muyan_yootk.png"; // 要移动的文件名称
        FTPClient client = FTPConnection.getFTPClient(); // 获取FTP连接
        client.enterLocalPassiveMode(); // 采用被动模式
        client.setFileType(FTPClient.BINARY_FILE_TYPE); // 采用二进制传输
        client.setBufferSize(2048); // 设置上传的缓存大小
        if (!client.changeWorkingDirectory(newPath)) {  // 目标的路径不存在
            client.makeDirectory(newPath); // 创建目标目录
        }
        // 如果要想移动文件,则一定要切换到移动的原始目录之中
        if (client.changeWorkingDirectory(oldPath)) {   // 移动的源目录存在
            FTPFile[] files = client.listFiles(); // 获取全部的目录文件
            for (FTPFile file : files) {    // 文件的列表
                if (file.getName().trim().equals(fileName)) {  // 可以查找到当前文件
                    client.rename(new String(file.getName().getBytes("UTF-8"), "ISO-8859-1"), newPath + new String(file.getName().getBytes("UTF-8"), "ISO-8859-1"));
                }
            }
        }

        client.logout(); // 注销
    }
}

JMeter 压力测试工具

压力测试简介

压力测试

软件项目的执行性能取决于开发者的技术实力以及良好的硬件支持,而为了保证程序的执行稳定往往就需要在其上线正式提供服务前就可以准确的预估出服务器可以承受的最大处理峰值而这就需要借助一些压力测试工具来获取准确的数据,而 Apache 提供的 JMeter 工具就可以很好的实现压力测试的功能开发者可以通过“http://jmeter.apache.org”免费获取JMeter工具包

JMemter 启动界面

JMeter 是一个使用 Java 开发的绿色软件(需要配置好 JAVA HOME 环境属性),开发者只需要解压缩后就可以直接通过“bin/imeter.sh”程序文件即可直接启动

数据库压力测试

配置测试线程组

在此配置项中有如下几个重要选项

  • 线程数:模仿用户并发访问的线程数量
  • Ram-Up 时间(秒):设置这些线程的执行完成时间。如果此时设置了 20 个线程,并且设置 5 秒内完成线程启动,则相当于每秒要启动 4 个线程
  • 循环次数:每个线程的重复执行次数;

WEB 服务压力测试

demo


上次编辑于: