Go 项目(一)

news2025/1/11 4:20:26

目录

  • 基础
  • 环境
  • 包管理
  • 编码规范
    • 命名规范
    • 注释
    • import 规范
    • 错误处理
  • RPC
    • 内置 RPC
    • 改协议
    • 改调用

基础

  • 基础部分参考这个系列
  • 接下来的这部分是对上面的更新和重构,更加深入理解框架部分

环境

  • 基础环境,主要在Linux上搞;最主要是 docker,docker-compose 和 node,记得 docker 要配置阿里云镜像加速器
    1
    # docker
    curl -fsSL https://get.docker.com bash -s docker --mirror Aliyun
    sudo systemctl start docker
    sudo systemctl enable docker
    sudo usermod -aG docker ${USER}
    sudo chmod a+rw /var/run/docker.sock
    sudo systemctl restart docker
    docker run hello-world
    
    3
    4
    # mysql
    docker run -p 3306:3306 --name mymysql -v $PWD/conf:/etc/mysql/conf.d -v $PWD/logs:/logs -v $PWD/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
    mysql -uroot -p
    # 授权访问
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' IDENTIFIED BY 'root' WITH GRANT OPTION;
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY 'root' WITH GRANT OPTION;
    FLUSH PRIVILEGES;
    
  • centos7上安装 node,推荐使用16.16.0版本,太高会报错
    2

包管理

  • 说明一下 go path 和现在 go module 之间的差异
  • GOPATH
    • go env 能看到,这个需要自己设置,比如 D:\Go\workspace;
    • 那么,我们新建的项目都要放在 D:\Go\workspace\src 下面
    • 同时,要设置 GO111MODULE=off,用 go env -w
    • 不设置的话会去 GOROOT 下面找,就是安装目录 D:\Go\src 下的,放标准库的
    • 也就是说,这种包管理方式就是:不做包管理,对开发go的人容易,对go开发者很头疼
  • go module
    • 设置 GO111MODULE=on
    • 在 GoLand 新建项目会自动新建 go.mod 文件,有这么两行
      module goModProject
      go 1.17
      
    • import 未下载的包会自动下载并管理,可以多版本

编码规范

  • 代码规范不是强制的,⽬的是⽅便团队形成⼀个统⼀的代码⻛格,提高可读性;规范并不是唯⼀的

命名规范

  • 统⼀的命名规则有利于提⾼的代码的可读性,仅通过命名就可以获取到⾜够多的信息
  • 大写字⺟开头(常量、变量、类型、函数名、结构体等),代表可以被外部包的代码所使用,也被称为导出
  • 小写字⺟开头,则对包外是不可见的,类似 Java 的 private
  • 包名,package
    • 保持 package 的名字和⽬录⼀致,尽量采取有意义的包名,和标准库不要冲突
    • 使用小写单词,不要使⽤下划线或者混合大小写
  • 文件名
    • 应该为小写单词,使⽤下划线分隔各个单词
  • 结构体
    • 采用驼峰命名法,首字母根据访问控制⼤写或者小写
      type User struct{
      	Username string
      	Email   string
      }
      u := User{
      	Username: "Roy",
      	Email:  "roy.yang@gmail.com",
      }
      
  • 接口
    • 和结构体类似
    • 单个函数的接口名以 er 作为后缀
      type Reader interface {
          Read(p []byte) (n int, err error)
      }
      
  • 变量
    • ⼀般遵循驼峰法
    • 遇到特有名词时(API,ID等),需要遵循以下规则
      • 如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient
      • 其它情况都应当使⽤该名词原有的写法,如 APIClient、repoID、UserID;错误示例:UrlArray
      • 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头
  • 常量命名
    • 常量均需使⽤全部⼤写字⺟组成,并使⽤下划线分词
    • 如果是枚举类型的常量,需要先创建相应类型
      const  APP_VER = "1.0.0"
      type Scheme string
      const (
      	HTTP Scheme = "http"
      	HTTPS Scheme = "https"
      )
      

