导航:首页 > 源码编译 > 路由分片源码

路由分片源码

发布时间:2024-02-03 08:31:43

1. OkHttp源码解析 (三)——代理和路由

初看OkHttp源码,由于对Address、Route、Proxy、ProxySelector、RouteSelector等理解不够,读源码非常吃力,看了几遍依然对于寻找复用连接、创建连接、连接服务器、连接代理服务器、创建隧道连接等逻辑似懂非懂,本篇决定梳理一遍相关的概念及基本原理。

● HTTP/1.1(HTTPS)
● HTTP/2
● SPDY

一个http请求的流程(直连):
1、输入url及参数;
2、如果是url是域名则解析ip地址,可能对应多个ip,如果没有指定端口,则用默认端口,http请求用80;
3、创建socket,根据ip和端口连接服务器(socket内部会完成3次TCP握手);
4、socket成功连接后,发送http报文数据。

一个https请求的流程(直连):
1、输入url及参数;
2、如果是url是域名则解析ip地址,可能对应多个ip,如果没有指定端口,则用默认端口,https请求用443;
3、创建socket,根据ip和端口连接服务器(socket内部会完成3次TCP握手);
4、socket成功连接后进行TLS握手,可通过java标准款提供的SSLSocket完成;
5、握手成功后,发送https报文数据。

1、分类
● HTTP代理:普通代理、隧道代理
● SOCKS代理:SOCKS4、SOCKS5

2、HTTP代理分类及说明
普通代理
HTTP/1.1 协议的第一部分。其代理过程为:
● client 请求 proxy
● proxy 解析请求获取 origin server 地址
● proxy 向 origin server 转发请求
● proxy 接收 origin server 的响应
● proxy 向 client 转发响应
其中proxy获取目的服务器地址的标准方法是解析 request line 里的 request-URL。因为proxy需要解析报文,因此普通代理无法适用于https,因为报文都是加密的。

隧道代理
通过 Web 代理服务器用隧道方式传输基于 TCP 的协议。
请求包括两个阶段,一是连接(隧道)建立阶段,二是数据通信(请求响应)阶段,数据通信是基于 TCP packet ,代理服务器不会对请求及响应的报文作任何的处理,都是原封不动的转发,因此可以代理 HTTPS请求和响应。
代理过程为:
● client 向 proxy 发送 CONNET 请求(包含了 origin server 的地址)
● proxy 与 origin server 建立 TCP 连接
● proxy 向 client 发送响应
● client 向 proxy 发送请求,proxy 原封不动向 origin server 转发请求,请求数据不做任何封装,为原生 TCP packet.

3、SOCKS代理分类及说明
● SOCKS4:只支持TCP协议(即传输控制协议)
● SOCKS5: 既支持TCP协议又支持UDP协议(即用户数据包协议),还支持各种身份验证机制、服务器端域名解析等。
SOCK4能做到的SOCKS5都可得到,但反过来却不行,比如我们常用的聊天工具QQ在使用代理时就要求用SOCKS5代理,因为它需要使用UDP协议来传输数据。

有了上面的基础知识,下面分析结合源码分析OkHttp路由相关的逻辑。OkHttp用Address来描述与目标服务器建立连接的配置信息,但请求输入的可能是域名,一个域名可能对于多个ip,真正建立连接是其中一个ip,另外,如果设置了代理,客户端是与代理服务器建立直接连接,而不是目标服务器,代理又可能是域名,可能对应多个ip。因此,这里用Route来描述最终选择的路由,即客户端与哪个ip建立连接,是代理还是直连。下面对比下Address及Route的属性,及路由选择器RouteSelector。

描述与目标服务器建立连接所需要的配置信息,包括目标主机名、端口、dns,SocketFactory,如果是https请求,包括TLS相关的SSLSocketFactory 、HostnameVerifier 、CertificatePinner,代理服务器信息Proxy 、ProxySelector 。

Route提供了真正连接服务器所需要的动态信息,明确需要连接的服务器IP地址及代理服务器,一个Address可能会有很多个路由Route供选择(一个DNS对应对个IP)。

Address和Route都是数据对象,没有提供操作方法,OkHttp另外定义了RouteSelector来完成选择的路由的操作。

1、读取代理配置信息:resetNextProxy()

