怎么设计一个基于socket的分布式高并发聊天室的架构

怎么设计一个基于socket的分布式高并发聊天室的架构,第1张

聊天室服务器端逻辑

//Debugjava

import javautil;

public class Debug {

public static String p(String s) {

Systemoutprintln(s);

return s;

}

public static Object p(Object O) {

Systemoutprintln(OtoString());

return O;

}

public static Object[] p(Object[] O) {

Systemoutprint("[");

String s = "";

for (Object o : O) {

Systemoutprint(s);

Systemoutprint(otoString());

s = ",";

}

Systemoutprintln("]");

return O;

}

public static int p(int i) {

Systemoutprintln(i);

return i;

}

public static long p(long i) {

Systemoutprintln(i);

return i;

}

public static MapEntry p(MapEntry l) {

Systemoutprintln(l);

return l;

}

public static HashSet p(HashSet l) {

Systemoutprintln(l);

return l;

}

public static HashMap p(HashMap l) {

Systemoutprintln(l);

return l;

}

public static Map p(Map l) {

Systemoutprintln(l);

return l;

}

public static LinkedHashSet p(LinkedHashSet l) {

Systemoutprintln(l);

return l;

}

public static TreeSet p(TreeSet l) {

Systemoutprintln(l);

return l;

}

public static Set p(Set l) {

Systemoutprintln(l);

return l;

}

public static ArrayList p(ArrayList l) {

Systemoutprintln(l);

return l;

}

public static LinkedList p(LinkedList l) {

Systemoutprintln(l);

return l;

}

public static List p(List l) {

Systemoutprintln(l);

return l;

}

}

//Servicejava

import javaio;

import javanet;

import javautil;

public class Service extends Thread{

private Socket client=null;

private Server server=null;

public Service(Server server,Socket client){

thisserver=server;

thisclient=client;

}

public void run(){

try {

BufferedReader br=new BufferedReader(new InputStreamReader(clientgetInputStream()));

while(true){

String str=brreadLine();

List<Socket> li=servergetLi();

for(Socket c:li){

if(!cisClosed() && cisConnected()){

OutputStream os=cgetOutputStream();

oswrite((str+"\n")getBytes());

}else{

clientclose();

servergetLi()remove(client);

break;

}

}

}

}catch (IOException e) {

eprintStackTrace();

}

}

}

//Serverjava

import javautil;

import javaio;

import javanet;

public class Server {

private List<Socket> li=new ArrayList<Socket>();

private ServerSocket ss=null;

public List<Socket> getLi(){

return li;

}

public Server(int port){

try {

ss=new ServerSocket(port);

} catch (IOException e) {

eprintStackTrace();

}

}

public void start(){

while(true){

Socket s;

try {

s = ssaccept();

liadd(s);

new Service(this,s)start();

} catch (IOException e) {

eprintStackTrace();

}

}

}

public static void main(String[] args) {

new Server(8888)start();

}

}

17聊天室客户端逻辑

//Debugjava

import javautil;

public class Debug {

public static String p(String s) {

Systemoutprintln(s);

return s;

}

public static Object p(Object O) {

Systemoutprintln(OtoString());

return O;

}

public static Object[] p(Object[] O) {

Systemoutprint("[");

String s = "";

for (Object o : O) {

Systemoutprint(s);

Systemoutprint(otoString());

s = ",";

}

Systemoutprintln("]");

return O;

}

public static int p(int i) {

Systemoutprintln(i);

return i;

}

public static long p(long i) {

Systemoutprintln(i);

return i;

}

public static MapEntry p(MapEntry l) {

Systemoutprintln(l);

return l;

}

public static HashSet p(HashSet l) {

Systemoutprintln(l);

return l;

}

public static HashMap p(HashMap l) {

Systemoutprintln(l);

return l;

}

public static Map p(Map l) {

Systemoutprintln(l);

return l;

}

public static LinkedHashSet p(LinkedHashSet l) {

Systemoutprintln(l);

return l;

}

public static TreeSet p(TreeSet l) {

Systemoutprintln(l);

return l;

}

public static Set p(Set l) {

Systemoutprintln(l);

return l;

}

public static ArrayList p(ArrayList l) {

Systemoutprintln(l);

return l;

}

public static LinkedList p(LinkedList l) {

Systemoutprintln(l);

return l;

}

public static List p(List l) {

Systemoutprintln(l);

return l;

}

}

