阻塞与非阻塞
同步和异步
BIO 同步阻塞IO,block io IO操作时会阻塞线程,并发处理能力低。
NIO 同步非阻塞IO ,NIO是对BIO的改进,基于Reactor模型。
AIO(NIO2.0) 异步非阻塞IO 完成了客户端请求处理,在通知服务器去启动线程进行处理。
单线程模型
Reactor 多线程模型
但是如果并发仍然很大,Reactor 仍然无法处理大量的客户端请求
Reactor 主从多线程模型
这种线程模型是 Netty 推荐使用的线程模型,
这种模型适用于高并发场景,一组线程池接收请求,一组线程池处理 IO。
<dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.15.Final</version> </dependency> </dependencies>
public class WebsocketServer { public static void main(String[] args) { //创建两个线程池 NioEventLoopGroup mainGroup = new NioEventLoopGroup(); NioEventLoopGroup subGroup = new NioEventLoopGroup(); try { //创建Netty服务器启动对象 ServerBootstrap serverBootstrap = new ServerBootstrap(); //初始化容器启动对象 serverBootstrap //指定使用上面创建的两个线程池 .group(mainGroup, subGroup) //指定Netty通道类型 .channel(NioServerSocketChannel.class) //指定通道初始化器用来加载 当Channel收到事件后 //进行业务处理 .childHandler(new WebSocketChanelInitailizer()); //绑定服务器端口,已同步的方式启动服务器 ChannelFuture future = serverBootstrap.bind(9090).sync(); //等待服务器关闭 future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { //关闭服务器 mainGroup.shutdownGracefully(); subGroup.shutdownGracefully(); } } }
/** * 通道初始化器,用来加载通道处理器(ChanelHandler) * * @Author chenppeng * @Date 2021-06-03 10:58 * @Version 1.0 */ @SuppressWarnings("all") public class WebSocketChanelInitailizer extends ChannelInitializer<SocketChannel> { /** * 初始化通道,这个方法主要是加载对应的ChannelHandler * * @param socketChannel * @throws Exception */ @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //获取管道,将一个一个的ChannelHandler加载到管道中 ChannelPipeline pipeline = socketChannel.pipeline(); //添加一个http的编解码器 pipeline.addLast(new HttpServerCodec()); //添加一个用户支持大数据流的支持 pipeline.addLast(new ChunkedWriteHandler()); //添加一个聚合器,这个聚合器主要是将HttpMessage聚合成FullHttpRequest/Response pipeline.addLast(new HttpObjectAggregator(1024 * 64)); //需要指定接受请求的路由 //必须使用以ws后缀结尾的url才能访问 pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); //添加自定义hanlder pipeline.addLast(new ChatHandler()); } }
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { /** * 用户保存所有的客户端连接 */ private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); /** * 时间格式解析器 */ private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd hh:MM:ss"); /** * 当Channel中有新的事件消息会自动调用 * * @param channelHandlerContext * @param textWebSocketFrame * @throws Exception */ @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception { //当接受到数据后 会自动调用 //获取客户端发送过来的文本消息 String text = textWebSocketFrame.text(); System.out.println("接受到的消息数据为:" + text); for (Channel client : clients) { //将消息发送给所有的客户端 client.writeAndFlush(new TextWebSocketFrame(simpleDateFormat.format(new Date()) + ":" + text)); } } /** * 当有新的客户端li连接服务器之后,会自动调用这个方法 * * @param ctx * @throws Exception */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { //将新的通道加入到clients clients.add(ctx.channel()); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>在线聊天室</title> </head> <body> <label for="message"><input type="text" id="message"></label> <input type="button" value="发送消息" onclick="SendMessage()"> 接受到的消息 <p id="server_message" style="background-color: #AAAAAA"></p> <script> let websocket = null; //判断当前浏览器是否支持web socket if (window.WebSocket) { //连接到服务器并发送消息 websocket = new WebSocket("ws://127.0.0.1:9090/ws"); websocket.onopen = function () { console.log("建立连接"); }; websocket.onclose = function () { console.log("断开连接") }; websocket.onmessage = function (e) { console.log("接受到服务器的消息:" + e.data); document.getElementById("server_message").innerHTML += e.data + "<br>"; } } else { alert("当前浏览器不支持 web socket") } function SendMessage() { let message = document.getElementById("message"); websocket.send(message.value); } </script> </body> </html>
MUI 是一个轻量级的前端框架。MUI 以 iOS 平台 UI 为基础,补充部分 Android 平台特有的 UI 控件。MUI 不依赖任何第三方 JS 库,压缩后的 JS 和 CSS 文件仅有 100+K 和 60+K,可以根据自己的需要,自定义去下载对应的模块。并且 MUI 编写的前端,可以打包成 APK 和 IPA 安装文件,在手机端运行。也就是,编写一套代码,就可以在 Android、
IOS 下运行。
H5+提供了对 HTML5 的增强,提供了 40WAPI 给程序员使用。使用 H5+ API 可以轻松开发二维码扫描、摄像头、地图位置、消息推送等功能
@Override public User login(String username, String password) { if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) { TbUserExample tbUserExample = new TbUserExample(); TbUserExample.Criteria criteria = tbUserExample.createCriteria(); criteria.andUsernameEqualTo(username); List<TbUser> userList = tbUserMapper.selectByExample(tbUserExample); if (userList != null && userList.size() == 1) { String pwd = DigestUtils.md5DigestAsHex(password.getBytes()); if (pwd.equals(userList.get(0).getPassword())) { User user = new User(); BeanUtils.copyProperties(userList.get(0), user); return user; } } } return null; }
@Override public void register(TbUser tbUser) { //判断用户在数据库是否存在 TbUserExample example = new TbUserExample(); TbUserExample.Criteria criteria = example.createCriteria(); criteria.andUsernameEqualTo(tbUser.getUsername()); List<TbUser> userList = tbUserMapper.selectByExample(example); if (userList != null && userList.size() > 0) { throw new RuntimeException("注册失败"); } //将用户信息保存到数据库中 tbUser.setId(idWorker.nextId()); tbUser.setPassword(DigestUtils.md5DigestAsHex(tbUser.getPassword().getBytes())); tbUser.setPicSmall(""); tbUser.setPicNormal("l"); tbUser.setNickname(tbUser.getUsername()); tbUser.setCreatetime(new Date()); tbUserMapper.insert(tbUser); }
FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS 很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
#整合FASTDFS fdfs.soTimeout=1501 fdfs.connectTimeout=601 #缩略图生成参数 fdfs.thumbImage.width=150 fdfs.thumbImage.height=150 #TrackerList参数,支持多个 fdfs.trackerList[0]=192.168.25.135:22122 #HTTP URL fdfs.httpurl=http://192.168.25.135/
上传头像业务层
@Override public User upload(MultipartFile file, String userid) { try { //返回的fastdfs中的url路径,这个路径不带http://192.168.1.133/.. String url = fastDFSClient.uploadFile(file); //在fastDFS上传的时候,会自动生成一个缩略图 文件名_150*150,后缀 String[] fileNameList = url.split("\\."); String fileName = fileNameList[0]; String ext = fileNameList[1]; String picSmallUrl = fileName + "_150x150." + ext; String prefix = environment.getProperty("fdfs.httpurl"); TbUser tbUser = tbUserMapper.selectByPrimaryKey(userid); //设置头像大图片 tbUser.setPicNormal(prefix + url); //设置头像小图片 tbUser.setPicSmall(prefix + picSmallUrl); //将新的头像url更新到数据库里面 tbUserMapper.updateByPrimaryKey(tbUser); User user = new User(); BeanUtils.copyProperties(tbUser, user); return user; } catch (IOException e) { e.printStackTrace(); return null; } }