注释

  • 清晰的注释非常重要
  • 虽然我们自己不喜欢写注释,但是很讨厌不写注释的人
  • go 的注释是 C++ 风格,用 // 单行注释和 /**/ 多行注释
  • go 语⾔⾃带的 godoc ⼯具可以根据注释生成⽂档
  • 包注释
    • 每个包都应该有⼀个包注释,⼀个位于 package 子句之前的块注释或行注释
    • 应该包含
      /*
      包的基本简介(包名,简介)
      创建者,格式: 创建⼈: rtx 名
      创建时间,格式:创建时间: yyyyMMdd
      */
      
  • 结构体/接口注释
    • 结构体名, 结构体说明
    • 结构体内的每个成员变量都要有说明,该说明放在成员变量的后⾯
  • 函数注释
    • 应包括三个方面
      /*
      简要说明,格式说明:以函数名开头,“,” 分隔说明部分
      参数列表:每⾏⼀个参数,参数名开头,“,” 分隔说明部分
      返回值: 每⾏⼀个返回值
      */
      
    • 上面说的这些注释都是写在上面
  • 代码注释
    • ⼀些关键位置的代码逻辑,或者局部较为复杂的逻辑,需要有相应的说明
  • 注释风格
    • 中英⽂字符之间严格使⽤空格分隔

import 规范

  • 包分三类,标准库包,程序内部包,第三⽅包
  • 建议采用如下顺序
    import (
    	"encoding/json"
    	"strings"
    	
    	"github.com/astaxie/beego"
    	"github.com/go-sql-driver/mysql"
    	
    	"myproject/models"
    	"myproject/controller"
    	"myproject/utils"
    )
    
  • 在项⽬中不要使⽤相对路径引⼊第三方包,本项目自己的包建议用相对路径

错误处理

  • 不能丢弃任何返回 err 的调⽤,不要使⽤ _ 丢弃,必须全部处理
  • 接收到错误,要么 return err,要么用 log 记录下来
  • 尽量不要用 panic,除非你知道你在做什么
  • 采⽤独⽴的错误流进⾏处理
    // 错误写法
    if err != nil {
     	// error handling
    } else {
    	// normal code
    }
    
    // 正确写法
    if err != nil {
    	// error handling
    	return	// or continue
    }
    

RPC

  • remote procedure call 一个节点请求另一个节点的服务,一般分布式应用才会用到
  • 将本地过程调用变为远程过程调用要面临的问题,我们可以类比本地调用过程
    • Call 的 ID 映射
    • 序列化和反序列化(json/xml/protobuf/msgpack)
    • 网络传输(gin/beego/net)
  • 前面解释过这些概念和流程,这里再举个例子回顾一下
    • 先看一个简单的RPC流程
      1
    • 调用端和服务端都要经历序列化和反序列化
    • 我们选择 protobuf 作为序列化协议,更为通用;不同语言之间只要遵守了协议,也能交流
    • json 序列化主要是效率不够高,一般只在 gin 和浏览器之间使用,但我们的服务不止在这两个地方
    • 网络传输也是必不可少的,我们需要建立 http 连接,在 grpc 中使用 http2.0 避免连接断开的问题;我们也可以自己封装一层协议
  • 使用 RPC
    • 封装一个简单的 RPC 流程
    • server 端,使用 http 启动服务
      // server/main.go
      /*
      1.定义处理逻辑(反序列化,序列化)
      2.启动服务,监听端口
      */
      package main
      
      import (
      	"encoding/json"
      	"fmt"
      	"net/http"
      	"strconv"
      )
      
      func main() {
      	// http://127.0.0.1:8000?a=1
      	http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) {
      		_ = r.ParseForm() // 解析参数
      		fmt.Println("path:", r.URL.Path)
      		a, _ := strconv.Atoi(r.Form["a"][0]) // 搞成int
      		w.Header().Set("Content-Type", "application/json")
      		d, _ := json.Marshal(map[string]int{"data": a}) // 序列化(格式化)
      		_, _ = w.Write(d)
      	})
      	err := http.ListenAndServe(":8088", nil)
      	if err != nil {
      		fmt.Println(err)
      	}
      }
      
    • client 端,网络传输:http,序列化:json,ID:/add
      // client/main.go
      /*
      1.建立连接
      2.序列化数据
      3.发起请求
      4.反序列化返回值
      */
      package main
      
      import (
      	"encoding/json"
      	"fmt"
      	"github.com/kirinlabs/HttpRequest"
      )
      
      type RespData struct {
      	Data int `json:"data"` // 使用json解析
      }
      
      func Add(a int) int {
      	req := HttpRequest.NewRequest()
      	res, _ := req.Get(fmt.Sprintf("http://127.0.0.1:8088/%s?a=%d", "add", a))
      	body, _ := res.Body()
      	//fmt.Println(string(body)) // {"data":1}
      	r := RespData{}
      	_ = json.Unmarshal(body, &r)
      	return r.Data
      }
      func main() {
      	fmt.Println(Add(10))
      }
      
    • 看起来和普通的单体应用前后端请求没啥区别
    • 因为这里在本地请求,主要是体会 RPC 的几个要点,后面 grpc 框架会帮我们解决这些问题
  • RPC 框架开发要素
    • 技术架构上有四部分:客户端、客户端存根、服务端、服务端存根
      3
    • 存根(stub)干大部分的活
      • 该程序运⾏在客户端所在的计算机机器上,主要⽤来存储要调⽤的服务器的地址
      • 另外,还负责将客户端请求远端服务器程序的数据信息打包成数据包,通过⽹络发送给服务端Stub 程序
      • 其次,还要接收服务端 Stub 程序发送的调⽤结果数据包,并解析返回给客户端
    • 具体开发中,我们都会使用动态代理技术,⾃动⽣成的 Stub 程序
      • 因为地址、数据信息等可能会修改,stub 文件也可能会非常多,靠人写估计会辞职