//GetMessagejava

import javaxswing;

import javaio;

import javanet;

public class GetMessage extends Thread{

private JTextArea context=null;

private Socket s=null;

public GetMessage(JTextArea context,Socket s){

thiscontext=context;

thiss=s;

}

public void run(){

try {

BufferedReader br=new BufferedReader(new InputStreamReader(sgetInputStream()));

while(true){

String str=brreadLine();

String c=contextgetText();

contextsetText(c+"\n"+str);

}

} catch (IOException e) {

eprintStackTrace();

}

}

}

//Clientjava

import javaxswing;

import javaawt;

import javaawtevent;

import javaio;

import javanet;

public class Client extends JFrame {

private String ip = "";

private int port = 0;

private Socket s = null;

private JTextArea context = new JTextArea(10, 10);

JScrollPane sp = new JScrollPane(context);

private JTextField say = new JTextField(10);

private Container c;

public Client(String ip, int port) {

c = thisgetContentPane();

csetLayout(new BorderLayout());

contextsetEditable(false);

cadd(sp);

JPanel p = new JPanel();

padd(BorderLayoutCENTER, say);

cadd(BorderLayoutSOUTH, p);

try {

s = new Socket(ip, port);

} catch (UnknownHostException e) {

eprintStackTrace();

} catch (IOException e) {

eprintStackTrace();

}

sayaddActionListener(new ActionListener() {

private void send(String str) {

try {

OutputStream os = sgetOutputStream();

oswrite(strgetBytes());

} catch (IOException e) {

eprintStackTrace();

}

}

public void actionPerformed(ActionEvent arg0) {

String str = saygetText();

send(str + "\n");

saysetText("");

}

});

new GetMessage(context, s)start();

thissetSize(300, 300);

thissetVisible(true);

thissetResizable(false);

}

public static void main(String[] args) {

String strIP = null;

try {

strIP = InetAddressgetLocalHost()getHostAddress()toString();

} catch (UnknownHostException e) {

eprintStackTrace();

}

new Client(strIP, 8888); //"127001"

}

}

不同于顺序服务器, 并发服务器 就要能在一个时间为多个客户端提供服务。 例如,一个 聊天服务器 可能服务一个特定的客户端数小时 不同于顺序服务器, 并发服务器 就要能在一个时间为多个客户端提供服务。 例如,一个 聊天服务器 可能服务一个特定的客户端数小时 ──在停止为这个客户端服务之前服务器不能等待, 除非是在等待一下个客户端到来之前的间隙才能等待。── 在停止为这个客户端服务之前服务器不能等待, 除非是在等待一下个客户端到来之前的间隙才能等待。 http:/ /cnsnapcnfreebsdorg/doc/zh_ CNGB2312/books/developers- handbook/sockets-concurrent- servershtml

通过学习《亿级流量网站架构核心技术》及《linux就该这么学》学习笔记及自己的感悟:架构设计之高可用高并发系统设计原则,架构设计包括墨菲定律、康威定律和二八定律三大定律,而系统设计包括高并发原则、高可用和业务设计原则等。

架构设计三大定律

墨菲定律 – 任何事没有表面看起来那么简单 – 所有的事都会比预计的时间长 – 可能出错的事情总会出错 – 担心某种事情发生,那么它就更有可能发生

康威定律 – 系统架构师公司组织架构的反映 – 按照业务闭环进行系统拆分/组织架构划分,实现闭环、高内聚、低耦合,减少沟通成本 – 如果沟通出现问题,应该考虑进行系统和组织架构的调整 – 适合时机进行系统拆分,不要一开始就吧系统、服务拆分拆的非常细,虽然闭环,但是每个人维护的系统多,维护成本高 – 微服务架构的理论基础 – 康威定律https://yqaliyuncom/articles/8611– 每个架构师都应该研究下康威定律http://36krcom/p/5042735html

二八定律 – 80%的结果取决于20%的原因