读取代理配置:
● 如果有指定代理(不读取系统配置,在OkHttpClient实例中指定),则只用1个该指定代理;
● 如果没有指定,则读取系统配置的,可能有多个。

2、获取需要尝试的socket地址(目标服务器或者代理服务器):resetNextInetSocketAddress()

结合Address的host和代理,解析要尝试的套接字地址(ip+端口)列表:
● 直连或者SOCK代理, 则用目标服务器的主机名和端口,如果是HTTP代理,则用代理服务器的主机名和端口;
● 如果是SOCK代理,根据目标服务器主机名和端口号创建未解析的套接字地址,列表只有1个地址;
● 如果是直连或HTTP代理,先DNS解析,得到InetAddress列表(没有端口),再创建InetSocketAddress列表(带上端口),InetSocketAddress与InetAddress的区别是前者带端口信息。

3、获取路由列表:next()

选择路由的流程解析:
● 遍历每个代理对象,可能多个,直连的代理对象为Proxy.DIRECT(实际是没有中间代理的);
● 对每个代理获取套接字地址列表;
● 遍历地址列表,创建Route,判断Route如果在路由黑名单中,则添加到失败路由列表,不在黑名单中则添加到待返回的Route列表;
● 如果最后待返回的Route列表为空,即可能所有路由都在黑名单中,实在没有新路由了,则将失败的路由集合返回;
● 传入Route列表创建Selection对象,对象比较简单,就是一个目标路由集合,及读取方法。

为了避免不必要的尝试,OkHttp会把连接失败的路由加入到黑名单中,由RouteDatabase管理,该类比较简单,就是一个失败路由集合。

1、创建Address
Address的创建在RetryAndFollowUpInteceptor里,每次请求会声明一个新的Address及StreamAllocation对象,而StreamAllocation使用Address创建RouteSelector对象,在连接时RouteSelector确定请求的路由。

每个Requst都会构造一个Address对象,构造好了Address对象只是有了与服务器连接的配置信息,但没有确定最终服务器的ip,也没有确定连接的路由。

2、创建RouteSelector
在StreamAllocation声明的同时会声明路由选择器RouteSelector,为一次请求寻找路由。

3、选择可用的路由Route

下面在测试过程跟踪实例对象来理解,分别测试直连和HTTP代理HTTP2请求路由的选择过程:
● 直连请求流程
● HTTP代理HTTPS流程
请求url: https://www.jianshu.com/p/63ba15d8877a

1、构造address对象

2、读取代理配置:resetNextProxy

3、解析目标服务器套接字地址:resetNextInetSocketAddress

4、选择Route创建RealConnection

5、确定协议

测试方法:
● 在PC端打开Charles,设置端口,如何设置代理,网上有教程,比较简单;
● 手机打开WIFI,选择连接的WIFI修改网络,在高级选项中设置中指定了代理服务器,ip为PC的ip,端口是Charles刚设置的端口;
● OkHttpClient不指定代理,发起请求。

1、构造address对象

2、读取代理配置:resetNextProxy

3、解析目标服务器套接字地址:resetNextInetSocketAddress

4、选择Route创建RealConnection

5、创建隧道
由于是代理https请求,需要用到隧道代理。

从图可以看出,建立隧道其实是发送CONNECT请求,header包括字段Proxy-Connection,目标主机名,请求内容类似:

6、确定协议,SSL握手

1、代理可分为HTTP代理和SOCK代理;
2、HTTP代理又分为普通代理和隧道代理;普通代理适合明文传输,即http请求;隧道代理仅转发TCP包,适合加密传输,即https/http2;
3、SOCK代理又分为SOCK4和SOCK5,区别是后者支持UDP传输,适合代理聊天工具如QQ;
4、没有设置代理(OkHttpClient没有指定同时系统也没有设置),客户端直接与目标服务器建立TCP连接;
5、设置了代理,代理http请求时,客户端与代理服务器建立TCP连接,如果代理服务器是域名,则解释代理服务器域名,而目标服务器的域名由代理服务器解析;
6、设置了代理,代理https/http2请求时,客户端与代理服务器建立TCP连接,发送CONNECT请求与代理服务器建立隧道,并进行SSL握手,代理服务器不解析数据,仅转发TCP数据包。

如何正确使用 HTTP proxy
OkHttp3中的代理与路由
HTTP 代理原理及实现(一)

