一文掌握fastapi微服务开发

2023-05-16

目录

一、概述

1.1 微服务

1.1.1 微服务的优势

1.1.2 微服务的缺点

1.2 为何使用Python开发微服务

1.3 FastAPI概述

二、开发

2.1 安装FastAPI

2.1.1 安装虚拟环境

2.1.2 创建虚拟环境

2.1.3 激活虚拟环境

2.1.4 安装FastAPI

2.2 FastAPI简单使用

2.2.1 查询

2.2.2 添加

2.2.3 修改

2.2.4 删除

2.3 代码组织

2.4 使用PostgreSQL数据库

2.4.1 安装PostgreSQL数据库

2.4.2 在FastAPI中连接PostgreSQL数据库

2.5 微服务中的数据管理

三、小结


一、概述

1.1 微服务

如果你是一名Python Web开发人员,那么肯定听说过微服务这个名词,并且希望通过Python来构建微服务。那么到底什么是微服务呢?

微服务(Microservice)是一种构建高可伸缩应用程序的架构,是一种将大型单一应用程序分解为专门针对特定服务、功能的单个应用程序的方法。举例来说,假如我们需要给自己的家进行装修,我们以前的做法就是找一家全包的装修公司将家里的水电、门窗、家具等全部交给这家装修公司,这家装修公司跟我们签订合同以后就统筹来安排所有的装修细节,我们后续的对接直接跟这家装修公司沟通就行了。装修时,所有的材料费都由这家装修公司统一采购和支出。这种方式在web开发领域就是典型的单片体系结构,每个业务逻辑(门窗装修、水电装修、家具装修)都驻留在同一个应用程序中(同一家装修公司负责),并且使用相同的数据库(所有装修的采购和财务由公司统一管理)。微服务架构则与这种单片体系结构不同,在微服务体系结构中,应用程序被分解为几个独立的服务,这些服务在不同的进程中运行。同样以装修为例,如果我们对这家公司的整体装修水平是认同的,但是,对他们打的家具不满意,我们想用别家专门做家具的来给我们打一套家具,那么我们就可以把打家具这项装修任务摘出去,交给另一家专门做家具的公司。很显然,我们完全可以将各个装修子任务都采用这种方式交给专门的装修公司。水电的交给专门做水电的、家具的交给专门做家具的、门窗的交给专门做门窗的。这种“细分”的方式能够让我们得到更满意的装修成果,毕竟术业有专攻,这种方式最终的性价比会更高。但是有个问题,全部任务分散以后我们必须规划好各个任务的进度和接口对接。例如,对于装修来说,我们一般是先装修水电再装修门窗家具,这里就有一个时间调度的安排。另外,水电的一些接口位置也必须提前规划好,因为后面设计门窗时也要考虑水电的位置安排。总之,这些各个分散的独立服务之间需要有效“沟通”才能真正发挥微服务的作用。微服务对于应用程序的不同功能有一个不同的数据库,根据每个服务的性质,微服务使用HTTP、AMQP或类似TCP的二进制协议相互通信,也可以使用RabbitMQ、Kafka或Redis等消息队列执行服务间通信。

1.1.1 微服务的优势

  • 松耦合

松散耦合的应用程序意味着可以使用最适合它们的技术来构建不同的服务。因此,开发团队可以根据每个子服务最适合的技术来开发。

  • 易控制

术业有专攻,微服务可以使整个应用程序更容易理解和控制。

  • 易扩展

使用微服务可以使应用程序扩展变得更容易,因为如果其中一个服务需要高GPU使用率,那么只有包含该服务的服务器需要高GPU,而其他服务器可以在普通服务器上运行。这一点在当今的人工智能时代尤其重要,因为我们经常需要部署一些基于AI这种高性能计算的微服务。

1.1.2 微服务的缺点

微服务并不是万能的,它也有一些不足。

  • 数据库同步

由于不同的服务使用不同的数据库,因此涉及多个微服务的数据库事务提交需要保证一致性。

  • 微服务拆分

在第一次尝试时很难实现服务的完美分割,到底哪些功能应该单独拆出来组成一个微服务才是最佳的,这些都需要迭代测试。

  • 通信速度

由于微服务之间使用网络交互进行通信,这使得应用程序容易因为网络延迟和服务速度慢而变慢。

1.2 为何使用Python开发微服务

Python是构建微服务的完美工具,因为它具有强大的社区、易于学习的曲线和大量的第三方库。