内置 RPC

  • 尝试使用 go 语言内置 RPC,记得要和上面的简单过程对比
  • server 端
    package main
    
    import (
    	"net"
    	"net/rpc"
    )
    
    // class
    type HelloService struct {
    }
    
    // class function 定义处理逻辑
    func (s *HelloService) Hello(request string, reply *string) error {
    	// 通过修改指针地址返回
    	*reply = "hello" + request // 注:这里的 *reply 是不能分配空间的,调用时必须实例化
    	return nil
    }
    
    func main() {
    	// 1.实例化server
    	listen, _ := net.Listen("tcp", ":1234")
    	// 2.注册处理逻辑
    	_ = rpc.RegisterName("HelloService", &HelloService{})
    	// 3.启动服务,监听端口
    	conn, _ := listen.Accept()
    	rpc.ServeConn(conn)
    }
    
  • client 端
    package main
    
    import (
    	"fmt"
    	"net/rpc"
    )
    
    func main() {
    	// 1. 建立连接
    	client, err := rpc.Dial("tcp", "localhost:1234")
    	if err != nil {
    		panic(err)
    	}
    	// 2. 发起请求
    	// var reply = new(string) // new会实例化空间并返回指针,通过指针reply返回数据,这里必须分配空间,不能传nil,或者说不能直接写成 var reply *string
    	var reply string // 或者直接用 string 类型,它是有默认值的,或者说有地址,不是 nil
    	err = client.Call("HelloService.Hello", "roy", &reply)
    	if err != nil {
    		panic("调用失败")
    	}
    	fmt.Println(reply) // 传用 &,取用 *
    }
    
  • 上面的过程明显简单了很多,本质是 rpc 把 ID 和序列化的操作搞定了,无需我们关心底层细节

