基础知识
一、网站架构及其演变过程
三大模型架构
- 单体应用
- C/S架构
- 灵活性和安全性较高,如QQ、WPS
- 业务分别拆分到客户端(安全性和稳定性较差)和服务端(安全性/稳定性较好且易升级,但会增大服务器的负担)实现
- B/S架构
- 服务端又可以拆分成服务端程序和应用端程序
- 服务端程序负责统一处理数据连接、封装、解析等工作,如Tomcat服务器
- 应用端程序专注与业务处理逻辑以及与数据端的交互
- 相比C/S架构,开发得到简化
浅谈B/S架构
- TCP/IP模型
- 四层架构模型(接入层<对应OSI的物理层和链路层>、网络层、传输层、应用层)
- 网络层–>IP协议;传输层–>TCP协议;应用层–>HTTP协议
- OSI标准参考模型
- 七层模型(物链网输会式应)
- 常用协议补充
- 网络层:IPV4、IPV6…以及常见路由协议
- 传输层:TCP、UDP(直播等可靠性要求不高的应用场景下使用)
- 应用层:DNS、FTP、SMTP、POP…
- B/S架构的演变过程(海量数据引起的变革)
- 缓存
- 降低数据库的访问压力
- 根据场景建立空缓存也是一种有效的方法
- 缓存失效机制(定期失效…)
- 程序实现:Map–>ConcurrentHashMap
- 框架实现:Redis、Memcache…
- 页面静态化
- 数据库优化
- 表结构优化
- SQL语句优化(语法层面、业务逻辑层面、配合索引和缓存)
- 分区
- 分表
- 索引优化
- 使用存储过程代替程序操作
- 分离活跃数据(分表)
- 批量读取
- 延迟修改
- 读写分离
- 搭建数据库服务器集群
- 主数据库接受写请求,并承担同步到从数据库的任务
- 从数据库承担读请求
- 分布式数据库
- NoSQL和Hadoop
- 高并发解决方案
- 应用和静态资源分离
- 页面缓存
- 集群和分布式
- 反向代理
- 代理服务器(不透明、不需要域名)
- 反向代理服务器(透明、需要域名)
- CDN
- 全国节点搭建
- 二级DNS分配访问从缓存服务器
- 从缓存服务器没有资源则访问主服务器
- 如各类通信运营商的节点访问方案
- 底层优化
二、常见协议和标准
DNS
- DNS服务器(多级结构:根->权限->多级)
- hosts文件(比本地域名服务器优先级还高的解析)
C:\windows\system32\drivers\etc\hosts
nslookup www.baidu.com
TCP/IP
- IP协议
- TCP协议
- 负责可靠传输
- 三次握手
- 四次挥手
- SYN泛洪攻击(客户端利用不发送第三次握手使服务器重发第二次握手,消耗服务器资源)
- 通常应用于网页、邮件等服务
- UDP协议
HTTP
- 请求报文和响应报文
- 首行、头部、主体
- 请求方法(GET、HEAD、POST、DELETE)
- 响应状态码
- 1XX(信息性状态码)
- 2XX(成功状态码)
- 3XX(重定向状态码)
- 4XX(客户端错误)
- 5XX (服务端错误)
Servlet
- 对HTTP接收到的数据处理并产生要返回给客户端的结果
MVC基本概念
- Model:模型层(处理数据)
- 实体类Beans,存储业务数据
- 业务处理Beans(Service,Dao),实现业务逻辑和数据访问
- View:视图层,与用户交互,展示数据
- Controller:Servlet,接收请求和响应浏览器
- 补充三层架构:表示层、业务逻辑层、数据访问层
MVC工作流程
- 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收
- Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller
- Controller再根据请求处理的结果
找到相应的View视图,渲染数据后最终响应给浏览器
三、Java Socket
Socket用法
- ServerSocket创建一个服务器监听对象,并使用.accept()返回一个Socket对象进行通信
- Socket对象进行通信,可以通过.get()获取输入输出流
- 通过Reader和Writer对流进行读写
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try{
ServerSocket server = new ServerSocket(8080);
Socket socket = server.accept();
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = is.readLine();
System.out.println("received from client: "+line);
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.println("received data: "+line);
pw.flush();
pw.close();
is.close();
socket.close();
server.close();
}catch (Exception e)
{
e.printStackTrace();
}
}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
String msg = "Client Data.";
try {
Socket socket = new Socket("127.0.0.1",8080);
PrintWriter pw = new PrintWriter(socket.getOutputStream());
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw.println(msg);
pw.flush();
String line = is.readLine();
System.out.println("received from server: "+line);
pw.close();
is.close();
socket.close();
}catch (Exception e)
{
e.printStackTrace();
}
}
}
NioSocket用法
- Nio(New IO)是JDK1.4之后重写的更高效的IO
- 提供ServerSocketChannel与SocketChannel分别对应于原来的ServerSocket和Socket
- Buffer:担任货物的角色
- Channel:担任送货员的角色
- Selector:担任分拣员的角色
- Selection.Key.OP_ACCEPT
- Selection.Key.OP_CONNECT
- Selection.Key.OP_READ
- Selection.Key.OP_WRITE
- SelectionKey
- 保存处理当前请求的Channel和Selector
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
public class NIOServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
Handler handler = new Handler(1024);
while(true)
{
if(selector.select(3000)==0){
System.out.println("等待请求超时...");
continue;
}
System.out.println("处理请求...");
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while(keyIter.hasNext()){
SelectionKey key = keyIter.next();
try {
if(key.isAcceptable()){
handler.handleAccept(key);
}
if(key.isReadable())
{
handler.handlerRead(key);
}
}catch (IOException e){
keyIter.remove();
continue;
}
keyIter.remove();
}
}
}
private static class Handler {
private int bufferSize = 1024;
private String localCharset = "UTF-8";
public Handler(){}
public Handler(int bufferSize) {
this(bufferSize,null);
}
public Handler(String localCharsSet)
{
this(-1,localCharsSet);
}
public Handler(int bufferSize,String localCharset)
{
if(bufferSize>0)
this.bufferSize = bufferSize;
if(localCharset!=null)
this.localCharset = localCharset;
}
public void handleAccept(SelectionKey key) throws IOException{
SocketChannel sc = ((ServerSocketChannel)key.channel()).accept();
sc.configureBlocking(false);
sc.register(key.selector(),SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
System.out.println("handleAccept process...");
}
public void handlerRead(SelectionKey key) throws IOException{
SocketChannel sc = (SocketChannel)key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
if(sc.read(buffer)==-1){
sc.close();
}else{
buffer.flip();
String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();
System.out.println("received from client: "+receivedString);
String sendString = "received data: "+receivedString;
buffer = ByteBuffer.wrap(sendString.getBytes(localCharset));
sc.write(buffer);
sc.close();
}
System.out.println("handleAccept process...");
}
}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
String msg = "Client Data.";
try {
Socket socket = new Socket("127.0.0.1",8080);
PrintWriter pw = new PrintWriter(socket.getOutputStream());
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw.println(msg);
pw.flush();
String line = is.readLine();
System.out.println("received from server: "+line);
pw.close();
is.close();
socket.close();
}catch (Exception e)
{
e.printStackTrace();
}
}
}
源代码
一、详解Servlet
Servlet结构
框架层级(Application\Config\Context)?
Servlet接口
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest req,ServletResponse res) throws ServletException,IOException;
String getServletInfo();
void destory();
- ServletConfig配置
- SpringMVC通过指定xml配置文件构造ServletConfig对象
- Tomcat传入org.apache.catalina.core.StandardWrapper作为配置对象
GenericServlet
- 是Servlet的默认实现
- 实现了ServletConfig接口
- 提供了无参的init方法
- 提供了log方法
ServletContext getServletConfig().getServletContext();
ServletContext getServletContext();
getServletContext().log(String msg);
HttpServlet(具体实现)
- 是用Http协议实现的Servlet的基类,开发中写Servlet直接继承HttpServlet就可以了
- 主要重写了service(),重载的Servlet参数为新的HttpServlet参数,并根据Http请求类型路由到不同的处理方法
- 对doGet()方法进行过期校验
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
this.service(request, response);
} else {
throw new ServletException("non-HTTP request or response");
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
二、Tomcat分析(暂时跳过)
整体结构
- Server:顶层容器代表整个服务器(只有1个)
- Service:可以有多个,提供具体的服务
- Connector:可以有多个,对应对同一服务的多个请求
- Container:只能有一个,用于封装和管理Servlet
![Tomcat整体结构](https://img-blog.csdnimg.cn/32ed196c871f425c9cb0dfba385e544d.png#pic_center)
工作方法(以下方法都是按容器的结构逐层调用)
- load()
- start()
- stop()
- await()
Bootstrap启动过程
实践
一、环境搭建
- 创建Maven项目
- 配置pom.xml
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
- 构建Web项目结构
![3](https://img-blog.csdnimg.cn/b67db9c645e3469ca6dc71d2f1d3951e.png#pic_center)
- 配置web.xml
- SpringMVC的配置文件默认位于WEB-INF下,默认名称[servlet-name]-servlet.xml
- 可以通过【init-param】标签指定SpringMvc的位置,classpath:表示\main
- 注意web项目结构的根目录
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 创建控制器
package com.fancm.controller;
import org.springframework.stereotype.Controller;
@Controller
public class HelloController {
}
- 修改springMVC配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--扫描控制层组件-->
<context:component-scan base-package="com.fancm.controller"/>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
</beans>
- 配置Tomcat
- 从官网下载符合系统的Tomcat二进制文件
- IDEA设置Tomcat启动并选择部署环境
- 注意应用上下文:访问路径的URL根目录(MVC映射的所有URL请求都要在以该目录开头)
- 启动Tomcat
环境搭建总结
- DispatcherServlet
- 相当于SpringMVC的入口程序,通过这个Servlet对请求拦截和响应
- 在web.xml中配置该Serlvet
- 控制层扫描
- 视图解析器
- 工作流程
- 请求经过DispatcherServlet拦截后转发到响应的控制器
- 控制器完成业务逻辑后返回视图名称
- 视图解析器封装视图路径
- 渲染器根据视图路径渲染页面
二、如何使用SpringMVC
RequestMapping注解
- 功能
- 作用就是将请求和处理请求的控制器方法关联起来,建立映射关系
- 注解位置
package com.fancm.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/test")
public class RequestMappingTestController {
@RequestMapping("/testRequestMapping")
public String testRequestMapping(){
return "success";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>RequestMappingTest</title>
</head>
<body>
<h1>success</h1>
</body>
</html>
- Value属性
- 字符串数组参数
- 能传入多个请求地址,将多个地址的请求映射到同一方法
@Controller
public class RequestMappingTestController {
@RequestMapping(value = {"/testRequestMapping","/test"})
public String testRequestMapping(){
return "success";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<a th:href="@{/testRequestMapping}">
测试@RequestMapping的value属性-- >/testRequestMapping
</a>
<br>
<a th:href="@{/test}">测试@RequestMapping的value属性-->/test</a>
<br>
</body>
</html>