2. FastAPI 源码阅读 (一) ASGI应用

本章开启 FastAPI 的源码阅读,FastAPI是当下python web中一颗新星,是一个划时代的框架。从诞生便是以快速和简洁为核心理念。
它继承于 Starlette ,是在其基础上的完善与扩展。详细内容可以翻看我之前的源码阅读。

openapi() 与 setup() 是在初始化阶段,对 OpenAPI 文档进行初始化的函数。
而 add_api_route() 一直到 trace() ,是关于路由的函数,它们都是直接对 router 的方法传参引用。所以这些放在解读 routing.py 时一并进行。

除了 Starlette 原生的参数,大量参数都是和API文档相关。
而路由从 Starlette 的 Router 换成了新式的 APIRouter

这两个概念查询了大量文档才搞明白。他们主要是关于文档与反向代理的参数。当使用了Nginx时等反向代理时,从Uvicorn直接访问,和从Nginx代理访问,路径可能出现不一致。比如Nginx中的Fastapi根目录是 127.0.0.1/api/ ,而Uvicorn角度看是 127.0.0.1:8000/ 。对于API接口来说,其实这个是没有影响的,因为服务器会自动帮我们解决这个问题。但对于API文档来说,就会出现问题。

因为当我们打开/docs时,网页会寻找 openapi.json 。他的是写在html内部的,而不是变化的。这会导致什么问题?

例如当我们从Uvicorn访问 127.0.0.1:8000/docs 时,他会寻找 /openapi.json 即去访问 127.0.0.1:8000/openapi.json (了解前端的应该知道)

但是假如我们这时,从Nginx外来访问文档,假设我们这样设置Nginx:

我们需要访问 127.0.0.1/api/docs ,才能从代理外部访问。而打开docs时,我们会寻找 openapi.json

所以这时,它应该在 127.0.0.1/api/openapi 这个位置存在。
但我们的浏览器不知道这些,他会按照 /openapi.json ,会去寻 127.0.0.1/openapi.json 这个位置。所以他不可能找到 openapi.json ,自然会启动失败。

这其实是openapi文档前端的问题。

root_path ,是用来解决这个问题的。既然 /openapi.json 找不到,那我自己改成 /api/openapi.json 不就成了么。
root_path 即是这个 /api ,这个是在定义时手动设置的参数。为了告诉FastAPI,它处在整个主机中的哪个位置。即告知 所在根目录 。这样,FastAPI就有了"自知之明",乖乖把这个 前缀 加上。来找到正确的 openapi.json

加上了 root_path openapi.json 的位置变成了 /api/openapi.json 。当你想重新用Uvicorn提供的地址从代理内访问时,他会去寻找哪?没错 127.0.0.1:8000/api/openapi.json ,但我们从代理内部访问,并不需要这个 前缀 ,但它还是 “善意” 的帮我们加上了,所以这时候内部的访问失灵了。

虽然我们不大可能需要从两个位置访问这个完全一样的api文档。但这点一定要注意。

我在翻官方文档时,看到他们把 root_path 吹得天花乱坠,甚至弃用了 openapi_prefix 参数。但最后是把我弄得晕头转向。

这样要提到 servers 这个参数,官方首先给了这么段示例,稍作修改。

当我们打开API文档时

我们可以切换这个 Servers 时,底下测试接口的发送链接也会变成相应的。

但是记住,切换这个server,下面的接口不会发送变化,只是发送的host会改变。
这代表,虽然可以通过文档,测试多个其他主机的接口。但是这些主机和自己之间,需要拥有一致的接口。这种情况通常像在 线上/开发服务器 或者 服务器集群 中可能出现。虽然不要求完全一致,但为了这样做有实际意义,最好大体上是一致的。

但是我们看到,这是在代理外打开的,如果我们想从代理内打开,需要去掉 root_path 。会发生什么?
我们将 root_path 注释掉:

如果想解决这个问题,只需要将自身手动加入到 Servers 中。

关于 root_path_in_servers ,当 root_path servers 都存在时, root_path 会自动将自己加入到 servers 中。但如果这个置为False,就不会自动加入。(默认为True)

API文档实际上以字符串方式,在FastAPI内部拼接的。实际上就是传统的 模板(Templates) ,这个相信大家都很熟悉了。优点是生成时灵活,但缺点是不容易二次开发。fastapi提供了好几种文档插件,也可以自己添加需要的。