系统设计遵循的原则

1高并发原则

无状态

无状态应用,便于水平扩展

有状态配置可通过配置中心实现无状态

实践: Disconf、Yaconf、Zookpeer、Consul、Confd、Diamond、Xdiamond等

拆分

系统维度:按照系统功能、业务拆分,如购物车,结算,订单等

功能维度:对系统功能在做细粒度拆分

读写维度:根据读写比例特征拆分;读多,可考虑多级缓存;写多,可考虑分库分表

AOP维度: 根据访问特征,按照AOP进行拆分,比如商品详情页可分为CDN、页面渲染系统,CDN就是一个AOP系统

模块维度:对整体代码结构划分Web、Service、DAO

服务化

服务化演进: 进程内服务-单机远程服务-集群手动注册服务-自动注册和发现服务-服务的分组、隔离、路由-服务治理

考虑服务分组、隔离、限流、黑白名单、超时、重试机制、路由、故障补偿等

实践:利用Nginx、HaProxy、LVS等实现负载均衡,ZooKeeper、Consul等实现自动注册和发现服

消息队列

目的: 服务解耦(一对多消费)、异步处理、流量削峰缓冲等

大流量缓冲: 牺牲强一致性,保证最终一致性(案例:库存扣减,现在Redis中做扣减,记录扣减日志,通过后台进程将扣减日志应用到DB)

数据校对: 解决异步消息机制下消息丢失问题

数据异构

数据异构: 通过消息队列机制接收数据变更,原子化存储

数据闭环: 屏蔽多从数据来源,将数据异构存储,形成闭环

缓存银弹

用户层:

DNS缓存

浏览器DNS缓存

操作系统DNS缓存

本地DNS服务商缓存

DNS服务器缓存

客户端缓存

浏览器缓存(Expires、Cache-Control、Last-Modified、Etag)

App客户缓存(js/css/image…)

代理层:

CDN缓存(一般基于ATS、Varnish、Nginx、Squid等构建,边缘节点-二级节点-中心节点-源站)

接入层:

Opcache: 缓存PHP的Opcodes

Proxy_cache: 代理缓存,可以存储到/dev/shm或者SSD

FastCGI Cache

Nginx+Lua+Redis: 业务数据缓存

Nginx为例:

PHP为例:

应用层:

页面静态化

业务数据缓存(Redis/Memcached/本地文件等)

消息队列

数据层:

NoSQL: Redis、Memcache、SSDB等

MySQL: Innodb/MyISAM等Query Cache、Key Cache、Innodb Buffer Size等

系统层:

CPU : L1/L2/L3 Cache/NUMA

内存

磁盘:磁盘本身缓存、dirtyratio/dirtybackground_ratio、阵列卡本身缓存

并发化

2高可用原则

降级

降级开关集中化管理:将开关配置信息推送到各个应用

可降级的多级读服务:如服务调用降级为只读本地缓存

开关前置化:如Nginx+lua(OpenResty)配置降级策略,引流流量;可基于此做灰度策略

业务降级:高并发下,保证核心功能,次要功能可由同步改为异步策略或屏蔽功能

限流

目的: 防止恶意请求攻击或超出系统峰值

实践:

恶意请求流量只访问到Cache

穿透后端应用的流量使用Nginx的limit处理

恶意IP使用Nginx Deny策略或者iptables拒绝

切流量

目的:屏蔽故障机器

实践:

DNS: 更改域名解析入口,如DNSPOD可以添加备用IP,正常IP故障时,会自主切换到备用地址;生效实践较慢

HttpDNS: 为了绕过运营商LocalDNS实现的精准流量调度

LVS/HaProxy/Nginx: 摘除故障节点

可回滚

发布版本失败时可随时快速回退到上一个稳定版本

3业务设计原则

防重设计

幂等设计

流程定义

状态与状态机

后台系统操作可反馈

后台系统审批化

文档注释

备份

4总结

先行规划和设计时有必要的,要对现有问题有方案,对未来有预案;欠下的技术债,迟早都是要还的。

本文作者为网易高级运维工程师

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
网站模板库 » 怎么设计一个基于socket的分布式高并发聊天室的架构

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情