最新要闻
- 【前沿】尺寸接近米13!大厂新机渲染图出炉 白色打几分?|全球关注
- 最便宜4系来了!宝马发布i4 eDriver35四门纯电轿跑:大降4万
- 今日视点:胃癌几乎都是吃出来的:钟爱烧烤、熏制食物的朋友要注意
- 女孩用“免作业券”不写暑假作业 网友点赞:凭实力不写-要闻速递
- 每日聚焦:三星Galaxy S23 FE外观曝光:后置长焦三摄、边角圆润
- 差评第一!景区摆渡车不能兜来绕去只为钱
- 我莫得感情也莫得耳朵_我莫得感情哪里的梗
- 续航38小时/能水洗!万魔S50运动蓝牙耳机图赏
- 爸爸给女儿买RTX 4060 Ti显卡当生日礼物 小女孩表情亮了
- 来试试手气?《逆水寒》手游6月30日上线:官方宣布抽奖送鹤岗一套房-环球速读
- 每日消息!比电脑还大:24GB+1TB!今年最能打的直屏旗舰杀疯了
- 迅雷云盘TV版最高可领180天超级会员特权 支持4K电视超清播放-天天信息
- 天天微资讯!广州杨大爷退休后,喝酒不喝茅五剑,青睐三款廉价酒
- 农村三胞胎姐妹均高分超一本线:成绩一直很优秀
- 极氪001欧洲价格公布:起售价47万元 比国内贵17万
- 高管曾言等本土车企“烧死了”再来抢市场!起亚EV6开启盲订 每日速递
手机
光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯
搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注
- 光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯
- 搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注
- 视点!美国首位女总统即将诞生?拜登恐怕要提前下岗,美政坛迎来变局?
- 当前速递!用理想仪器实现更好的颗粒 德国新帕泰克亮相CPHI & PMEC China获好评
- 微粒贷怎么申请开通 开通方法如下
- 焦点简讯:心疼!这位40岁的云南缉毒警,已是满头白发
家电
nethttp和gin 路由
net/http 路由注册
func test1() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello world!") }) err := http.ListenAndServe(":9001", nil) if err != nil { log.Fatal("ListenAndServer:", err) }}
在使用ListenAndServe
这个方法时,系统就会给我们指派一个路由器,DefaultServeMux
是系统默认使用的路由器,如果ListenAndServe
这个方法的第2个参数传入nil,系统就会默认使用DefaultServeMux
。当然,这里也可以传入自定义的路由器。
(资料图片)
先看http.HandleFunc("/", ...)
,从HandleFunc
方法点进去,如下:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}
在这里调用了DefaultServeMux
的HandleFunc
方法,这个方法有两个参数,pattern
是匹配的路由规则,handler
表示这个路由规则对应的处理方法,并且这个处理方法有两个参数。
在我们书写的代码示例中,pattern
对应/
,handler
对应sayHello
,当我们在浏览器中输入http://localhost:9001
时,就会触发匿名函数。
我们再顺着DefaultServeMux
的HandleFunc
方法继续点下去,如下:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler))}
在这个方法中,路由器又调用了Handle
方法,注意这个Handle
方法的第2个参数,将之前传入的handler
这个响应方法强制转换成了HandlerFunc
类型。
这个HandlerFunc
类型到底是个什么呢?如下:
type HandlerFunc func(ResponseWriter, *Request)
看来和我们定义的"/"的匿名函数的类型都差不多。但是!!! 这个HandlerFunc
默认实现了ServeHTTP
接口!这样HandlerFunc
对象就有了ServeHTTP
方法!如下:
// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}
接下来,我们返回去继续看mux
的Handle
方法,也就是这段代码mux.Handle(pattern, HandlerFunc(handler))
。这段代码做了哪些事呢?源码如下
// Handle registers the handler for the given pattern.// If a handler already exists for pattern, Handle panics.func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern") } if handler == nil { panic("http: nil handler") } if _, exist := mux.m[pattern]; exist { panic("http: multiple registrations for " + pattern) } if mux.m == nil { mux.m = make(map[string]muxEntry) } e := muxEntry{h: handler, pattern: pattern} mux.m[pattern] = e if pattern[len(pattern)-1] == "/" { mux.es = appendSorted(mux.es, e) } if pattern[0] != "/" { mux.hosts = true }}
主要就做了一件事,向DefaultServeMux
的map[string]muxEntry
中增加对应的路由规则和handler
。
map[string]muxEntry
是个什么鬼?
map
是一个字典对象,它保存的是key-value
。[string]
表示这个字典的key
是string
类型的,这个key
值会保存我们的路由规则。muxEntry
是一个实例对象,这个对象内保存了路由规则对应的处理方法。mux.es
为模糊匹配 有长倒短排序 比如有路由/hello/
访问/hello/world
时没有路由 会落到/hello/
上
找到相应代码,如下:
// 路由器type ServeMux struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames}type muxEntry struct { h Handler pattern string}// 路由响应方法type Handler interface { ServeHTTP(ResponseWriter, *Request)}
net/http 运行
第二部分主要就是研究这句代码err := http.ListenAndServe(":9001",nil)
,也就是ListenAndServe
这个方法。从这个方法点进去,如下:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}
在这个方法中,初始化了一个server
对象,然后调用这个server
对象的ListenAndServe
方法,在这个方法中,如下:
func (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed } addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(ln)}
在这个方法中,调用了net.Listen("tcp", addr)
,也就是底层用TCP协议搭建了一个服务,然后监控我们设置的端口。
代码的最后,调用了srv
的Serve
方法,如下:
func (srv *Server) Serve(l net.Listener) error { if fn := testHookServerServe; fn != nil { fn(srv, l) // call hook with unwrapped listener } origListener := l l = &onceCloseListener{Listener: l} defer l.Close() if err := srv.setupHTTP2_Serve(); err != nil { return err } if !srv.trackListener(&l, true) { return ErrServerClosed } defer srv.trackListener(&l, false) baseCtx := context.Background() if srv.BaseContext != nil { baseCtx = srv.BaseContext(origListener) if baseCtx == nil { panic("BaseContext returned a nil context") } } var tempDelay time.Duration // how long to sleep on accept failure ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { rw, err := l.Accept() if err != nil { select { case <-srv.getDoneChan(): return ErrServerClosed default: } if ne, ok := err.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay) time.Sleep(tempDelay) continue } return err } connCtx := ctx if cc := srv.ConnContext; cc != nil { connCtx = cc(connCtx, rw) if connCtx == nil { panic("ConnContext returned nil") } } tempDelay = 0 c := srv.newConn(rw) c.setState(c.rwc, StateNew, runHooks) // before Serve can return go c.serve(connCtx) }}
最后3段代码比较重要,也是Go语言支持高并发的体现,如下:
c := srv.newConn(rw)c.setState(c.rwc, StateNew, runHooks) // before Serve can returngo c.serve(connCtx)
上面那一大坨代码,总体意思是进入方法后,首先开了一个for
循环,在for
循环内时刻Accept请求,请求来了之后,会为每个请求创建一个Conn
,然后单独开启一个goroutine
,把这个请求的数据当做参数扔给这个Conn
去服务:go c.serve()
。用户的每一次请求都是在一个新的goroutine
去服务,每个请求间相互不影响。
在conn
的serve
方法中,有一句代码很重要,如下:
serverHandler{c.server}.ServeHTTP(w, w.req)
表示serverHandler
也实现了ServeHTTP
接口,ServeHTTP
方法实现如下:
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") { var allowQuerySemicolonsInUse int32 req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() { atomic.StoreInt32(&allowQuerySemicolonsInUse, 1) })) defer func() { if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 { sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192") } }() } handler.ServeHTTP(rw, req)}
在这里如果handler
为空(这个handler
就可以理解为是我们自定义的路由器),就会使用系统默认的DefaultServeMux
,代码的最后调用了DefaultServeMux
的ServeHTTP()
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) //这里返回的h是Handler接口对象 h.ServeHTTP(w, r) //调用Handler接口对象的ServeHTTP方法实际上就调用了我们定义的sayHello方法}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // CONNECT requests are not canonicalized. if r.Method == "CONNECT" { // If r.URL.Path is /tree and its handler is not registered, // the /tree -> /tree/ redirect applies to CONNECT requests // but the path canonicalization does not. if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } return mux.handler(r.Host, r.URL.Path) } // All other requests have any port stripped and path cleaned // before passing to mux.handler. host := stripHostPort(r.Host) path := cleanPath(r.URL.Path) // If the given path is /tree and its handler is not registered, // redirect for /tree/. if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } if path != r.URL.Path { _, pattern = mux.handler(host, path) u := &url.URL{Path: path, RawQuery: r.URL.RawQuery} return RedirectHandler(u.String(), StatusMovedPermanently), pattern } return mux.handler(host, r.URL.Path)}func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { mux.mu.RLock() defer mux.mu.RUnlock() // Host-specific pattern takes precedence over generic ones if mux.hosts { h, pattern = mux.match(host + path) } if h == nil { h, pattern = mux.match(path) } if h == nil { h, pattern = NotFoundHandler(), "" } return}func (mux *ServeMux) match(path string) (h Handler, pattern string) { // Check for exact match first. v, ok := mux.m[path] if ok { return v.h, v.pattern } // Check for longest valid match. mux.es contains all patterns // that end in / sorted from longest to shortest. for _, e := range mux.es { if strings.HasPrefix(path, e.pattern) { return e.h, e.pattern } } return nil, ""}
它会根据用户请求的URL
到路由器里面存储的map
中匹配,匹配成功就会返回存储的handler
,调用这个handler
的ServeHTTP()
就可以执行到相应的处理方法了,这个处理方法实际上就是我们刚开始定义的sayHello()
,只不过这个sayHello()
被HandlerFunc
又包了一层,因为HandlerFunc
实现了ServeHTTP
接口,所以在调用HandlerFunc
对象的ServeHTTP()
时,实际上在ServeHTTP ()
的内部调用了我们的sayHello()
。
总结
- 调用
http.ListenAndServe(":9090",nil)
- 实例化
server
- 调用
server
的ListenAndServe()
- 调用
server
的Serve
方法,开启for
循环,在循环中Accept请求 - 对每一个请求实例化一个
Conn
,并且开启一个goroutine
为这个请求进行服务go c.serve()
- 读取每个请求的内容
c.readRequest()
- 调用
serverHandler
的ServeHTTP()
,如果handler
为空,就把handler
设置为系统默认的路由器DefaultServeMux
- 调用
handler
的ServeHTTP()
=>实际上是调用了DefaultServeMux
的ServeHTTP()
- 在
ServeHTTP()
中会调用路由对应处理handler
- 在路由对应处理
handler
中会执行sayHello()
有一个需要注意的点: DefaultServeMux
和路由对应的处理方法handler
都实现了ServeHTTP
接口,他们俩都有ServeHTTP
方法,但是方法要达到的目的不同,在DefaultServeMux
的ServeHttp()
里会执行路由对应的处理handler
的ServeHttp()
。
自定义个简单的路由
package muximport ( "net/http" "strings")type muxEntry struct { h TesthandleFunc}type TesthandleFunc func(http.ResponseWriter, *http.Request)type TestHandler struct { routes map[string]map[string]muxEntry}func (h *TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { method := strings.ToUpper(r.Method) path := r.URL.Path if route, ok := h.routes[method]; ok { if entry, ok := route[path]; ok { entry.h(w, r) return } } w.WriteHeader(http.StatusNotFound)}func Newhandler() *TestHandler { return &TestHandler{routes: make(map[string]map[string]muxEntry)}}func (h *TestHandler) Handle(method, path string, handler TesthandleFunc) { method = strings.ToUpper(method) if _, ok := h.routes[method]; !ok { h.routes[method] = make(map[string]muxEntry) } h.routes[method][path] = muxEntry{handler}}
package mainimport ( "fmt" "net/http" "study/mux")func main() { handler := mux.Newhandler() handler.Handle("GET", "/hello", func(rw http.ResponseWriter, r *http.Request) { rw.Write([]byte("Hello World")) }) handler.Handle("Post", "/hello/world", func(rw http.ResponseWriter, r *http.Request) { fmt.Fprintln(rw, "你好") }) http.ListenAndServe(":9002", handler)}
自定义context
package routerimport ( "encoding/json" "net/http" "strings")type Context struct { w http.ResponseWriter r *http.Request}func (c *Context) Json(code int, v interface{}) { c.w.Header().Set("Content-Type", "application/json") c.w.WriteHeader(code) s, _ := json.Marshal(v) c.w.Write(s)}type Routerfunc func(c *Context)type RouterHandler struct { routes map[string]map[string]Routerfunc}func NewRouterHandler() *RouterHandler { return &RouterHandler{routes: make(map[string]map[string]Routerfunc)}}func (h *RouterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { method := strings.ToUpper(r.Method) path := r.URL.Path c := &Context{w: w, r: r} if route, ok := h.routes[method]; ok { if h, ok := route[path]; ok { h(c) return } } w.WriteHeader(http.StatusNotFound)}func (h *RouterHandler) Handle(method, path string, handler Routerfunc) { method = strings.ToUpper(method) if _, ok := h.routes[method]; !ok { h.routes[method] = make(map[string]Routerfunc) } h.routes[method][path] = handler}func (r *RouterHandler) Run(addr string) error { return http.ListenAndServe(addr, r)}
Gin
type Engine struct { RouterGroup pool sync.Pool trees methodTrees}// trietype RouterGroup struct { basePath string engine *Engine}func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { c := engine.pool.Get().(*Context) // 从pool 拿出一个context c.writermem.reset(w) // 记录http.ResponseWriter 及 *http.Request c.Request = req c.reset() // 重置上一个留下的值 engine.handleHTTPRequest(c) engine.pool.Put(c) // 把用完的context放回池子}// get: /bac
添加路由
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj()}
Context
type Context struct { Request *http.Request Writer ResponseWriter Params Params handlers HandlersChain index int8 fullPath string engine *Engine params *Params skippedNodes *[]skippedNode // This mutex protect Keys map mu sync.RWMutex // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} // Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs // Accepted defines a list of manually accepted formats for content negotiation. Accepted []string // queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query() queryCache url.Values // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH, // or PUT body parameters. formCache url.Values // SameSite allows a server to define a cookie attribute making it impossible for // the browser to send this cookie along with cross-site requests. sameSite http.SameSite}func (c *Context) Next() { c.index++ for c.index < int8(len(c.handlers)) { c.handlers[c.index](c) c.index++ }}
func (engine *Engine) handleHTTPRequest(c *Context) { httpMethod := c.Request.Method rPath := c.Request.URL.Path unescape := false if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 { rPath = c.Request.URL.RawPath unescape = engine.UnescapePathValues } if engine.RemoveExtraSlash { rPath = cleanPath(rPath) } // Find root of the tree for the given HTTP method t := engine.trees for i, tl := 0, len(t); i < tl; i++ { if t[i].method != httpMethod { continue } root := t[i].root // Find route in tree value := root.getValue(rPath, c.params, c.skippedNodes, unescape) if value.params != nil { c.Params = *value.params } if value.handlers != nil { c.handlers = value.handlers c.fullPath = value.fullPath c.Next() c.writermem.WriteHeaderNow() return } if httpMethod != http.MethodConnect && rPath != "/" { if value.tsr && engine.RedirectTrailingSlash { redirectTrailingSlash(c) return } if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) { return } } break } if engine.HandleMethodNotAllowed { for _, tree := range engine.trees { if tree.method == httpMethod { continue } if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil { c.handlers = engine.allNoMethod serveError(c, http.StatusMethodNotAllowed, default405Body) return } } } c.handlers = engine.allNoRoute serveError(c, http.StatusNotFound, default404Body)}
关键词:
nethttp和gin 路由
文心一言 VS 讯飞星火 VS chatgpt (50)-- 算法导论6.2 2题-简讯
Golang 简单的数据对齐可提高程序速度和内存使用率_环球新消息
【前沿】尺寸接近米13!大厂新机渲染图出炉 白色打几分?|全球关注
最便宜4系来了!宝马发布i4 eDriver35四门纯电轿跑:大降4万
今日视点:胃癌几乎都是吃出来的:钟爱烧烤、熏制食物的朋友要注意
女孩用“免作业券”不写暑假作业 网友点赞:凭实力不写-要闻速递
每日聚焦:三星Galaxy S23 FE外观曝光:后置长焦三摄、边角圆润
差评第一!景区摆渡车不能兜来绕去只为钱
【Java】使用 fasterxml.jackson 进行反序列化以及部分注意事项
微动态丨可观测性是什么? 入门指南
WPF MVVM之点滴分享
Donaukit用户创建
【财经分析】东北地区国资国企改革深化推进 或迎来哪些针对性政策?
我莫得感情也莫得耳朵_我莫得感情哪里的梗
续航38小时/能水洗!万魔S50运动蓝牙耳机图赏
爸爸给女儿买RTX 4060 Ti显卡当生日礼物 小女孩表情亮了
来试试手气?《逆水寒》手游6月30日上线:官方宣布抽奖送鹤岗一套房-环球速读
每日消息!比电脑还大:24GB+1TB!今年最能打的直屏旗舰杀疯了
迅雷云盘TV版最高可领180天超级会员特权 支持4K电视超清播放-天天信息
天天微资讯!广州杨大爷退休后,喝酒不喝茅五剑,青睐三款廉价酒
强化学习从基础到进阶-常见问题和面试必知必答[8]:近端策略优化(proximal policy optimization,PPO)算法
债市日报:6月28日|全球新视野
债市日报:6月28日
农村三胞胎姐妹均高分超一本线:成绩一直很优秀
极氪001欧洲价格公布:起售价47万元 比国内贵17万
高管曾言等本土车企“烧死了”再来抢市场!起亚EV6开启盲订 每日速递
今天起 韩国人集体“年轻一两岁”:与一项法案有关
XREAL Beam投屏盒子首次亮相MWC!AR空间屏体验太酷了
一般小县城稀缺行业有哪些 今日热搜
跑得更快!华为云GaussDB以出色的性能守护“ERP的心脏”
记录--不定高度展开收起动画 css/js 实现
今日播报!Cognos教程_编程入门自学教程_菜鸟教程-免费教程分享
LRU 缓存淘汰算法
Mac反编译安卓APK
世界看点:原来是他们!退市股获举牌4个交易日翻倍,步步高系大佬要进董事会
【金融街发布】财政部:5月地方债发行规模为7554亿元 新增债券占比近四成_即时看
天天即时看!注意!天孚通信:股东朱国栋计划减持公司股份不超过约395万股
游戏主播被大额打赏后剃光头!结果被坑惨:打赏退款了
尽享丝滑!MWC现场体验领克08魅族Flyme Auto车机:流畅度爆表 环球新要闻
焦点资讯:萧敬腾求婚好多地方下雨 “雨神”真有这么神?
15万的特斯拉廉价新车Model 2跳票了:延期到2025年
今日快讯:能否追上高铁?凯迪拉克CT5-V推出全新改装套件 动力可达1000匹
js的Map数据类型
Sudo堆溢出漏洞(CVE-2021-3156)复现
Spring 赌上未来一击,推出响应式框架 WebFlux,代码更优雅,性能更强! 最新
解锁高质量文档转换:通过Java应用程序免费将PDF转换为XPS
每日看点!未来可期!农村三胞胎姐妹高分同超一本线
北交所日报:指数调整回补缺口 曙光鼎智双双大涨
特斯拉Model Y的劲敌来了 溜背造型+电动尾翼 全新蔚来EC6实车曝光 报道
滚动:中国互联网协会重申:我国人工智能已进入全球第一梯队
Intel Arc显卡驱动打鸡血:游戏性能飙升最高3.13倍!
全球热点评!骁龙8 Gen2没到极限!24GB运存手机不是终点 你觉得有必要吗?
高温再度来袭!北方气温将高于南方四大火炉城市|环球即时看
ST深天: 关于落实《关于中国证监会深圳监管局行政监管措施决定书的书面整改报告》相关整改措施的公告-全球报道
php如何解决高并发|每日热门
今日讯!业务安全情报第十七期 | 国际航班上,小“票代”在疯狂倒卖高价票
在 Java、Python、JavaScript 和 Go 中拥抱异步
数据交换不失控:华为云EDS,让你的数据你做主 环球微头条
天天速递!知嘛家整屋案例 | 116㎡现代简约风,超强收纳颜值功能拉满!
【新华500】新华500指数(989001)28日探底回升微跌0.11%
当前观点:索尼推送满血包:ZV-E1相机支持4K/120p和1080P/240p
车主看后想骂人 特斯拉一充电站全被“割了”:只剩桩不见枪
天天热消息:河北邢台雷雨夜震撼闪电撕破夜空:闪电如怒吼银蛇
警惕!侧躺着玩手机可能会影响你的颜值 同时威胁健康
【世界聚看点】80%毕业生都出国了 是马斯克公司最大“生源地”?清华大学发声
腾讯在深圳宝安启用三个与AI大模型相关的平台
解决TrueNAS中Smb共享文件路径不区分大小写的问题 每日精选
GIS地形场景的应用与前景:从美景欣赏到环境保护与城市规划|焦点热门
讯息:遥望科技启动商家大会,瑜大公子现身会场分享进阶故事
焦点热文:助力首都建设 浦发银行北京分行发售北京市地方政府柜台债券
今天解禁!RTX 4060终于来了 2399元买吗?
芯片之母 是德EDA升级支持6G设计:速度快10倍|当前热门
王宝强做客东方甄选!被俞敏洪问是否还相信爱情
24GB内存时代来了!一加真我全都上24GB超大内存:性能激进 当前播报
CDPR:《赛博朋克2077》首发没那么糟糕 都怪跟风黑 全球今日讯
天天速看:全国电力行业学习宣传贯彻习近平新时代中国特色社会主义思想和党的二十大精神宣讲比赛举办
热点在线丨MongoDB(二)
奥特银河格斗:被官方遗忘的形态与奥特曼阿古茹成为了全剧最惨
当前热文:男子在动车上霸座充电被行拘 网友惊叹处罚严厉:7天还少?
华为孟晚舟:5.5G是必然之路、下行可达万兆!
世界热议:上海发布高温黄色预警,预计本市大部地区最高温将超过35度
过烂路前避震可能会断 丰田召回近60万辆汽车|当前速看
核污水强行入海无视反对!日本官方全面检查排海设备:污染倒计时
环球通讯!致6死20余伤!广州宝马撞人案司机二审结果出炉:被判死刑
天天最资讯丨大学生游戏本怎么选?RTX4060加持 华硕天选4锐龙版到手7999元
【世界独家】世界最丑狗狗比赛胜者决出:七岁中国冠毛犬夺冠
焦点快看:众智科技:6月27日融资买入551.57万元,融资融券余额4327.64万元
[python] 基于matplotlib-scalebar库绘制比例尺
易基因|表观遗传学与脑卒中:DNA甲基化的作用及衰老对血脑屏障修复的影响
RabbitMQ的死信队列,延时队列
复旦微电间接股东章勇被批评 正拟发不超20亿可转债|环球观点
Uzi请假原因找到了!担任亚运会《英雄联盟》国家队教练:指导下路
5G专利全球第一 华为向日本公司收专利费:日企有三大麻烦了
通讯!笔记本电脑坐上过山车 下半年或复苏
全球通讯!任天堂打造!《超级马力欧兄弟大电影》7月14日上线网播:B站、腾讯已预告
多女子开直播看望杭州跳河救人外卖小哥 本人回应:别利用我带货
安徽省利辛县发布大风蓝色预警 环球关注
直播系统源码知识分享:解你忧愁!降低直播延迟的实现 全球热议
焦点信息:2023成都迎大运蓉港公益足球赛直播时间+入口