Python是一种面向对象的、解释型的、通用的、开源的脚本编程语言,它之所以非常流行,主要有三点原因:

  • Python 简单易用,学习成本低,看起来非常优雅干净;
  • Python 标准库和第三库众多,功能强大,既可以开发小工具,也可以开发企业级应用;
  • Python 站在了人工智能和大数据的风口上,流行度广。

举个简单的例子来说明一下 Python 的简单。比如要实现某个功能,C语言可能需要 100 行代码,而 Python 可能只需要几行代码,因为C语言什么都要得从头开始编写,而 Python 已经内置了很多常见功能,我们只需要导入包,然后调用一个函数即可。简单就是 Python 的巨大魅力之一,是它的杀手锏。正因为python的简洁,很多著名的web应用也开始陆续的采用python进行开发,例如豆瓣和知乎。但是,Python也有缺陷,就是它的速度相比其它语言要慢。虽然Python的速度问题一直被人诟病,但是由于Python引入了异步编程,因此近来出现了性能与GO和Node.js同等的web框架:Fastapi。

1.3 FastAPI概述

FastAPI是近几年基于Python推出的一款高性能web框架,使用Python 3.6+并基于标准的Python进行构建。它的优势如下:

  • 快速:可与NodeJSGo比肩的极高性能(归功于 Starlette 和 Pydantic),使其超越django和flask成为最快的python web框架之一。

  • 高效编码:提高功能开发速度约200%至300%。

  • 更少 bug:减少约40%的人为(开发者)错误。
  • 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
  • 简单:设计的易于使用和学习。
  • 简短:使代码重复最小化。
  • 健壮:生产可用级别的代码。还有自动生成的交互式文档。

具体可参考FastAPI官方文档。

本教程将使用python web框架FastAPI来构建一个微服务应用。相关环境说明如下所示:

编程语言:Python 3.6.1

操作系统:Windows 7

二、开发

2.1 安装FastAPI

2.1.1 安装虚拟环境

虚拟环境是Python解释器的一个副本环境,在这个环境中可以安装其它第三方Python包,在虚拟环境中安装的Python包不会影响全局环境中的包。打个比方,如果我同时接手两个团队的活:团队A和团队B,A团队的项目依赖了人工智能的tensorflow库(非常流行的深度学习库),B团队的项目也依赖了tensorflow,但是不巧的是A团队使用的是tensorflow 1版本,而B团队使用的是tensorflow 2版本,两个版本的兼容性非常差,那么两个项目同时在我自己的电脑上操作我该安装哪个库呢。假设两个库能同时安装在电脑上,具体执行的时候Python解释器也不知道到底该调用哪个tensorflow库。有没有什么办法把两个库在同一台电脑上隔离开,需要用哪个库就切换到哪个,这样,我就可以在一台电脑上同时开发两个不同环境的项目了。这个解决办法就是虚拟环境。

简单理解,虚拟环境就像一个容器,在某个虚拟环境中安装的python包可以独立于其它环境。

Windows平台下的虚拟环境需要使用第三方工具virtualenv来创建,打开命令终端,输入下面的命令即可完成安装:

pip install virtualenv

安装完成后检查是否成功安装,继续输入命令如下:

virtualenv --version

效果如下图所示:

此时会输出virtualenv的版本号,上面的版本号为20.4.2。

2.1.2 创建虚拟环境

现在我们假定有一个项目工程,该工程位于文件夹micropro中,我们现在的目标就是在这个micropro项目中创建python虚拟环境,这个虚拟环境是专门为micropro项目成立的。在命令行终端中通过cd命令进入项目目录中(假设micropro文件夹位于D盘目录下),如下图所示:

输入下面的命令用来创建名为venv的虚拟环境:

virtualenv venv

成功效果图如下所示:

这样我们就在micrppro文件夹下有了一个名为venv的子文件夹,它保存一个全新的虚拟环境,其中有一个私有的Python解释器位于micropro/venv/Scripts,在该虚拟环境中安装的python包会存放在micropro/venv/Lib路径下。

2.1.3 激活虚拟环境

Windows平台下激活虚拟环境的命令如下:

venv\Scripts\activate

成功激活之后,虚拟环境解释器的路径就被加入PATH中,但这种改变不是永久的,他只会影响当前的命令行终端。为了提醒用户已经激活了虚拟环境,如下图所示,会修改命令行的提示符,加入环境名:

像上述这种前面带()的命令行就说明已经在虚拟环境中了。

2.1.4 安装FastAPI

在虚拟环境中,我们使用下面的命令安装FastAPI:

pip install fastapi

由于FastAPI没有内置web服务,所以需要安装uvicorn才能运行web应用。uvicorn是一个ASGI服务器,它允许我们使用异步特性。

使用以下命令安装uvicorn:

pip install uvicorn

2.2 FastAPI简单使用

2.2.1 查询

本小节先简单介绍和熟悉下FastAPI的基本使用方法。

在新创建的micropro目录中创建一个新目录app和一个新文件main.py,然后在main.py中添加以下代码:

from fastapi import FastAPI

app = FastAPI()


@app.get('/')
async def index():
    return {"Real": "Python"}

上述代码首先导入并实例化FastAPI,然后注册根网址/,然后返回JSON。

我们可以使用uvicorn运行应用程序服务器:

uvicorn app.main:app

这里app.main表示使用app目录中的main.py文件,:app表示程序中定义的FastAPI实例名称。

成功启动后效果如下:

然后我们可以在浏览器中访问 http://127.0.0.1:8000,最终效果如下:

我们也可以访问http://127.0.0.1:8000/docs 来查看当前的所有访问路由并进行测试:

接下来,我们再深入扩展一下,在脚本中定义一个电影类Movie,代码如下所示:

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

app = FastAPI()
fake_movie_db = [{
    'name': 'Star Wars: Episode IX - The Rise of Skywalker',  #电影名
    'genres': ['Action', 'Adventure', 'Fantasy'], #题材
    'casts': ['Daisy Ridley', 'Adam Driver'] # 演员阵容
}]


class Movie(BaseModel):
    name: str
    genres: List[str]
    casts: List[str]


@app.get('/', response_model=List[Movie])
async def index():
    return fake_movie_db

上述代码中我们创建了一个新的类电影Movie,它从pydantic扩展了BaseModel。电影模型包含名称、题材和演员阵容。Pydantic内置了FastAPI,使得定义模型并对模型进行请求验证变得容易。我们在定义路由时额外使用了response_model=List[Movie],表明我们想要返回类Movie对应的实例对象列表,这里我们返回fake_movie_db。

我们重新运行服务器,然后访问 http://127.0.0.1:8000/docs ,可以看到如下效果:

我们可以看到示例响应部分中已经生成了电影模型的字段。

2.2.2 添加

接下来,我们添加路由,可以将电影添加到电影列表中。添加新的路由定义来处理POST请求:

@app.post('/', status_code=201)
async def add_movie(payload: Movie):
    movie = payload.dict()
    fake_movie_db.append(movie)
    return {'id': len(fake_movie_db) - 1}

重新启动服务器然后测试这个新的API。

我们先访问http://127.0.0.1:8000/docs,然后测试一下刚添加的api,输入数据如下图所示:

可以看到,系统成功返回了信息。我们成功的添加了一条电影信息。我们重新访问http://127.0.0.1:8000,查询效果如下图所示:

可以看到,我们刚添加的信息已经成功返回了。

2.2.3 修改

接下来,我们希望能够通过请求来修改电影列表中的数据,添加代码如下:

from fastapi import HTTPException 

@app.put('/{id}')
async def update_movie(id: int, payload: Movie):
    movie = payload.dict()
    movies_length = len(fake_movie_db)
    if 0 <= id < movies_length:
        fake_movie_db[id] = movie
        return None
    raise HTTPException(status_code=404, detail="Movie with given id not found")

上述代码中的id是我们的电影列表中某部电影的索引。我们重新启动服务器,来看下效果。提交数据如下所示:

然后我们重新访问http://127.0.0.1:8000,可以看到效果如下:

我们看到信息已经被修改了过来。

2.2.4 删除

最后我们定义删除接口,添加代码如下:

@app.delete('/{id}')
async def delete_movie(id: int):
    movies_length = len(fake_movie_db)
    if 0 <= id < movies_length:
        del fake_movie_db[id]
        return None
    raise HTTPException(status_code=404, detail="Movie with given id not found")

然后我们重新启动服务器,首先使用前面的Post接口添加3条数据,效果如下:

然后使用刚添加的删除接口,首先删除id=0的电影:

重新访问http://127.0.0.1:8000,效果如下:

可以看到,第一部电影已经删除掉了。

