43.QQ聊天软件GUI窗口编写
QQ聊天软件代码功能编写
一,Tkinter聊天界面编写
1,聊天软件客户端界面开发-1
- Tkinter的模块(“TK接口”)是标准的Python接口从Tk的GUI工具包
- https://i.cnblogs.com/EditPosts.aspx?opt=1 ###Tkinter官方文档:关于相关函数具体看文档介绍
Tkinter模块:自带的,跨平台
做GUI:界面软件 不难,但是复杂,东西多,窗口控件多,设计,美感,抠图 界面开发设计
设置窗口:像素大小- 拜访控件:按钮,复选框,文本框
- root控件流程:
- 1:导入模块 import tkinter
- 2:创建主窗口root = tkinter.Tk()
- 3:创建其他窗口以及控件(按钮,文本框,等等)
- 4:摆放到root上
- 5:开启root的事件循环:root.mainloop()
- 6:如果不需要该窗口,可以使用root.destory()
- 组件:
- 文本框
- tkinter.Text()
- insert():插入数据
- tkinter.END,从末尾插入
- delete(‘0.0’,tkinter.END):删除全部文本框数据
- tag_config('标签名',foreground='颜色',):标签配置,设置字体颜色, 第一个字符串参数是标签名,第二个是颜色
- get('0.0',thinter.END):阅读拿出文本框的内容
- 按钮
- tkinter.Button()
- text:按钮名字
- command:回调函数,按钮点击时的工作任务列表框
- 列表框
- tkinter.Listbox()
- insert(tkinter.END, 追加的数据)
- 事件绑定
- 双击事件:<Double-Button-1>
事件绑定的函数必须有一个参数,这个参数就是事件- self.right_listbox.curselection()
返回当前选择的列表框索引位置值,返回结果为单个数据的元组- index = self.listbox_user.curselection()[0]
self.talk_user_name = self.listbox_user.get(index)
- index #获取的当前列表框中的索引位置的值
root.title('**')
- 设置上标字符
- grid()
- x,y轴的摆放方式
- row:行
column:列 rowspan:行宽,跨行数 pady:表格间距,x轴 padx:y轴的表格间距 sticky
对其方向,上北下南左西右东N,S,W,E grid_propagate(0)
0代表禁止容器窗口缩放- root.resizable(width=False,height=Flase)
- 禁止窗口缩放
root.winfo_screenwidth()
自动获取用户的屏幕宽度 root.winfo_screenheight()
自动获取用户的屏幕高度 root.geometry("%dx%d%+d%+d" % (width, height, xoffset, yoffset))
设置控件所处的屏幕位置- tkinter.Label(所属的主窗口,text='名字',font=("黑体",9, "bold"))
- 添加标签lable
tkinter.Entry()
单行文本框 获取数据直接使用get() 函数即可,不需要传参。 from tkinter import messagebox ##展示报错信息
'showerror', 'showinfo', 'showwarning'
title=None, message=None
messagebox.showerror(title='登录失败',message='登录失败')
- 客户端登录及聊天窗口代码编写整合如下:
from tkinter import Tk
import tkinter
import time
from multiprocessing.pool import ThreadPool
import socket
from tkinter import messagebox
import pickle
import _pickle
#scroolbar
class TalkRoot:
'''
实现用户聊天的主要界面
'''
def __init__(self,user_name,client,user_id):
self.root = Tk()
self.root.title('欢迎你:%s' % user_name)
self.client = client
self.user_id = user_id
#-----------------创建进程池----------------------
self.thread_pool = ThreadPool(5) #5个线程的线程池
#-------功能变量----------------------
self.talk_user_name = '' #你要聊天的人
self.client = client #套接字
#------------------设置窗口所处的用户界面位置及窗口大小在屏幕中间------------
self.root_width=550
self.root_height=420
self.user_screen_width = self.root.winfo_screenwidth()#自动获取用户的屏幕宽度
self.user_screen_height = self.root.winfo_screenheight()#用户的屏幕高
self.root.geometry("%dx%d%+d%+d" %
(self.root_width,
self.root_height,
(self.user_screen_width - self.root_width) / 2,
(self.user_screen_height - self.root_height) / 2,)
)
#-------容器--------
#上: 输出
self.frame_top = tkinter.Frame(width=380,height=270)
#中: 输入
self.frame_center = tkinter.Frame(width=380,height=100)
#下: 按钮
self.frame_bottom = tkinter.Frame(width=380,height=30)
#右: 列表
self.frame_right = tkinter.Frame(width=170,height=400) #--------控件--------
#上: 输出
self.text_output = tkinter.Text(self.frame_top,height=260)
self.text_output.tag_config('time_stamp',foreground='green') #标签配置颜色
self.text_output.tag_config('self_msg',foreground='blue')#标签配置颜色
self.text_output.tag_config('other_msg',foreground='red')#标签配置颜色
#中: 输入
self.text_input = tkinter.Text(self.frame_center)
self.text_input.bind('<Return>',self.button_send_msg)
#下: 按钮
self.button_send = tkinter.Button(self.frame_bottom,text='发送',command=self.button_send_msg) #发送按钮
self.button_cancle = tkinter.Button(self.frame_bottom,text='取消',command=self.fc_button_cancle) #取消按钮
#右: 列表
self.listbox_user = tkinter.Listbox(self.frame_right,width=150,height=25)
self.listbox_user.bind('<Double-Button-1>',self.get_talk_user)#绑定双击事件
#--------摆放生效容器---------
self.frame_top.grid(row=0,column=0,padx=2,pady=5)
self.frame_center.grid(row=1,column=0,padx=2,pady=5)
self.frame_bottom.grid(row=2,column=0,sticky=tkinter.E)
self.frame_right.grid(row=0,column=1,rowspan=3,padx=5,pady=5) #--------摆放生效控件---------
self.text_output.grid()
self.text_input.grid()
self.button_send.grid(row=0,column=1,padx=300)
self.button_cancle.grid(row=0,column=0)
self.listbox_user.grid() #--------禁止容器窗口缩放grid_--------------------
self.frame_top.grid_propagate(0)
self.frame_center.grid_propagate(0)
self.frame_bottom.grid_propagate(0)
self.frame_right.grid_propagate(0)
#------------专门开一个接受消息聊天的线程------------
self.thread_pool.apply_async(func=self.check_recv_msg) #非阻塞线程
#------------窗口循环---------------------------------------
self.root.mainloop() def check_recv_msg(self):
while True: #循环窗口聊天
try:
data = pickle.loads(self.client.recv(1024))
except _pickle.UnpicklingError:
messagebox.showinfo(title='服务端消息错误',message='服务端发来无法解析的错误')
self.root.destroy()
exit()
except Exception as e:
messagebox.showinfo(title='服务端断开',message='服务端断开')
self.root.destroy()
exit()
if data.get('flag') == 'flush':
#服务端返回最新在线用户
self.thread_pool.apply_async(func=self.flush_onlien_user,args=(data,))
elif data.get('flag') == 'forward':
#服务端返回别人发来的消息
self.thread_pool.apply_async(func=self.show_other_msg,args=(data,))
elif not data:
#服务端发来空消息,断开连接
exit() def show_other_msg(self,data):
'''
展示别人发来的消息
'''
send_name = data.get('data')['send_name']
other_msg = data.get('data')['message']
print('别人发来了消息:',other_msg)
time_stamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime() ) + '\n'
self.text_output.insert(tkinter.END,time_stamp,'green')
self.text_output.insert(tkinter.END,'%s:%s' % (send_name,other_msg),'red') def flush_onlien_user(self,data):
self.listbox_user.delete(0,tkinter.END) #清空在线用户列表
for user in data.get('data'):
#user : {id:name}
id_ = list(user.keys())[0]
name = list(user.values())[0]
self.listbox_user.insert(tkinter.END, '%s:%s' % (name,id_)) def button_send_msg(self,events=None):
self_msg = self.text_input.get('0.0',tkinter.END)
self.text_input.delete('0.0',tkinter.END)
if not self_msg.isspace():
time_stamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime() ) + '\n'
self.text_output.insert(tkinter.END,time_stamp,'time_stamp')
self.text_output.insert(tkinter.END,' ' + self_msg,'self_msg')
self.thread_pool.apply_async(func=self.clear_input_text)
#id:name
if self.talk_user_name:
print('捕捉到在线用户,准备发送消息')
recv_id = self.talk_user_name.split(':')[0]
print(recv_id)
send_data = {
'flag': 'send',
'data': {
'send_id': self.user_id,
'recv_id': recv_id,
'message': self_msg,
}
}
self.client.send(pickle.dumps(send_data))
else:
'''
群发功能
'''
send_data = {
'flag': 'part',
'data': {
'send_id': self.user_id,
'message': self_msg,
}
} def clear_input_text(self):
time.sleep(0.1)
self.text_input.delete('0.0',tkinter.END) def get_talk_user(self,events):
'''
获取当前聊天用户
'''
if self.listbox_user.size() != 0:
index = self.listbox_user.curselection()[0]
self.talk_user_name = self.listbox_user.get(index)
print(self.talk_user_name) def fc_button_cancle(self):
'''
聊天界面的销毁
销毁窗口
客户端套接字释放
'''
exit() def __del__(self):
self.client.close() #套接字释放
self.root.destroy() #销毁窗口 class LoginRoot():
def __init__(self):
self.root = tkinter.Tk()
self.root.title('登陆')
self.root.resizable(width=False,height=False) #禁止窗口缩放调整 self.root_width = 197
self.root_height = 75 #自适应,首先要获取到用户的屏幕分辨率
self.user_screen_width = self.root.winfo_screenwidth() #用户的屏幕也宽度
self.user_screen_height = self.root.winfo_screenheight() #用户的屏幕高度 self.root.geometry("%dx%d%+d%+d" % #设置窗口所处的用户界面位置及窗口大小
(self.root_width,
self.root_height,
(self.user_screen_width - self.root_width) / 2,
(self.user_screen_height - self.root_height) / 2,)
) #创建提示label
self.label_id = tkinter.Label(self.root,text='I D:',font=("黑体",10, "bold")) # font设置字体样式
self.label_name = tkinter.Label(self.root,text='昵称:',font=("黑体",10, "bold"))
#摆放提示label
self.label_id.grid(row=0,column=0,sticky=tkinter.W)
self.label_name.grid(row=1,column=0,)
#创建输入单行文本框
self.entry_id = tkinter.Entry(self.root)
self.entry_name = tkinter.Entry(self.root)
#摆放文本框
self.entry_id.grid(row=0,column=1)
self.entry_name.grid(row=1,column=1)
#创建功能按钮
self.button_login = tkinter.Button(self.root,text='登陆',command=self.user_login)
self.button_cancle = tkinter.Button(self.root,text='取消',command=self.user_cancle)
#摆放功能按钮
self.button_login.grid(row=2,column=1,sticky=tkinter.E)
self.button_cancle.grid(row=2,column=0,sticky=tkinter.W)
self.root.mainloop() def user_login(self):
'''
要确定用户提供的ID是唯一的
把昵称传递给TalkRoot
'''
user_id = self.entry_id.get()
user_name = self.entry_name.get() send_login_msg = {
'flag': 'login',
'data':{
'id': user_id,
'name': user_name
}
}
ip = '192.168.1.101'
port = 8000
try:
self.client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.client.connect( (ip,port) )
except Exception as e:
print('登陆连接时的错误:',e)
messagebox.showerror(title='登陆失败',message='登陆失败')
else:
self.client.send(pickle.dumps(send_login_msg))
data = pickle.loads(self.client.recv(1024))
if data.get('flag') == 'login':
if data.get('data')['state']:
#登陆成功
self.root.destroy()
tkr = TalkRoot(user_name,self.client,user_id)
else:
self.client.close()
messagebox.showwarning(title='登陆失败',message='%s' % (data.get('data')['message'])) def user_cancle(self):
self.root.destroy() def main():
lgr = LoginRoot() if __name__ == '__main__':
main()
二,聊天类软件后台架构及请求数据设计
一,服务端功能
1:接收用户登陆
- 主线程,消息收集
- 服务端套接字,唯一ID的校验
2:告知在线用户
- 另开线程,遍历访问服务端保存的客户端序列数据
3:处理在线用户的消息发送
- 主线程,消息收集 一旦捕获到了一个消息是要发送给别人的
- 另开线程,处理消息转发,发给另外一个在线的套接字
二,客户端功能
1:登陆
- 套接字,发送ID和昵称即可
- 2:接收处理服务端发来的在线用户列表
- 3:发送消息,展示消息
三,请求数据结构体 / 客户端
- 1:pickle.dumps() #打包二进制数据体
{ #客户端登陆发送的数据体
'flag': 'login', #数据标志位
'data': {
'id': '123456', #用户ID
'name: 'xxxxx', #用户昵称
}
} { #客户端发送数据体
'flag': 'send' #数据标志位
'data': {
'send_id': '123455', #发送者的ID
'recv_id': '123456', #接收人的ID号
'message: 'xxxxx', #发给别人的数据
}
} { #客户端注册数据体
'flag': 'register', #数据标志位
'data': {
'id': '123456', #用户ID
'name: 'xxxxx', #用户昵称
'password': 'xxxxx', #
}
}四,服务端保存在线用户数据体
- 1:直接连进来的,不进入这个数据体,不算有效用户
- 2:某个线程,捕捉这个套接字发来的第一个login的数据体
- 3:判断ID:
- ID不重复:登陆保存
- ID重复:当用户发送重复id的时,返回的数据
{
'flag':'login',
'data': {
'state':True,/False
'message':'登陆成功'
}
}
- 4:成功登陆的用户保存在字典中的数据结构体
#字典:
dict_onlien_user = {
xxxx:{
'name':'666',
'socket':client,
},
xxxx:{,
name:'666',
socket:client,
},
}- onlien_user.get(recv_id)['socket'].send(message) #如果再转发消息的时候,准确快速的找到接收者
- 5:成功的用户发来的数据,要给别人发消息,服务端怎么保存这个数据?
{ #客户端发送数据体
'flag': 'send' #数据标志位
'data': {
'send_id': '123455', #发送者的ID
'recv_id': '123456', #接收人的ID号
'message: 'xxxxx', #发给别人的数据
}
}
- 6:客户端接收服务端发来的在线用户数据结构体
list_onlien_user = [
{'id1':'name1'},
{'id2':'name2'},
}
- 7: 刷新的用户列表数据是一个列表,现在要更改
{ #服务端发给客户端的数据体
'flag': 'flush' #数据标志位
'data': [在线用户列表]
}- 8: 发给其他用户的数据,怎么发过去?
{ #服务端发给客户端的数据体
'flag': 'forward' #数据标志位
'data': {
'send_name': '123455', #发送者的ID
'message': '123456', #接收人的ID号
}
}五,代码编写如下:
#服务端
import socket
from multiprocessing.pool import ThreadPool
import pickle
import copy
import time class TalkServer:
def __init__(self,ip,port):
self.ip = ip
self.port = port
self.socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket_server.setblocking(0) #服务端套接字设置为非阻塞
self.socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #设置端口复用
self.socket_server.bind( (self.ip, self.port) ) self.dict_online_user = {} #保存未来的在线用户字典
self.list_online_user = [] #保存即将发送给别人在现在用户列表
self.thread_pool = ThreadPool(10) self.is_flush = False #用来判断是否需要刷新在线用户的 def run(self, num_=5):
self.socket_server.listen(num_)
#2:用户发送给别人的消息处理
self.thread_pool.apply_async(func=self.inform_online_user_list)
self.thread_pool.apply_async(func=self.check_send_data)
while True:
'''
接收用户来访
'''
try:
client,client_addr = self.socket_server.accept()
except BlockingIOError:
pass
else:
print('有人来了:[%s|%s]' % client_addr)
#没报错,那么就是用户真正的连接到了,accept捕捉到了返回值
#1:用户登陆发来的消息处理,开一个线程去等待他发来对应的数据,或者说判断这个ID是否是重复的
self.thread_pool.apply_async(func=self.check_login_data,args=(client,))
#login数据 def inform_online_user_list(self):
'''
告知客户端现在的在线用户,就是直接发送一个在线用户的列表
'''
while True:
if self.is_flush:
time.sleep(1)
print('刷新在线用户')
send_online_user_msg = {
'flag': 'flush',
'data': self.list_online_user,
} for id in self.dict_online_user:
client = self.dict_online_user[id]['socket']
client.send(pickle.dumps(send_online_user_msg))
self.is_flush = False def check_send_data(self):
'''
处理用户的发送数据,要发给别人拉
还会碰到用户发来的断开连接的数据
'''
while True:
list_onlien_user_bak = copy.copy(self.list_online_user)
for data in list_onlien_user_bak: #[{'id1':'name1'},{'id1':'name1'}...]
id = list(data.keys())[0]
client = self.dict_online_user[id]['socket'] #套接字拿到
name = self.dict_online_user[id]['name']
#print('当前遍历到的在线用户,%s:%s' % (id,name))
try:
recv_msg = client.recv(1024) #非阻塞形式获取客户端发来的数据
except BlockingIOError as e:
pass #当前该在线用户并没有发送任何消息
except ConnectionResetError as e:
'''
客户端断开连接
'''
pass
else:
#1:判断是否是断开的数据
if not recv_msg:
print('离开了:%s,%s' % (id,name))
client.close()
del self.dict_online_user[id]
self.list_online_user.remove(data)
self.is_flush = True
else:
'''
{ #发送数据体
'flag': 'send' #数据标志位
'data': {
'send_id': '123455', #发送者的ID
'recv_id': '123456', #接收人的ID号
'message: 'xxxxx', #发给别人的数据
}
}
'''
clear_recv_msg = pickle.loads(recv_msg)
if clear_recv_msg.get('flag') == 'send': #确实是要给别人发送消息
clear_recv_data = clear_recv_msg.get('data')
if clear_recv_data: #发来的数据中确实有data
send_id = clear_recv_data.get('send_id')
recv_id = clear_recv_data.get('recv_id')
message = clear_recv_data.get('message') if self.dict_online_user.get(recv_id):
recv_socket = self.dict_online_user.get(recv_id)['socket'] #接收者的套接字
else:
continue
send_name = self.dict_online_user[send_id]['name']#发送者的名字
'''
{ #服务端发给客户端的数据体
'flag': 'forward' #数据标志位
'data': {
'send_name': '123455', #发送者的ID
'message': '123456', #接收人的ID号
}
}
'''
forward_data = {
'flag': 'forward',
'data': {
'send_name': send_name,
'message': message,
}
}
recv_socket.send(pickle.dumps(forward_data))
print('消息转发完毕:\n',forward_data) def check_login_data(self,client):
'''
{ #登陆数据体
'flag': 'login', #数据标志位
'data': {
'id': '123456', #用户ID
'name: 'xxxxx', #用户昵称
}
}
'''
recv_msg = client.recv(1024) #接收用户发来的login数据体
if recv_msg: #发来的数据是有效的
clear_recv_msg = pickle.loads(recv_msg)
if clear_recv_msg.get('flag') == 'login': #发来的确实是login的数据
clear_recv_data = clear_recv_msg.get('data')
if clear_recv_data: #发来的数据中确实有data
id = clear_recv_data.get('id')
name = clear_recv_data.get('name')
#判断ID是否重复
if id in self.dict_online_user:
print('[%s:%s]该用户出现重复ID' % (id, name))
send_login_msg = {
'flag': 'login',
'data': {
'state': False,
'message': 'ID使用重复',
}
}
client.send(pickle.dumps(send_login_msg)) #给客户端返回错误信息
elif not id or not name:
print('[%s:%s]该用户所需数据为空' % (id, name))
send_login_msg = {
'flag': 'login',
'data': {
'state': False,
'message': 'ID或名字为空',
}
}
client.send(pickle.dumps(send_login_msg)) #给客户端返回错误信息
else:
'''
dict_onlien_user = {
xxxx:{
'name':'666',
'socket':client,
...
},
'''
client.setblocking(0) #真正存储在在线字典里的用户套接字是个非阻塞的
self.dict_online_user[id] = {
'name': name,
'socket': client,
}
self.list_online_user.append( {id:name} )
#刷新在线用户
print('在线用户已添加')
self.is_flush = True
send_login_msg = {
'flag': 'login',
'data': {
'state': True,
'message': '登陆成功',
}
}
client.send(pickle.dumps(send_login_msg))
return
client.close() #用户数据无效,关闭连接 def main():
ip = ''
port = 8000
tks = TalkServer(ip, port)
tks.run() if __name__ == '__main__':
main()
运行结果:
43.QQ聊天软件GUI窗口编写的更多相关文章
- 仿QQ聊天软件2.0版
地址:http://blog.csdn.net/u012027907/article/details/36952893 Oracle java
- MFC 仿QQ聊天软件(黄花寒)
http://blog.csdn.net/lh844386434/article/details/6655080 http://download.csdn.net/download/lh8443864 ...
- 高仿QQ即时聊天软件开发系列之三登录窗口用户选择下拉框
上一篇高仿QQ即时聊天软件开发系列之二登录窗口界面写了一个大概的布局和原理 这一篇详细说下拉框的实现原理 先上最终效果图 一开始其实只是想给下拉框加一个placeholder效果,让下拉框在未选择未输 ...
- 高仿QQ即时聊天软件开发系列之二登录窗口界面
继上一篇高仿QQ即时聊天软件开发系列之一开端之后,开始做登录窗口 废话不多说,先看效果,只有界面 可能还有一些细节地方没有做,例如那个LOGO嘛,不要在意这些细节 GIF虽短,可是这做起来真难,好吧因 ...
- 网页调启用qq对话聊天客服窗口的链接地址方法大全(包含移动端)
z转自: http://www.wazhuti.com/1781.html 在PC端,腾讯的QQ软件还是应用最为广泛的即时通讯工具了,除了网站自动的一些对话软件外,qq可以有效的将用户留存下来, ...
- “仿QQ局域网聊天软件”项目-常用编程技巧总结
1 信号槽篇 qqLogin loginDialog; QQ mainDialog; loginDialog.show(); //连接登陆窗口和主窗口 QObject::connect(&lo ...
- 仿QQ局域网聊天软件
1 目的 想复习一下TCP/IP协议,再结合一下以前学的Qt的知识,加上前段时间学的MySQL数据库操作,所以写了个"仿QQ局域网聊天软件"小项目,只实现了一部分功能,还没写完 ...
- 高仿QQ即时聊天软件开发系列之一开端
前段时间在园子里看到一个大神做了一个GG2014IM软件,仿QQ的,那感觉···,赶快下载源码过来试试,还真能直接跑起来,效果也不错.但一看源码,全都给封装到了ESFramework里面了,音视频那部 ...
- 搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)
搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 原文地址(英文):http://www.networkcomms.net/creating ...
随机推荐
- asp.net用sql数据库生成json字符串并显示出来
use Shop ,) )) insert into DictBase select '包装' UNION ALL select '价格' UNION ALL select '品牌' 工厂方法模式 I ...
- 添加tag
创建tag git tag -a V1 -m 'release 1' 创建了本地一个版本v1,同时添加注释 release 1 查看tag git tag 显示注释 git show V1 本地tag ...
- [51Nod] 配对
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1737 求出树的重心,跑spfa #include <iostre ...
- [POI2008]BLO-Blockade 割点
[POI2008]BLO-Blockade 割点 题面 容易想到用\(\text{Tarjan}\)求割点.对于非割点,会损失\(2\times(n-1)\)次访问(注意是互相访问,所以要乘2):对于 ...
- Simple Problem with Integers(POJ 3486)
A Simple Problem with Integers Time Li ...
- Js 之移动端图片上传插件mbUploadify
一.下载 https://pan.baidu.com/s/1NEL4tkHoK4ydqdMi_hgWcw 提取码:vx7e 二.Demo示例 <div class="weui_uplo ...
- Raspberry Pi 摄像头模块入门
目录 一.摄像头模块安装 二.使用命令控制摄像头 三.使用Python程序控制摄像头 四.基于vlc的Raspberry Pi摄像头实时监控 参考资料 Raspberry Pi提供了摄像头模块的接口, ...
- elasticsearch中文分词器(ik)配置
elasticsearch默认的分词:http://localhost:9200/userinfo/_analyze?analyzer=standard&pretty=true&tex ...
- Kafka - SASL认证
kafka SASL认证配置 1.找到kafka安装根目录,在config文件夹下创建kafka_server_jaas.conf,写入 KafkaServer { org.apache.kafka. ...
- varnish web cache服务
varnish介绍 缓存开源解决方案: - varnish - 充分利用epoll机制(能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率),并发量大,单连接资源较轻 - squid ...