这么长一大串,实际上就一句话 self.router.add_api_route() ,其他剩下的那些我暂且省略的,其实基本都是这样的。就是调用router的一个功能。下面我用省略方式将它们列出。

可以看到有些在这里就做了闭包,实际上除了这里的'add_api_route()'他们最终都是要做闭包的。只是过程放在里router里。它们最终的指向都是 router.add_api_route() ,这是一个添加真正将 endpoint 加入到路由中的方法。
FastAPI 添加路由的方式,在starlette的传统路由列表方式上做了改进,变成了装饰器式。

其实就是通过这些方法作为装饰器,将自身作为 endpoint 传入生成 route 节点,加入到 routes 中。

FastAPI的入口没有太大的变化,借用starlette的 await self.middleware_stack(scope, receive, send) 直接进入中间件堆栈。

3. vue项目实现动态路由和动态菜单搭建插件式开发框架免费源码

以往我们在开发vue项目的时候,总是通过将路径和路由写在route/index.js文件中,然后直接进行访问即可,一般实现权限匹配都是通过菜单下面的权限参数和路由守卫进行一个验证拦截和权限匹配,然而这样安全性仍然不足。因为我们在route/index.js中已经写满了所有的路由,这样子不仅造成静态路由内容过多、修改困难,同时当静态路由内容过多的时候,我们在路由中的内容就显得极其复杂。

而后端对前端的控制也显得较为无力,无法实现严格性的控制。

由此我们发现通过动态路由控制是必然的,此时我们只需要通过后端获取数据菜单和路由信息json,然后动态添加路由并生成菜单,使菜单与动态路由内容进行一个匹配,这样子我们可以实现由后端控制前端的菜单和路由,我们的项目往往只需要内置几个组件无需权限的公共页面如登陆、注册、忘记密码和404错误这几个常用页面组件。

我们只需要将写好的组件放置到我们的view视图下,然后我们通过动态的路由和菜单实现路由添加和菜单进行匹配,我们便可实现对插件进行访问,我们减少了对route/index.js内容写入,同时也有利于减少内存的占用。

我们通过动态路由的形式,我们生成的菜单权限更加的完善,不仅实现依靠菜单与路由守卫拦截实现鉴权,也可以通过动态路由实现动态加载vue文件,控制更加深度

我们通过动态路由的形式,我们可以将项目分给不同的人进行完成,便于组建一个开发团队,因为他们所开发的组件,我们只需要在具备基本的javascript库的情况下。我们直接进行动态路由的一个挂载和菜单生成便可完成项目合作,减少了对route/index.js文件的操作,保证项目的完整性。

最后我发现在非node环境的开发条件下,我们可以实现远程的vue文件加载,这不仅为我们开发提供了便利,同时也有利于我们及时修改文件,以达到项目的需求,更有利于保障安全,实现服务器vue文件加载。

Vue:2.6.11。

Vue-route:3.2.0。

主页

聊天

第一通过后端返回的一个路由json数据,我们通过前端生成符合路由路由静态内容数组的一个数组,然后再通过addRoute进行一个循环添加,我们以此生成动态路由。在登陆时获取后端返回的菜单信息,我们进行菜单的一个循环生成,由此我们的一个动态项目就已经完成。

第二怎样对动态路由和菜单项目进行一个管理。

我们首先可以通过搭建一个组件通过添加路由信息和管理菜单实现二者的动态匹配。我们只需要对路由信息进行一个添加和修改,并和菜单相互间进行匹配,我们便可实现简单的路由挂载。

组件管理

菜单管理

此时将数据提交的后端由后端进行数据保存,我们此时的组件只需要放在views文件夹下,添加路由进行文件加载,我们便可实现路由管理。

第一登陆页面配置。

我们需要在静态文件夹下创建一个menu.json和route.json。两个json文件模拟服务器登录时返回的数据。

我们在登录页面模拟获取数据之后,我们通过菜单的一个方法进行生成菜单,通过路由的方法生成路由数组并进行循环添加,然后执行路由跳转。

第二配置路由初始化内容。我们将route/index.js的路由信息填为空是非常不理智的,而且会报错,因为路由初始化在加载前已经完成。有些页面完全不需要权限便可访问,比如登录、注册、找回密码和404错误,这种不需要权限的页面,我们还是需要将其直接以静态的形式写在route/index.js文件中。