改协议

  • 上面的调用有些怪异,但 go 是静态语言,不像 python 能直接 client.Hello()
  • 我们可以跨语言发起请求,但要先更换协议
  • go 用的是 Gob 协议,换成 json
    // server
    package main
    
    import (
    	"net"
    	"net/rpc"
    	"net/rpc/jsonrpc"
    )
    
    // class
    type HelloService struct {
    }
    
    // class function
    func (s *HelloService) Hello(request string, reply *string) error {
    	// 通过修改指针地址返回
    	*reply = "hello" + request // 注:这里的 *reply 是不能分配空间的,调用时必须实例化
    	return nil
    }
    
    func main() {
    	// 实例化server
    	listen, _ := net.Listen("tcp", ":1234")
    	// 注册处理逻辑
    	_ = rpc.RegisterName("HelloService", &HelloService{})
    	// 启动服务,监听端口
    	for {
    		conn, _ := listen.Accept()
    		// 使用 json 序列化
    		go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) // 使用协程
    	}
    
    }
    
    // client
    package main
    
    import (
    	"fmt"
    	"net"
    	"net/rpc"
    	"net/rpc/jsonrpc"
    )
    
    func main() {
    	// 1. 建立连接
    	conn, err := net.Dial("tcp", "localhost:1234") // 不用 rpc 了,否则还是 Gob 协议
    	if err != nil {
    		panic(err)
    	}
    	// 2. 发起请求
    	client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn)) // 使用 json 序列化
    	var reply string
    	err = client.Call("HelloService.Hello", "roy", &reply)
    	if err != nil {
    		panic("调用失败")
    	}
    	fmt.Println(reply)
    }
    
  • 接下来用 python 发起请求
    • 这里可以用 socket,特点是每次调用就会发一次消息(等待套接字处理),不像 requests 底层是 HTTP 协议需要建立连接
    • 方便起见我们还是用 requests,先将 go 端的 server 改为使用 http 协议,并启动
      package main
      
      import (
      	"io"
      	"net/http"
      	"net/rpc"
      	"net/rpc/jsonrpc"
      )
      
      // class
      type HelloService struct {
      }
      
      // class function
      func (s *HelloService) Hello(request string, reply *string) error {
      	// 通过修改指针地址返回
      	*reply = "hello" + request // 注:这里的 *reply 是不能分配空间的,调用时必须实例化
      	return nil
      }
      
      func main() {
      	// 1.实例化server
      	_ = rpc.RegisterName("HelloService", &HelloService{})
      	// 将 rpc 的传输协议改为 http;先不用理解,照猫画虎
      	http.HandleFunc("/jsonrpc", func(w http.ResponseWriter, r *http.Request) {
      		var conn io.ReadWriteCloser = struct {
      			io.Writer
      			io.ReadCloser
      		}{
      			ReadCloser: r.Body,
      			Writer:     w,
      		}
      		// ServeRequest is like ServeCodec but synchronously serves a single request.
      		// It does not close the codec upon completion.
      		rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
      	})
      	http.ListenAndServe(":1234", nil)
      }
      
    • python 端
      import requests
      
      # 这些key名称是固定的
      req = {
          "id": 0,
          "params": ["Roy"],
          "method": "HelloService.Hello"
      }
      
      rsp = requests.post("http://localhost:1234/jsonrpc", json=req)
      print(rsp.text) # {"id":0,"result":"helloRoy","error":null}
      
    • 但还是没有实现上面说的 client.Hello() 形式的调用,可以参考这段代码理解
      import zerorpc
      
      c = zerorpc.Client()
      c.connect("tcp://127.0.0.1:1234")
      print(c.Hello("Roy"))
      
    • 在 python 中有一个魔法方法 __getattr__,即使 client 端没有 Hello 方法的定义,也能根据方法名称构建网络请求,可以进入源码查看
  • 上面改了序列化协议、传输协议,在 python 端尝试调用

改调用

  • 我们回到 go,它没有 python 的高级特性,只能自定义;从这里开始,我们朝 RPC 框架逼近了
  • 结构分析
    • 首先要有一个 handler 模块,因为 client 和 server 可能不在一个服务器,总不能为了运行 client 把 server 代码拿过去,将处理逻辑放在 handler,大家各自带上
      // handler/handler.go
      package handler
      
      // 名称冲突问题
      // 相当于 ID,server_proxy 中会注册这个 name,client_proxy 中会根据这个 ID 调用注册的 Hello 方法
      const HelloServiceName = "handler/HelloService"
      
      type NewHelloService struct {
      }
      
      func (s *NewHelloService) Hello(req string, reply *string) error {
      	*reply = "Hello, " + req
      	return nil
      }
      
    • client 想直接调用 Hello,还需要一层封装,我们称为 client_proxy;主要作用是与 server 建立连接,封装出 Hello 方法给 client
      package client_proxy
      
      import (
      	"goModProject/helloworld/handler"
      	"net/rpc"
      )
      
      // 所有方法,归在 Stub
      type HelloServiceStub struct {
      	*rpc.Client
      }
      
      // 这里是连接
      // go 语言没有类,没有初始化方法
      func NewHelloServiceClient(protocol, address string) HelloServiceStub {
      	conn, err := rpc.Dial(protocol, address)
      	if err != nil {
      		panic("Connect Error!")
      	}
      	return HelloServiceStub{conn}
      }
      
      // 这里是调用具体的逻辑,Hello方法
      func (c *HelloServiceStub) Hello(req string, reply *string) error {
      	// serviceMethod: Hello,对,就是 .Hello 调用
      	err := c.Call(handler.HelloServiceName+".Hello", req, reply)
      	if err != nil {
      		return err
      	}
      	return nil
      }
      
    • 同样,将注册逻辑放在 server_proxy,并使用接口作为参数解耦
      // server_proxy.go
      package server_proxy
      
      import (
      	"goModProject/helloworld/handler"
      	"net/rpc"
      )
      
      // 在服务端注册处理逻辑
      // 这么写耦合太紧了,proxy 受限于 handler
      //func RegisterHelloService(srv handler.NewHelloService) error {
      //	return rpc.RegisterName(handler.HelloServiceName, srv)
      //}
      
      // 使用接口解耦
      type HelloService interface {
      	Hello(request string, reply *string) error
      }
      
      // 使用接口作为参数,只要传入的结构体实现了 Hello 方法即可,不需要指定 handler.NewHelloService
      // 即使 handler 中的结构体名字变了也不需要改这里
      // server 和 handler 中对上即可
      func RegisterHelloService(srv HelloService) error {
      	return rpc.RegisterName(handler.HelloServiceName, srv)
      }
      
      // server.go
      package main
      
      import (
      	"goModProject/helloworld/handler"
      	"goModProject/helloworld/server_proxy"
      	"net"
      	"net/rpc"
      )
      
      func main() {
      	// 实例化server
      	listen, _ := net.Listen("tcp", ":1234")
      	// 注册处理逻辑
      	_ = server_proxy.RegisterHelloService(&handler.NewHelloService{})
      	// 启动服务,监听端口
      	for {
      		conn, _ := listen.Accept()
      		rpc.ServeConn(conn)
      	}
      }
      
  • 上面的过程一定要研究明白,主要是结构上优化,定义了框架的四要素;底层三问题还是使用 RPC 解决
  • 问题也来了,如果所有的 Stub 都自己写,会痛苦不堪的
    • 但我们发现,两个 proxy 的内容其实是有规律的,也就意味着可以自动生成
    • 需要用到一个工具:protoc
    • 后面从 grpc 开始介绍,和我们这部分对比就会发现,过程一样

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mac 可以玩游戏吗,有哪些游戏可以玩?