2.3 代码组织

通过2.2节我们熟悉了FastAPI基本的增删查改操作,前面我们的代码都是在一个脚本里编写的,这对于大型项目来说是不合适的,我们需要重新组织代码结构,使其更符合大型项目开发需求。

紧接着2.2节的内容。在应用程序中创建新文件夹api,并在api文件夹中创建新的movies.py。将所有与路由相关的代码从main.py移动到movies.py。调整后的movies.py如下所示:

#~/micropro/app/api/movies.py

from typing import List
from fastapi import Header, APIRouter
from fastapi import HTTPException

from app.api.models import Movie

fake_movie_db = [
    {
        'name': 'Star Wars: Episode IX - The Rise of Skywalker',
        'plot': 'The surviving members of the resistance face the First Order once again.',
        'genres': ['Action', 'Adventure', 'Fantasy'],
        'casts': ['Daisy Ridley', 'Adam Driver']
    }
]

movies = APIRouter()

@movies.get('/', response_model=List[Movie])
async def index():
    return fake_movie_db

@movies.post('/', status_code=201)
async def add_movie(payload: Movie):
    movie = payload.dict()
    fake_movie_db.append(movie)
    return {'id': len(fake_movie_db) - 1}

@movies.put('/{id}')
async def update_movie(id: int, payload: Movie):
    movie = payload.dict()
    movies_length = len(fake_movie_db)
    if 0 <= id < movies_length:
        fake_movie_db[id] = movie
        return None
    raise HTTPException(status_code=404, detail="Movie with given id not found")

@movies.delete('/{id}')
async def delete_movie(id: int):
    movies_length = len(fake_movie_db)
    if 0 <= id < movies_length:
        del fake_movie_db[id]
        return None
    raise HTTPException(status_code=404, detail="Movie with given id not found")

上述代码我们使用FastAPI中的APIRouter来注册新的API路由。另外,在api中创建一个新文件models.py,我们将在这个文件中保存我们定义的Pydantic模型。

接下来在main.py文件中注册这些新的路由:

#~/micropro/app/main.py
from fastapi import FastAPI
from app.api.movies import movies

app = FastAPI()
app.include_router(movies)

最终我们的应用结构如下:

最后,我们重新启动,查看下是否各项功能都正常。

2.4 使用PostgreSQL数据库

前面的案例中为了简单我们使用假的Python list来添加电影,在本小节我们将使用更实际的数据库来实现这个功能。本小节我们将使用PostgreSQL数据库。

2.4.1 安装PostgreSQL数据库

PostgreSQL是一个功能强大的开源对象关系型数据库系统,他使用和扩展了SQL语言,并结合了许多安全存储和扩展最复杂数据工作负载的功能。PostgreSQL的起源可以追溯到1986年,作为加州大学伯克利分校POSTGRES项目的一部分,并且在核心平台上进行了30多年的积极开发。PostgreSql提供了许多功能,旨在帮助开发人员构建应用程序,管理员保护数据完整性并且构建容错环境,并帮助你管理数据,无论数据集的大小。除了免费和开源之外,Postgre SQL还具有高度的可扩展性。 

进入下载页面:PostgreSQL: Downloads,由于我们使用windows系统,因此选择Windows对应的版本下载。下载完成后按照提示进行安装即可。安装完成后我们从开始菜单中找到pgAdmin4,这是PostgreSQL对应的数据库管理软件。

输入密码登陆pgAdmin4以后,我们创建一个名为movie_db数据库用于我们项目的数据管理。如下图所示:

最终效果如下:

2.4.2 在FastAPI中连接PostgreSQL数据库

为了能够在python中连接PostgreSQL数据库,我们需要依赖第三方库,使用下面的命令安装所需的库:

pip install 'databases[postgresql]'
pip install psycopg2

上述命令将同时安装sqlalchemy和asyncpg,这是后面使用PostgreSQL所必需的。

在api中创建一个新文件并将其命名为db.py,这个文件将包含REST API的实际数据库模型:

from sqlalchemy import (Column, Integer, MetaData, String, Table,
                        create_engine, ARRAY)

from databases import Database

DATABASE_URL = 'postgresql://movie_user:movie_password@localhost/movie_db'

engine = create_engine(DATABASE_URL)
metadata = MetaData()

movies = Table(
    'movies',
    metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('genres', ARRAY(String)),
    Column('casts', ARRAY(String))
)

database = Database(DATABASE_URL)

