golang 入门教程:迷你 Twitter 后端
请记住,这个项目主要是为了稍微熟悉下Golang,您可以复制架构,但该项目缺少适当的 ORM,没有适当的身份验证或授权,我完全无视中间件,也没有测试。
我将在其自己的部分中讨论所有这些问题,但重要的是你要知道这还没有准备好投入生产。
如果我必须从头开始或重新制作项目,我会添加诸如 sqlx 和 Gorm 之类的库。 以及改进 API 和我在下面进行的其他更改。
此外,我想谈谈我正在使用的路由库:Fiber。 请注意,它是版本 2,但是很多教程和博客文章都在展示和谈论 v1,此后发生了一些变化,因此在查找其他信息时确保导入是 github.com/gofiber/fiber/v2。
此外,您绝对应该在项目中有一个配置文件和一个 .env 来隐藏您的数据。
README 中还有一个免责声明:
免责声明
这是一个介绍性的项目,可以稍微了解一下 Golang。 这个项目还没有准备好生产,它有不好的做法,比如分页的工作方式或我们与数据库的交互方式。
通过代码库,您会发现用于调试项目的不同“打印件”,请随意使用它们。 生产就绪缺少什么?
您应该添加适当的记录器、配置、中间件、处理数据的不同方式(可能是 ORM)、处理分页的更好方式以及更好的 API。
请注意,如果您复制并粘贴代码,可能会使自己容易受到 SQL 注入的攻击。 这是出于学习目的而制作的。
Database
您可能应该让 Docker 运行 MySQL(或任何其他 SQL 数据库)。 如果没有,你总是可以在你的系统上安装 MySQL 并使用像 MySQL Workbench 这样的东西来处理它。
Database design
数据库本身相当简单,你有发布推文的用户和一个关注者表来保存谁关注谁的数据。关注者表主要是为我们的用户实现提要/时间线。
Create Database
在脚本文件夹中,您将找到运行以启动 MySQL 数据库的主要脚本:
use twitterdb;DROP TABLE IF EXISTS tweets;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS followers;CREATE TABLE users (user_id INT NOT NULL AUTO_INCREMENT,user VARCHAR(255) NOT NULL,passhash VARCHAR(40) NOT NULL,email VARCHAR(255) NOT NULL,first_name VARCHAR(255) NOT NULL,last_name VARCHAR(255) NOT NULL,dob DATE,PRIMARY KEY (user_id)
);CREATE TABLE tweets (tweet_id INT NOT NULL AUTO_INCREMENT,user_id INT NOT NULL,tweet VARCHAR(140) NOT NULL,date_tweet DATETIME NOT NULL,PRIMARY KEY (tweet_id),FOREIGN KEY user_id(user_id) REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE CASCADE
);CREATE TABLE followers (id_user INT NOT NULL REFERENCES users (user_id),id_follower INT NOT NULL REFERENCES users (user_id),PRIMARY KEY (id_user, id_follower)
);INSERT INTO users (user, passhash, email, first_name, last_name, dob) VALUES
("foo", "asdsad1", "test@gmail.com", "bob", "bobbinson", "2006-01-01"),
("foo2", "asdsad2", "test2@gmail.com", "bob2", "bobbinson2", "1992-01-01"),
("foo3", "asdsad3", "test3@gmail.com", "bob3", "bobbinson3", "1993-01-01"),
("foo4", "asdsad4", "test4@gmail.com", "bob4", "bobbinson4", "1994-01-01"),
("foo5", "asdsad5", "test5@gmail.com", "bob5", "bobbinson5", "1995-01-01"),
("foo6", "asdsad6", "test6@gmail.com", "bob6", "bobbinson6", "1996-01-01"),
("foo7", "asdsad7", "test7@gmail.com", "bob7", "bobbinson7", "1925-01-01"),
("foo8", "asdsad8", "test8@gmail.com", "bob8", "bobbinson8", "1980-01-01"),
("foo9", "asdsad9", "test9@gmail.com", "bob9", "bobbinson9", "1980-01-01"),
("foo10", "asdsad10", "test10@gmail.com", "bob10", "bobbinson10", "1970-01-01");INSERT INTO tweets(user_id, tweet, date_tweet) VALUES
(1, "test tweet", "2001-01-01 22:00:00"),
(2, "test tweet2", "2002-01-01 22:00:00"),
(3, "test tweet3", "2003-01-01 22:00:00"),
(4, "test tweet4", "2004-01-01 22:00:00"),
(5, "test tweet5", "2005-01-01 22:00:00");INSERT INTO followers(id_user, id_follower) VALUES
(5,1),
(4,1),
(3,1),
(2,1),
(6,1),
(2,5),
(4,5);
其他文件是我们稍后将使用的查询示例。
Project Architecture
如果您熟悉构建软件,如果不熟悉这应该很熟悉,首先让我告诉您缺少什么,然后我们将遍历每个文件夹。
该项目缺少配置、中间件、记录器和测试等文件夹。 如果你能更好地组织控制器,你就会有一个路由文件夹,你可以更好地组织 API。
关于导入包的说明
在这个项目中,您可能会对以下导入感到困惑:
import ("goexample/database""goexample/models""goexample/services/utils"
)
这里的 goexample 是项目的名称,我只是在之后重命名了我的 repo 这样它才有意义,所以我们使用“goexample/database”而不是“mini-twitter-clone/database”来导入数据库包。
对于任何其他新项目,只需使用文件夹的名称。
API
路由存储在 controller.go 中:
package apiimport ("goexample/services""github.com/gofiber/fiber/v2"
)func SetupRoutes(app *fiber.App) {api := app.Group("/api")//get all unordened usersapi.Get("/users", services.GetUsers)//get all users ordered by age ASCapi.Get("/users/age", services.GetUsersByAgeAsc)//get all unordened tweets from dbapi.Get("/tweets", services.GetTweets)//http://localhost:3000/api/feed/1//get MOST RECENT feed/timeline for the userapi.Get("/feed/:id", services.GetFeedTweets)//paginationapi.Get("/feed/:id/:limit/:offset", services.GetFeedTweetsPaginated)//can try https://github.com/gofiber/fiber/issues/193#issuecomment-591976894//a whole presentation on why you shouldn't do what I did://https://use-the-index-luke.com/no-offset}
我正在使用类似于 Express 的 Fiber 库,这里我们有几个端点,我们通过 Get 请求调用这些端点,一旦服务器收到请求,它的响应就是调用我从服务包中公开的函数——那就是 我们的业务逻辑在哪里。
注意:分页未正确实现。
这只是一个简单的示例,此端点与其他端点一样存在安全问题,但您应该通过示例了解库和 Golang 的工作方式。
请注意,在 /feed/:id 中,id 参数与我们要为其获取提要的用户有关。
启动项目
您可以通过执行 go run 来启动项目。 文件夹内。
您的终端应如下所示:
让我们访问不同的端点以查看响应,我将使用普通浏览,但如果您不熟悉调试后端,则应检查 Postman。
让我们从控制器运行端点:
http://127.0.0.1:3000/api/users
我留下了很多打印输出,所以如果你在每次通话时检查你的终端,你应该会看到如下内容:
接下来是我们按年龄对用户排序的调用:
http://127.0.0.1:3000/api/users/age
当我们请求推文时,我们会得到所有的推文:
http://127.0.0.1:3000/api/tweets
现在我们获取 ID 为 1 的用户的提要或时间线:
http://127.0.0.1:3000/api/feed/1
请记住,分页尚未准备好生产,但核心概念是相同的:
http://127.0.0.1:3000/api/feed/1/2/1
由于我们在服务包中的业务逻辑和我们公开的功能,所有调用都能正常工作。
Services
在服务包中,我们有当某人或某物点击其中一个端点时调用的函数。 请记住,我们通过在包中使用大写字母来公开功能。
让我们看一下 timeline_tweets.go,它包含 controller.go 文件中两个不同端点的两个函数:
package servicesimport ("fmt""goexample/database""goexample/models""goexample/services/utils""log""github.com/gofiber/fiber/v2"
)func GetFeedTweets(c *fiber.Ctx) error {//you shouldn't do this by the way, but it's just a demo// dbQuery := fmt.Sprintf("SELECT users.user_id, users.user, users.first_name, users.last_name, tweets.tweet, tweets.date_tweet FROM users INNER JOIN tweets ON users.user_id = tweets.user_id INNER JOIN followers ON users.user_id = followers.id_user WHERE followers.id_follower = %s ORDER BY tweets.date_tweet DESC;", c.Params("id"))// rows, err := database.DB.Query(dbQuery)//avoid the SQL injection by rewriting it likedbQuery := "SELECT users.user_id, users.user, users.first_name, users.last_name, tweets.tweet, tweets.date_tweet FROM users INNER JOIN tweets ON users.user_id = tweets.user_id INNER JOIN followers ON users.user_id = followers.id_user WHERE followers.id_follower = ? ORDER BY tweets.date_tweet DESC;"rows, err := database.DB.Query(dbQuery, c.Params("id"))//check for errorsif err != nil {return utils.DefaultErrorHandler(c, err)}//close db connectiondefer rows.Close()//create a slice of tweetsvar timelineTweets []models.TimelineTweet//loop through the result setfor rows.Next() {timelineTweet := models.TimelineTweet{}err := rows.Scan(&timelineTweet.User_id, &timelineTweet.User, &timelineTweet.First_name, &timelineTweet.Last_name, &timelineTweet.Tweet, &timelineTweet.Date_tweet)if err != nil {log.Fatal(err)}timelineTweets = append(timelineTweets, timelineTweet)}fmt.Print(timelineTweets)utils.ResponseHelperJSON(c, timelineTweets, "timeline", "No timeline found")return err
}func GetFeedTweetsPaginated(c *fiber.Ctx) error {// dbQuery := fmt.Sprintf("SELECT users.user_id, users.user, users.first_name, users.last_name, tweets.tweet, tweets.date_tweet FROM users INNER JOIN tweets ON users.user_id = tweets.user_id INNER JOIN followers ON users.user_id = followers.id_user WHERE followers.id_follower = %s ORDER BY tweets.date_tweet DESC LIMIT %s OFFSET %s;", c.Params("id"), c.Params("limit"), c.Params("offset"))// avoid a SQL injection by rewriting it likedbQuery := "SELECT users.user_id, users.user, users.first_name, users.last_name, tweets.tweet, tweets.date_tweet FROM users INNER JOIN tweets ON users.user_id = tweets.user_id INNER JOIN followers ON users.user_id = followers.id_user WHERE followers.id_follower = ? ORDER BY tweets.date_tweet DESC LIMIT ? OFFSET ?;"rows, err := database.DB.Query(dbQuery, c.Params("id"), c.Params("limit"), c.Params("offset"))if err != nil {return utils.DefaultErrorHandler(c, err)}defer rows.Close()var timelineTweets []models.TimelineTweetfor rows.Next() {timelineTweet := models.TimelineTweet{}err := rows.Scan(&timelineTweet.User_id, &timelineTweet.User, &timelineTweet.First_name, &timelineTweet.Last_name, &timelineTweet.Tweet, &timelineTweet.Date_tweet)if err != nil {log.Fatal(err)}timelineTweets = append(timelineTweets, timelineTweet)}//TODO: implement a response with pages and all that pagination jazzutils.ResponseHelperJSON(c, timelineTweets, "timeline", "No timeline found")return err
}
您首先注意到的是我们如何将上下文传递给 GetFeedTweets,然后我们使用变量“c”来使用 Fiber 库所说的上下文。
之后,我们使用包数据库中公开的变量 DB 打开 DB,读取数据,然后关闭它。
为了正确存储和扫描数据,我们使用模型包中的结构。
之后您将看到 utils 包中的几个函数。 这些函数主要是辅助函数,因此您可以查看 Golang 如何执行循环和某些其他操作。
我们的服务包中的其他两个文件非常相似,我们在这里的工作是从数据库中获取数据,通过我们的结构数组扫描它,然后以 JSON 格式将其发送回用户。 以及我们运行 utils 包中的一些功能。
Utils
这个包包含辅助函数,也许最有趣的是在 user_helper 里面,因为它有与切片数据交互的函数,但是要小心,它们的实现并不像你想象的那么好。
让我们看看最有帮助的,response_helper.go
package utilsimport ("github.com/gofiber/fiber/v2"
)//response JSON for services after you loop and scan
func ResponseHelperJSON(c *fiber.Ctx, data any, dataType string, dataError string) {if data != nil {c.Status(200).JSON(&fiber.Map{"success": true,dataType: data,})} else {c.Status(404).JSON(&fiber.Map{"success": false,"error": dataError,})}
}
请注意,此函数包含通用类型的数据,因为我使用关键字 any 并且我正在与 Fiber 通过变量“c”提供的上下文进行交互。
Models
models 文件夹包含我们用作与数据库交互的实体的不同结构,如 services 文件夹中所示。
这是一个示例,请注意,即使您想公开整个结构,所有内容都必须以大写字母开头:
package modelstype UserWithAge struct{Id int `json:"id"`User string `json:"user"`Passhash string `json:"passhash"`Email string `json:"email"`First_name string `json:"first_name"`Last_name string `json:"last_name"`Age int `json:"age"`
}
Database
数据库包非常简单,请记住使用配置文件和 .env 来存储您的敏感数据。
package databaseimport ("database/sql""fmt""log"
)var DB *sql.DBfunc Connect() error{var err error//use a config file for thisDB, err = sql.Open("mysql", "root:password@tcp(127.0.0.1:3306)/twitterdb")if err != nil {log.Fatal(err)return err}if err = DB.Ping(); err != nil {log.Fatal(err)return err}fmt.Println("Connected to database")return nil}
Main.go
最后,这是我们启动服务器的地方:
package mainimport ("github.com/gofiber/fiber/v2""goexample/database""log""goexample/api"_ "github.com/go-sql-driver/mysql"
)func main() {if err := database.Connect(); err != nil {log.Fatal(err)}app := fiber.New()api.SetupRoutes(app)log.Fatal(app.Listen(":3000"))}
相关文章:
golang 入门教程:迷你 Twitter 后端
请记住,这个项目主要是为了稍微熟悉下Golang,您可以复制架构,但该项目缺少适当的 ORM,没有适当的身份验证或授权,我完全无视中间件,也没有测试。 我将在其自己的部分中讨论所有这些问题,但重要的…...
CPP2022-30-期末模拟测试03
6-1 引用作函数形参交换两个整数 分数 5 全屏浏览题目 切换布局 作者 李廷元 单位 中国民用航空飞行学院 设计一个void类型的函数Swap,该函数有两个引用类型的参数,函数功能为实现两个整数交换的操作。 裁判测试程序样例: #include <…...
华为OD机试真题Python实现【最多等和不相交连续子序列】真题+解题思路+代码(20222023)
🔥系列专栏 华为OD机试(Python)真题目录汇总华为OD机试(JAVA)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出示例一输入输出说明示例二输入输出说明...
二叉搜索树
1.二叉搜索树 1.1.二叉搜索树概念 二叉搜索树又称二叉排序树,它或者是一颗空树,或者是具有一下性质的二叉树。 若它的左子树不为空,则左子树上的所有节点的值都小于根节点的值。若它的右子树不为空,则右子树上的所有节点的值都…...
数据结构(三):集合、字典、哈希表
数据结构(三)一、集合(Set)1.封装一个集合类2.集合常见的操作(1)并集(2)交集(3)差集(4)子集二、字典(Map)三、…...
Linux内核驱动开发(一)
Linux内核初探 linux操作系统历史 开发模式 git 分布式管理git clone 获取git push 提交git pull 更新 邮件组 mailing list patch 内核代码组成 Makfile arch 体系系统架构相关 block 块设备 crypto 加密算法 drivers 驱动(85%) atm 通信bluet…...
TCP/IP协议二十问
TCP/IP协议二十问 1. 什么是TCP网络分层? TCP网络分层一般分为五层: 应用层(HTTP):组装数据包传输层(TCP):增加TCP头部,包含端口号等信息网络互联层(IP&am…...
常用Array数组操作方法
定义一个测试数组constplayers[{name:科比,num:24},{name:詹姆斯,num:23},{name:保罗,num:3},{name:威少,num:0},{name:杜兰特,num:35}]复制代码1、forEach参数代表含义item:遍历项index:遍历项的索引arr:数组本身Array.prototype.sx_forEach…...
【C++】set/multiset、map/multimap的使用
目录 一、关联式容器 二、set的介绍 1、接口count与容器multiset 2、接口lower_bound和upper_bound 三、map的介绍 1、接口insert 2、接口insert和operator[]和at 3、容器multimap 四、map和set相关OJ 1、前K个高频单词 2、两个数组的交集 一、关联式容器 vector、…...
vue3语法
vue3教程 //ps 这里是基本写法 一般项目不需要ref 因为需要一直return 这里是根据在不使用ts后缀 来在.vue里面写setup 如下图所示:setup setup是启动页面会自动执行的一个函数 项目里定义的所有变量,都要在setup当中 在setup定义的变量和方法,都需要r…...
对象之间的关系
目录1. 依赖2. 关联3. 聚合4. 组合Java的对象/类之间有四种关系:依赖、关联、组合、聚合。 1. 依赖 依赖(Dependency): 一个对象的功能依赖于另一个对象。 类比:人类生存依赖食物和空气 体现:被依赖者体…...
云原生时代顶流消息中间件Apache Pulsar部署实操-上
文章目录安装运行时Java版本推荐Locally Standalone集群启动验证部署分布式集群部署说明初始化集群元数据部署BookKeeper部署BrokerAdmin客户端和验证Tiered Storage(层级存储)概述支持分级存储何时使用工作原理安装 运行时Java版本推荐 Locally Standalone集群 启动 # 下载…...
Python实现基于openCV+百度智能云平台实现《1:N人脸考勤机》文章最后附带源码!
目录 一、 项目介绍 1.1 项目名称 1.2 项目简介 1.3 项目物料 1.4 技术栈 二、 项目架构 三、项目细节 3.1 环境搭建 3.2 利用opencv实现摄像头调取及相关图像的采集 3.3 利用aips上传图像和结果返回 3.4 结果优化和处理 3.5 可扩展性 3.6 遗留问题和…...
因为锁的问题,我们被扣了1万
前言 春节放假期间,一个项目上的积分接口被刷,而且不止一个人在刷,并且东西也被兑走,放假晚上被人叫起来排查问题,通过这个人的积分明细观察,基本一秒就能获取一次,远远超过了积分规则限定的次…...
【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)
【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER) 前文: blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置(ADC唤醒无法使用、低功耗模式无法烧录…...
浏览器渲染中的相关概念
渲染 渲染流水线 构建 DOM 树 输入:HTML 文档;处理:HTML 解析器解析;输出:DOM 数据解构。 样式计算 输入:CSS 文本;处理:属性值标准化,每个节点具体样式(…...
【MySQL】数据类型
1、数据类型描述 类型类型举例整数类型TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT浮点类型FLOAT、DOUBLE定点数类型DECIMAL位类型BIT日期时间类型YEAR、TIME、DATE、DATETIME、TIMESTAMP文本字符串类型CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT枚举类…...
L2-037 包装机
一种自动包装机的结构如图 1 所示。首先机器中有 N 条轨道,放置了一些物品。轨道下面有一个筐。当某条轨道的按钮被按下时,活塞向左推动,将轨道尽头的一件物品推落筐中。当 0 号按钮被按下时,机械手将抓取筐顶部的一件物品&#x…...
MySQL -查询日志、二进制日志、错误日志、慢查询日志
文章目录1.错误日志2.二进制日志3.查询日志4.慢查询日志1.错误日志 错误日志是 MySOL中最重要的日志之一,它记录了当 mvsald 启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息当数据库出现任何故障导致无法正常使用时,建议…...
TCP实现可靠传输的实现
TCP实现可靠传输的实现 目录TCP实现可靠传输的实现ARQ协议停止等待协议(古老)连续ARQ协议累计重传(回退N帧的ARQ协议)缓存确认(选择重传ARQ协议)超时重传的时间选择TCP的流量控制零窗口探测报文段Nagle算法…...
2/14考试总结
时间安排 7:30–7:50 看题,T1可能是个数据结构之类的东西,T2是 dp ,T3 构造。 7:50–8:20 T3,仿照样例的构造,可以通过一部分测试点。 8:20–9:20 T1,发现题目实际上要求子树内各儿子的深度信息,可以 dsu ,对于不能暴…...
程序环境和预处理详解
文章目录一、程序环境1.1 - 翻译环境1.1.1 - 编译1.1.1.1 - 预编译(预处理)1.1.1.2 - 编译1.1.1.3 - 汇编1.1.2 - 链接1.2 - 执行环境二、预处理详解2.1 - 预定义符号2.2 - #define2.2.1 - #define 定义标识符2.2.1.1 - 语法2.2.1.2 - 建议2.2.2 - #defi…...
The Social-Engineer Toolkit(社会工程学工具包)互联网第一篇全模块讲解
一、工具介绍 Social-Engineer Toolkit 是一个专为社会工程设计的开源渗透测试框架,可以帮助或辅助你完成二维码攻击、可插拔介质攻击、鱼叉攻击和水坑攻击等。SET 本身提供了大量攻击选项,可让您快速进行信任型攻击,也是一款高度自定义工具…...
Windows11去掉不满足系统要求的提示水印
我的电脑是LEGION的拯救者R70002021,预装的是Windows 11 家庭中文版,没有折腾重装过系统,今天突然注意到右下角出现了这个提示:“不满足系统要求。转到’设置"了解详细信息”。 在进入设置 - 系统 面板中也提示不满足系统要…...
JavaScript 计时事件
JavaScript 计时事件 通过使用 JavaScript,我们有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。我们称之为计时事件。 在 JavaScript 中使用计时事件是很容易的,两个关键方法是: setInterval() - 间隔指定的…...
七大排序算法的多语言代码实现
文章目录 前言 一、排序算法 1.原理简述 2.分类与复杂度 二、实例代码 1.冒泡排序 C Python Java Golang Rust Dephi 2.选择排序 C Python Java Golang Rust Dephi 3.插入排序 C Python Java Golang Rust Dephi 4.希尔排序 编辑 C Python Java Gola…...
【基础算法】表达式计算
中缀表达式:我们平常见到的正常数学式子 后缀表达式:12-3* 后缀表达式对于计算机很容易计算,只需要从头部扫描字符串。然后遇到数字就入栈,遇到运算符就取出栈顶的两个数进行运算。最后把运算结果入栈,最后栈中就会剩一个数为答…...
动态规划问题
目录 一、动态规划简介 二、利用动态规划解决问题 1、斐波拉契序列 2、拆分词句 3、三角形最小路径和 4、不同的路径数目(一) 5、带权值的最小路径和 6、求路径ii 7、01背包 8、不同子序列 9、编辑距离 10、分割回文串 一、动态规划…...
【MySQL进阶】 存储引擎 索引
😊😊作者简介😊😊 : 大家好,我是南瓜籽,一个在校大二学生,我将会持续分享Java相关知识。 🎉🎉个人主页🎉🎉 : 南瓜籽的主页…...
5 款最好的免费 SSD 数据恢复软件
SSD(固态硬盘)提供比传统硬盘更快的读/写速度,使启动、软件加载和游戏启动更快。因此,在我们选择存储设备时,它是一个极好的选择。但是,它仍然存在数据丢失的风险。假设您是受害者之一,正在寻找…...
做app还是做微网站好/武汉网站竞价推广
树莓派Pico板子里有一个内置的温度传感器,它与一个模数转换器(ADC)相连,通道编号为4,Pico里模数转换器的数值范围为12位整数,但MicroPython把范围映射到16位,也就是从0到65535,微处理器的工作电压是3.3V&am…...
做图素材网站哪个好/网页设计费用报价
MES选型不是做秀,不是选美。 如今不少企业在信息化推广应用过程中面面求好、追求完美,用意没错,然而在MES开发过程中,软件商不可能将今后各种可能出现的问题考虑周全,不可能将系统做到十全十美。随着系统投入使用的范围…...
莘县网站建设价格/腾讯控股第三季度营收1401亿
---------------------------------------------------------------OSPF在NBMA网络中的解决方案---------------------------------------------------------------一、OSPF在NBMA网络中产生的通信问题配置基本的帧中继网络帧中继配置R1:interface s1/0ip address 1…...
公司注册代办公司靠谱吗/廊坊百度快照优化排名
操作步骤: 新建/opt/install目录(随便建,脚本中路径一致就行)将installjdk(安装命令脚本),java(环境变量配置脚本),jdk-8u201-linux-x64.tar.gz(安装包也可是其他版本JD…...
广东宇晟建设工程有限公司网站/十大互联网广告公司
在日常开发中我们经常会碰到小数计算,而小数直接计算的话会出现一些小小的错误,如下 System.out.println(1.01 2.02); 复制代码你说能输出什么?3.03?实际上输出的是3.0300000000000002。这是因为不论是float 还是double都是浮点…...
做企业网站用什么程序/seo常用工具网站
WPF 支持的多线程 UI 并不是线程安全的 原文:WPF 支持的多线程 UI 并不是线程安全的版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接&…...