Mac 可以玩游戏吗,有哪些游戏可以玩? 新款的 MacBook Pro 入手有一段时间了,期间一直在熟悉 MacOS 系统及日常工作使用,一直都听说 MacBook 是工作本,不得不说工作使用确实很强,但用的久了就还是特别想折腾…

flex 布局:实现一行固定个数,超出强制换行(流式布局)

一、flex 布局基础知识 flex 布局的知识想必不用多说,一些常用的属性如下: 设置在父容器上的属性:display:flex, align-items, justify-content, flex-wrap。 设置在子容器上的属性,通过 flex: 1&#x…

最终一致分布式事务方案解析

业来主流的分布式事务的解决方案主要归位两大类:强一致性分布式事务和最终一致性分布式事务,本文不对强一致性分布式事务做过多描述,主要针对最终一致性方案解析。 根据笔者的工作经验来看,最终一致性方案适用用大部分互联网场景…

SpringBoot 2.7.8 自定义 Starter 自动配置

文章目录SpringBoot 2.7.8 自定义 Starter前言本次练习的代码仓库代码简要说明custom-springboot-starter-demo 的pom文件customer-starter 的pom文件test 的pom文件配置类配置信息SpringBoot 2.7.8 自定义 Starter 前言 前段时间,SpringBoot 出 3.x 版本了。听说…

如何与他人交流 (如何跟老板提涨工资) 第16章

最重要的事情 ---强有力的论证上期我们说根据场景来优化策略,是在隔靴搔痒,然而一个容易给出理由的人很容易成为演讲的高手.说服别人的理性与感性举个例子,我们如果说服别人买一件商品。采用打广告的形式,往往有两种途径。比如说我们要买一辆车。展示它的…

Power BI瀑布图

瀑布图(Waterfall Plot)也被称为阶梯图,它出现的历史并不长,最初为麦肯锡所创,因自上而下形似瀑布而得名,面世之后以其展示效果清晰而流畅被广为接受,经常在经营和财务分析中使用。 瀑布图是根…

4d view软件 .vol .4dv转 dcom文件