这里,DATABASE_URL是用于连接到PostgreSQL数据库的URL。这里movie_user是数据库用户的名称,movie_password是数据库用户的密码,movie_db是数据库的名称。默认的用户名为postgres,密码即为安装PostgreSQL时设置的密码。上述代码中创建了Movie对应的table。

更新main.py来连接数据库。main.py代码如下所示:

#~/micropro/app/main.py
from fastapi import FastAPI
from app.api.movies import movies
from app.api.db import metadata, database, engine

metadata.create_all(engine)

app = FastAPI()

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()


app.include_router(movies)

FastAPI提供了一些事件处理程序,可以在应用程序启动时使用这些处理程序连接到我们的数据库,并在应用程序关闭时断开连接。

接下来我们更新movies.py文件,使其使用数据库而不是Python列表:

#~/micropro/app/api/movies.py

from typing import List
from fastapi import Header, APIRouter,HTTPException

from app.api.models import MovieIn, MovieOut
from app.api import db_manager

movies = APIRouter()

@movies.get('/', response_model=List[MovieOut])
async def index():
    return await db_manager.get_all_movies()

@movies.post('/', status_code=201)
async def add_movie(payload: MovieIn):
    movie_id = await db_manager.add_movie(payload)
    response = {
        'id': movie_id,
        **payload.dict()
    }
    return response


@movies.put('/{id}')
async def update_movie(id: int, payload: MovieIn):
    movie = await db_manager.get_movie(id)
    if not movie:
        raise HTTPException(status_code=404, detail="Movie not found")

    update_data = payload.dict(exclude_unset=True)
    movie_in_db = MovieIn(**movie)

    updated_movie = movie_in_db.copy(update=update_data)

    return await db_manager.update_movie(id, updated_movie)

@movies.delete('/{id}')
async def delete_movie(id: int):
    movie = await db_manager.get_movie(id)
    if not movie:
        raise HTTPException(status_code=404, detail="Movie not found")
    return await db_manager.delete_movie(id)

接下来添加db_manager.py文件用于操作数据库:

from app.api.models import MovieIn, MovieOut, MovieUpdate
from app.api.db import movies, database


async def add_movie(payload: MovieIn):
    query = movies.insert().values(**payload.dict())

    return await database.execute(query=query)

async def get_all_movies():
    query = movies.select()
    return await database.fetch_all(query=query)

async def get_movie(id):
    query = movies.select(movies.c.id==id)
    return await database.fetch_one(query=query)

async def delete_movie(id: int):
    query = movies.delete().where(movies.c.id==id)
    return await database.execute(query=query)

async def update_movie(id: int, payload: MovieIn):
    query = (
        movies
        .update()
        .where(movies.c.id == id)
        .values(**payload.dict())
    )
    return await database.execute(query=query)

最后更新models.py,以便可以将Pydantic模型与sqlalchemy表一起使用:

#~/micropro/api/models.py

from pydantic import BaseModel
from typing import List, Optional

class MovieIn(BaseModel):
    name: str
    genres: List[str]
    casts: List[str]


class MovieOut(MovieIn):
    id: int


class MovieUpdate(MovieIn):
    name: Optional[str] = None
    genres: Optional[List[str]] = None
    casts: Optional[List[str]] = None

这里MovieIn是用于将电影添加到数据库的基础类。当我们从数据库取出数据时我们需要使用MovieOut类。MovieUpdate类允许我们将模型中的值设置为可选的,以便在更新电影数据时只更新需要更新的字段。

最后,我们启动服务器,然后按照之前的方法添加数据,最后我们在pdAdmin4中查看有没有添加成功,效果如下图所示:

2.5 微服务中的数据管理

微服务中的数据库管理是一个难点。在构建整个应用时如何拆分数据?如何构建不同微服务的数据库?哪些数据是各个微服务共享的?这些在开发前都需要先仔细的考虑好。本小节给出一些基本的方案和优缺点介绍:

  • 一个微服务管理一个独立的数据库

如果希望微服务尽可能松耦合,那么为每个微服务提供一个独立的数据库是比较好的方案。每个微服务有一个不同的数据库允许我们独立地扩展不同的服务。涉及多个数据库的数据操作可以通过微服务之间良好的api实现。这种方式的缺点就是实现涉及多个微服务的业务数据操作比较麻烦,另外,网络开销的增加使得这种方式使用效率降低。

  • 共享数据库