Index初始数据

import Vue from 'vue'

import VueRouter from 'vue-router'

Vue . use ( VueRouter )

const routes = [{

path: '/' , //访问url

name: 'login' , //路由名称

component : () => import ( '@/unitui/pages/Login.vue' ), //加载模板文件

meta: {

show_site: 0 , //是否全屏显示

web_title: "登录" //网站标题

}

},

{

path: '/register' , //访问url

name: 'register' , //路由名称

component : () => import ( '@/unitui/pages/Register.vue' ), //加载模板文件

meta: {

show_site: 0 , //是否全屏显示

web_title: "注册" //网站标题

}

},

{

path: '/forget' , //访问url

name: 'forget' , //路由名称

component : () => import ( '@/unitui/pages/Forget.vue' ), //加载模板文件

meta: {

show_site: 0 , //是否全屏显示

web_title: "找回密码" //网站标题

}

},

{

path: '/404' , //访问url

name: '404' , //路由名称

component : () => import ( '@/unitui/pages/404.vue' ), //加载模板文件

meta: {

show_site: 0 , //是否全屏显示

web_title: "404错误" //网站标题

}

},

]

const router = new VueRouter ({

routes

})

router . beforeEach (( to , from , next ) => {

document . title = to . meta . web_title

console . log ( to );

next ()

})

export default router

第三,关于防止刷新后丢失的问题。我们需要在app.vue文件中的methods方法中定义一个路由生成方法。

示例:

init_route () { //初始化路由,防止刷新丢失

if ( sessionStorage . getItem ( "route_data" ) != null ) { //只有后端已经返回数据的情况下才允许生成

const route_data = JSON . parse ( sessionStorage . getItem ( "route_data" )); //获取路由信息

const data = []; //默认路由数组

for ( let index = 0 ; index < route_data . length ; index ++) { //生成路由信息

data [ index ] = {

path: route_data [ index ]. path , //访问url

name: route_data [ index ]. name , //路由名称

component : resolve =>

require ([ `@/views/ ${ route_data [ index ]. component } ` ], resolve ), //加载模板文件

meta: {

show_site: route_data [ index ]. meta . show_site , //是否全屏显示

web_title: route_data [ index ]. meta . web_title //网站标题

}

};

}

for ( let index = 0 ; index < data . length ; index ++) { //循环添加路由

this . $router . addRoute ( data [ index ]);

}

}

}

在mounted中进行方法调用,防止刷新的时路由丢失,导致发生错误。该方法内容基本和登陆页面的菜单出路由初始内容基本相同,但我们唯一差别的是,我们需要判断登陆所获取的路由信息是否存在,只有在存在的时候及后端已经返回了路由信息,即证明登录成功的时候,我们才会动态添加路由。

第一在刷新之后,默认跳转到path:’*’的一个路由界面中去,此时我们解决方法只需要将path:’*’路由进行一个删除,将其删除就变可正常访问。

第二动态路由跳转时发生Cannot find mole xxx错误。

意思是无法加载我们指定的一个vue文件,这是由于route3.0版本后import方式不支持传入变量,此时我们只需要将其改为require方式便可。

我们此次动态vue项目开发已经基本完成,我的开发的项目是基于element-ui进行,那么如果你需要源码参考。可以私信回复unit便可获取。

阅读全文

与路由分片源码相关的资料

热点内容
设备部门如何做好服务器 浏览:847
androido下载 浏览:476
神奇高量战法副图源码 浏览:828
汇编语言设计凯撒密码加密器 浏览:390
主次梁加密是加在哪里 浏览:662
模板匹配算法matlab 浏览:823
外地程序员去北京 浏览:22
安卓机换苹果12如何转移数据 浏览:418
互联网ntp服务器地址及端口 浏览:613
pdf到word转换器 浏览:267
飞行解压素材 浏览:498
51单片机指令用背吗 浏览:936
unityai算法 浏览:834
我的世界ice服务器如何打开pvp 浏览:975
c语言编程如何做标记 浏览:884
python数据分析实战pdf 浏览:985
u盘插入文件夹 浏览:918
华为amd云服务器 浏览:497
汉化编程卡是什么意思 浏览:128
python学习pdf 浏览:316