一、 .vol转 dcom文件 1、4d view软件打开vol文件 2、settings--dicom configuration-add,设置如下(前面的alias、ae title等设的可以随便一些,我都设了1),然后save&exit(第6步设置也可以&#xff09…

Vue13-计算属性computed

首先使用methods方法实现属性计算 步入正题: 计算属性:拿已有的属性计算得出新的属性 1.vue中属性和计算属性是分开的,属性在data中,计算属性在computed中 computed中计算属性以对象的形式存贮 这里是将fullName以及get的返回值…

计算机网络基础学习指南(一)

前言 计算机网络基础是研发/运维工程师都需掌握的知识,但往往会被忽略。 今天,我将献上一份详细 & 清晰的计算机网络基础学习指南,涵盖 TCP / UDP协议、Http协议、Socket等,希望你们会喜欢。 1. 计算机网络体系结构 1.1 简…

深入理解ThreadLocal看这篇就够了-应用场景、内部原理、内存泄漏以及父子线程如何共享数据

为了帮助大家在项目中更好使用ThreadLocal,本文向大家介绍ThreadLocal原理和常见问题,具体内容如下:ThreadLocal是什么ThreadLocal的应用场景ThreadLocal的内部原理ThreadLocal内存泄露问题父子线程如何共享数据ThreadLocal是什么java.lang.T…

CCS10新建TMS320F28335工程

CCS10新建TMS320F28335工程 1. 新建工程 点击Project → New CCS Project。选择芯片类型(TMS320F28335)、仿真器类型(XDS200V3)、新建工程名称、选择新建一个空工程。 2. 配置工程选项 右键项目工程名,进入配置选项Proprrties。或者AltEnter打开配置选项。在工程…

gd32f103vbt6 串口OTA升级-问题记录-2-平衡OTA弊端

走在路上的时候,我想起了这个OTA的弊端,那我想有没有办法解决呢?其实是有的。 那就是我还是把app程序放在flash的最开始的位置,而把OTA的程序放到后面(flash的最后12k)去。 这样也带来新的弊端&#xff1…

PMP考试技巧PMP考试大纲

一,PMP解题策略 PMP考试默认条件 精准审题 E(Eye):找到题眼; K(Key):找到考点; C(Choice):确定 选项; 情景结合 基本知识从PMI的角度出发,按PMI的思维方式,项目经理应该做出什么决定; 选…

SpringBoot自定义全局异常处理

自定义全局异常处理一. 创建所需类1. 自定义异常接口2. 自定义枚举类3. 自定义异常类4. 自定义异常处理类5. 自定义全局响应类5.1 BaseResponse类5.2 RespGenerator类二. 效果演示我们在 SpringBoot 项目中,往往会写许多 Controler 接口类,由于 Controll…

SNV的使用

一:什么是SVN? SVN是一个版本控制系统,SVN全称Subversion,用于记录一个或多个文件内容变化,方便我们查阅特定版本的修改情况。以前在没有版本控制的时候,我们通常在项目根目录下这样命名项目:p…

Python 环境搭建配置

Python可应用于多平台包括 Linux 和 Mac OS X。你可以通过终端窗口输入 "python" 命令来查看本地是否已经安装Python以及Python的安装版本。Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, SunOS, IRIX, 等等。)Win 9x/NT/2000Macintosh (Intel, PPC, 68K)OS/2DOS (多个…

微信小程序 Springboot校园达达互助平台快递代取系统 java

本 录 摘 要 III Abstract 1 1 系统概述 1 1.1 概述 2 1.2课题意义 3 1.3 主要内容 4 2 系统开发环境 5 2.1微信开发者工具 6 2.2小程序框架以及目录结构介绍 6 2.3 JAVA简介 7 2.4 MySQL数据库 7 3 需求分析 8 3.1 系统设计目标 8 3…

STM32开发(1)----stm32f103c6t6开发板介绍和环境搭建

stm32f103c6t6开发板介绍一、stm32f103c6t6芯片资源介绍STM32 的命名规则二、最小系统开发板介绍三、开发板基本使用方法软件安装MDK5 安装安装STM32芯片包安装licenseUSB转串口驱动安装四、本文小结一、stm32f103c6t6芯片资源介绍 stm32f103c6t6 是一款基于 ARM Cortex M3 内…

测试岗外包4年终上岸,这段日子说起来都是泪啊

昨天一个老哥找到我倾诉,他干了好几年外包,现在通过自己的努力应聘上了阿里测试开发,虽然只是P6,但也属实不容易了。这位老哥是湖南长沙毕业的,计算机专业,二流本科。长沙,湖南省会,…

Python图像合成与视频倒放

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、视频抓帧二、图像转换1,图像翻转2,图像文件倒序三、倒序视频合成四、图像截取拓展任务——动态图合成与倒放1,gif动态图生…