如果有很多数据操作涉及多个微服务,那么最好使用共享数据库。这带来了高度一致的应用程序的好处,但也丧失了微服务体系结构带来的大部分好处。在一个微服务上工作的开发人员需要与其他服务中的模式更改相协调。

  • API组合

在涉及多个数据库的操作中,API组合充当API网关,并按所需顺序执行对其他微服务的API调用。最后,每个微服务的结果在内存中执行连接后返回给客户机。这种方法的缺点是在内存中进行这种大型数据的链接效率比较低。

总之,到底该选用哪种数据拆分方式并没有一个绝对完美的指导方案,需要根据项目类型、项目复杂度等综合来考量,也需要在试错和实践中来改进。用好微服务架构能够给整个项目带来超强性能的效果。

三、小结

本文针对FastAPI微服务开发作了简单介绍,想要快速上手FastAPI的读者可以通过本文快速掌握FastAPI框架特性,能够依样“画葫芦”的将相关功能揉进自己的项目中去。除了数据库相关操作以外,FastAPI最有特色的功能就是集成机器学习和深度学习模型,完成线上AI推理,有兴趣的读者可以参考我的另一篇博文。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文掌握fastapi微服务开发 的相关文章

  • 亚马逊爬虫-python

    找实习遇到的作业 xff1a 最终结果 xff1a 实现代码分两部分 xff1a 抓取书籍id 爬取详细数据 1 xff1a import requests import re from pyquery import PyQuery as
  • http请求转串口通信模块实现网页发送文字硬件语音播放(esp8266 串口语音模块 )

    首先你有这样的8266 这两种8266自身带2个按键和烧录芯片方便调试 xff0c 综合性价比较高 还有就是需要这样的串口语音播放模块 有了这些我们开始吧 xff01 1 先看一段视频效果演示 xff0c 再来介绍实现步骤 http请求转串
  • 多智能体系统编队算法仿真--python3实现

    初始条件 xff1a 智能体位置随机生成所有智能体位置全局可知目标多边形位置给定所有个体运行相同算法 xff0c 根据环境来决定自己动作 目标 xff1a 形成均匀多边形分布 xff0c 所谓的 均匀 效果如下图 xff1a 即是多边形上间
  • 大众点评美食评论爬虫

    大家都知道的 xff0c 大众点评用了css反爬 xff0c 脑壳疼 评论文字使用SVG替换 然后还需要登录才能查看全部评论 xff0c 也就是要带cookie了 xff0c 此外时不时跳验证码 xff0c 验证码还有几种 xff0c 这帮
  • Python大佬手把手教你利用wxPython模块编写界面程序

    wxPython是一个开发桌面端图形界面的跨平台函数库 xff0c 开发语言为Python xff0c 它是基于C 43 43 的函数库wxWidgets的封装 很多人学习python xff0c 不知道从何学起 很多人学习python x
  • 什么是栈,栈存储结构详情

    什么是栈 xff0c 栈存储结构详情 同顺序表和链表一样 xff0c 栈也是用来存储逻辑关系为一对一数据的线性存储结构 xff0c 如图所示 从图1我们看到 xff0c 栈存储结构与之前学的线性存储有所差异 xff0c 这源于栈对数存和取的
  • C++cmath数学常用库中的代码介绍

    今天小编来分享一波C 43 43 cmath数学常用库中的常用代码 首先 xff0c 这些所有的代码都来自cmath库 xff0c 所以必须先引用cmath头文件 xff0c 即 xff1a include lt cmath gt 1 绝对
  • vue-cli-service Axios 持续返还401

    后端 xff1a 在登录超时或没有登录的情况 xff0c 所有请求都会拒绝并且返还HTTP状态码401 前端 xff1a 前端将认证的Token放到loadStorage中 xff0c 在认证失效时候清空 在接到HTTP状态码为401时 x
  • 载波相位测量

    1 简述GPS载波相位测量的基本原理 载波相位测量的观测量是GPS接收机所接收的卫星载波信号与接收机本振参考信号的相位差 利用接收机测定载波相位观测值 xff0c 经基线向量解算以获得两个同步观测站之间的基线向量坐标差 2 在高精度GPS测
  • PJLib开发杂谈

    去年公司搞了一个智慧乡村的项目 xff0c 其中涉及到视频监控的问题 电信提供的解决方案 xff0c 我们的平台只需要跟电信对接HLS流就可以了 这就有一个问题 xff0c 原来公司安装的监控无法接入到自己的平台 xff0c 好尴尬啊 于是
  • 开发GB28181监控平台前期准备总结

    首先得准备PJLIB的编译 xff0c 这个搜一下 xff0c 下载以后它是有VS的工程文件的 xff0c 所以编译很方便 得到这个库以后 xff0c 就可以编写SIP服务程序了 xff0c 服务程序可以验证GB28181的相关通讯流程 x
  • esp8266 丢失固件 丢失程序问题

    1 首先esp8266 丢失固件 丢失程序问题已经解决 2 解决方法 我们制作了一个固件保护主板 xff0c 提供2种供电接口 xff0c 支持5v稳压 串口电平保护 xff0c 固件保护 xff0c 反电动势保护 xff0c 支持复位按键
  • printf重定向

    1 printf与fputc 对于 printf 函数相信大家都不陌生 xff0c 第一个C语言程序就是使用 printf 函数在屏幕上的控制台打印出Hello World xff0c 之后使用 printf 函数输出各种类型的数据 xff
  • ESP32_BLUFI代码移植过程遇到的问题

    1 先是运行esp32官方给的例程 xff0c 出现了错误报错如下 xff1a esp image Image length 1053648 doesn t fit in partition length 1048576 boot Fact
  • Java 中的 Iterator 迭代器详解

    x1f366 Iterator 接口 在程序开发中 xff0c 经常需要遍历集合中的所有元素 针对这种需求 xff0c JDK 专门提供了一个接口 java util Iterator Iterator 接口也是 Java 集合中的一员 x
  • 三.【NodeJs入门学习】POST接口

    上一节我们学习了get接口 xff0c 这一节我们自己来写一下post接口 1 复习一下 先复习一下上一节中get请求的步骤 上图是在入口app js中处理get请求 xff0c 先拿到请求的url xff0c 然后设置了一个函数handl
  • 多进程和多线程比较

    原文 xff1a http blog csdn net lishenglong666 article details 8557215 很详细 对比维度 多进程 多线程 总结 数据共享 同步 数据共享复杂 xff0c 需要用IPC xff1b
  • C++ 之头文件声明定义

    最近在学习 c 43 43 在编译与链接过程中遇到了一些定义与声明的问题 经过多处查阅资料 基本解惑 现记录与此 希望让后面人少走些弯路 C 43 43 的头文件应该用什么扩展名 目前业界的常用格式如下 implementation fil
  • arduino修改串口缓冲区大小的三种办法

    由于SoftwareSerial h默认只接收64字节串行缓冲区 xff0c Arduino会将之后接收到的数据丢弃 xff0c 不满足业务需求 以下三种方法是笔者参考网上各种资料总结出来 xff0c 对于WEMOS D1 R2 xff0c
  • C语言调用libcurl的一个简单例子

    首先我们创建一个php页面 xff1a lt meta http equiv 61 span class hljs string 34 Content Type 34 span content 61 span class hljs stri

随机推荐

  • 【C++】类构造函数、析构函数的调用顺序「完整版」

    一 全局变量 静态变量和局部变量 全局变量在程序开始时调用构造函数 在程序结束时调用析构函数 静态变量在所在函数第一次被调用时调用构造函数 在程序结束时调用析构函数 xff0c 只调用一次 局部变量在所在的代码段被执行时调用构造函数 xff
  • linux下使用shell发送http请求

    本文主要介绍如何在linux下使用shell发送http请求 一 curl 1 get请求 curl命令默认下就是使用get方式发送http请求 curl www span class hljs preprocessor baidu spa
  • 【STL真好用】1057 Stack C++(30)

    1057 Stack 30 分 Stack is one of the most fundamental data structures which is based on the principle of Last In First Ou
  • C++学习之头文件引用

    目录结构如下 test h的定义如下 xff1a ifndef TEST H define TEST H include lt vector gt include lt string gt using namespace std class
  • checksum 算法

    说明 checksum xff1a 总和检验码 xff0c 校验和 xff0c 可以理解为check xff08 校验 xff09 xff0c sum xff08 和 xff09 在数据处理和通信领域 xff0c 通过一定算法对传输的数据进
  • 解决cannot open shared object file: No such file or directory

    一 linux下调用动态库 so文件时提示 xff1a cannot open shared object file No such file or directory 解决办法 xff1a 1 此时ldd xxx查看依赖缺少哪些库 lib
  • cmake 使用(六)

    本文是 cmake 使用的第六篇 主要介绍如何设置编译器优化标志 上一篇的链接为 xff1a https blog csdn net QCZL CC article details 119825737 xff0c 主要介绍如何将自己的软件安
  • 8086寄存器介绍

    8086 有14个16位寄存器 xff0c 这14个寄存器按其用途可分为 1 通用寄存器 2 指令指针 3 标志寄存器和 4 段寄存器等4类 1 通用寄存器有8个 又可以分成2组 一组是数据寄存器 4个 另一组是指针寄存器及变址寄存器 4个
  • C++常用操作符:: -> . (例子详解)

    C 43 43 提供了三种访问类或者类对象的操作符 xff0c 他们是 双冒号 点 箭头 gt 这三种操作符有着各自的使用场景和定义 双冒号 A B 表示作用域运算符 A一定是一个类的名称或命名空间的名称 仅仅用于当B是A类 A命名空间的一
  • STM32中断优先级的分配以及中断原则

    STM32d的中断优先级由NVIC IPRx寄存器来配置 xff0c IPR的宽度为8bit所以原则上每个中断可配置的优先级为0 255 xff0c 数值越小优先级越高 xff0c 但对于大部分的 Cortex M3芯片都会精简设计 xff
  • 晶体管的结构、类型和三种组态

    晶体管有两大类型 双极型晶体管 BJT 和场效应管 FET 双极型晶体管又称为半导体三极管 晶体三极管 xff0c 简称晶体管 它由两个PN结组合而成 xff0c 有两种载流子参与导电是一种电流控制电流源器件 场效应管仅有一种载流子参与导电
  • STM32单片机基础09——重定向printf函数到串口输出的多种方法

    本文详细的介绍了如何重定向printf输出到串口输出的多种方法 xff0c 包括调用MDK微库 xff08 MicroLib xff09 的方法 xff0c 调用标准库的方法 xff0c 以及适用于 GNUC 系列编译器的方法 1 prin
  • STM32直流减速电机控制篇(一)PWM调速

    直流电机原理 下面是分析直流电机的物理模型图 其中 xff0c 固定部分有磁铁 xff0c 这里称作主磁极 xff1b 固定部分还有电刷 转动部分有环形铁芯和绕在环形铁芯上的绕组 直流电机的转动原理我就不再赘述 xff0c 比较简单易懂 直
  • STM32直流减速电机控制篇(二)编码器测速原理

    编码器 编码器是一种将角位移或者角速度转换成一连串电数字脉冲的旋转式传感器 xff0c 我们可以通过编码器测量到底位移或者速度信息 编码器从输出数据类型上分可以分为增量式编码器和绝对式编码器 从编码器检测原理上来分 xff0c 还可以分为光
  • STM32直流减速电机控制篇(三)编码器测速程序编写

    编程思路 任何一个程序的编写我们都应该先理清楚编程思路 xff0c 通过上一篇讲解的编码器测速原理我们应该知道要想通过编码器得知电机转速我们第一步就应该是捕获A相和B相输出的脉冲 因为电机速度的定义是单位时间内的转数 xff0c 所以第二步
  • GPIO模式

    开漏输出 只能输出低电平 xff0c 不能输出高电
  • 单片机485通信

    1 RS485简介 485 xff08 一般称作 RS485 EIA 485 xff09 是隶属于 OSI 模型物理层的电气特性规定为 2 线 xff0c 半双工 xff0c 多点信的标准 它的电气特性和 RS 232 大不一样 用缆线两端
  • Jetson Xavier NX 镜像制作、烧录及克隆

    以下所有方法仅适用于Jetson Xavier Nx 16G emmc版本 其他版本仅供参考 官方文档下载链接为https developer nvidia com embedded downloads search 61 Develope
  • Postman下载,安装,注册及登录教程

    目录 一 Postman简介 二 Postman的注册 1 首先下载Postman xff0c 进入官网 xff1a Download Postman Get Started for Free 2 安装Postman 3 找到所下载的app
  • 一文掌握fastapi微服务开发

    目录 一 概述 1 1 微服务 1 1 1 微服务的优势 1 1 2 微服务的缺点 1 2 为何使用Python开发微服务 1 3 FastAPI概述 二 开发 2 1 安装FastAPI 2 1 1 安装虚拟环境 2 1 2 创建虚拟环境