Nginx模塊開發之http過濾器filter

文章目錄

  • 什么是過濾模塊
  • Nginx相關數據結構介紹
    • ngx_module_t的數據結構
    • ngx_http_module_t數據結構
    • ngx_command_s數據結構
  • 相關宏定義
  • filter(過濾器)實現
    • Nginx模塊開發流程
    • Nginx 模塊執行
    • 具體實現流程
      • create_loc_conf
      • merge_loc_conf
      • postconfiguration
      • 修改header信息
      • 修改body信息
    • 示例代碼
    • 編寫config文件
    • 編譯模塊到Nginx源碼中
    • 執行效果
  • 總結

什么是過濾模塊

在這里插入圖片描述
Nignx是一個代理服務器, 他前端被客戶端請求,后端連接服務器。這里涉及的數據處理大概有

  1. 客戶端請求數據,Nginx直接返回(handler 模塊)
  2. 客戶端請求數據,Nginx轉發給服務器(upstream 模塊)
  3. 服務器返回數據,Nginx轉發給客戶端(filter 模塊)

Nginx相關數據結構介紹

ngx_module_t的數據結構

struct ngx_module_s {ngx_uint_t            ctx_index; //是哪個進程ngx_uint_t            index;	//進程idchar                 *name;ngx_uint_t            spare0;ngx_uint_t            spare1;ngx_uint_t            version; //版本號const char           *signature; //簽名證書void                 *ctx;//上下文ngx_command_t        *commands;//命令ngx_uint_t            type;// nginx模塊類型ngx_int_t           (*init_master)(ngx_log_t *log);//ngx_int_t           (*init_module)(ngx_cycle_t *cycle); //模塊啟動時候ngx_int_t           (*init_process)(ngx_cycle_t *cycle); //進程啟動時候ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);void                (*exit_thread)(ngx_cycle_t *cycle);void                (*exit_process)(ngx_cycle_t *cycle);void                (*exit_master)(ngx_cycle_t *cycle);uintptr_t             spare_hook0;uintptr_t             spare_hook1;uintptr_t             spare_hook2;uintptr_t             spare_hook3;uintptr_t             spare_hook4;uintptr_t             spare_hook5;uintptr_t             spare_hook6;uintptr_t             spare_hook7;
};
typedef struct ngx_module_s ngx_module_t;

ngx_http_module_t數據結構

typedef struct {void        **main_conf;void        **srv_conf;void        **loc_conf;
} ngx_http_conf_ctx_t;typedef struct {ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);	// 解析配置文件之前ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);	// 解析配置文件完成之后
// **_main_ **解析配置文件中http關鍵字的內部void       *(*create_main_conf)(ngx_conf_t *cf);	char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);
// **_srv_ **解析配置文件中server關鍵字的內部void       *(*create_srv_conf)(ngx_conf_t *cf);	char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
// **_loc_ **解析配置文件中location關鍵字的內部void       *(*create_loc_conf)(ngx_conf_t *cf);char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

ngx_command_s數據結構

struct ngx_command_s {ngx_str_t             name;ngx_uint_t            type;char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);ngx_uint_t            conf;ngx_uint_t            offset;void                 *post;
};

相關宏定義

#define NGX_MODULE_V1                                                         \NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX,                           \NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0#define NGX_HTTP_MODULE           0x50545448   /* "HTTP" 模塊*//* 以下宏定義為了去確定該項配置屬于哪個類目下 
比如service 
比如location
*/
#define NGX_HTTP_MAIN_CONF        0x02000000
#define NGX_HTTP_SRV_CONF         0x04000000
#define NGX_HTTP_LOC_CONF         0x08000000
#define NGX_HTTP_UPS_CONF         0x10000000
#define NGX_HTTP_SIF_CONF         0x20000000
#define NGX_HTTP_LIF_CONF         0x40000000
#define NGX_HTTP_LMT_CONF         0x80000000

filter(過濾器)實現

Nginx模塊開發流程

(1)定義一個模塊名,ngx_module_t,選擇好http模塊NGX_HTTP_MODULE。
(2)定義cmd命令,有多少條cmd寫多少條cmd,ngx_command_t。
(3)定義用來解析http block,ngx_http_module_t。
(4)執行過程實現添加模塊。

Nginx 模塊執行

(1)初始化。當進程啟動的時候進行的模塊初始化。
(2)解析conf文件。解析conf文件中模塊的相關命令和設置。
(3)Nginx啟動之后,有命令或請求到來時,處理請求的流程。

開發模塊時,需要實現的主要是這三個流程的功能。

具體實現流程

create_loc_conf

內存池中分配一片kong空間,用以存儲配置文件中指令對應的值

// void       *(*create_loc_conf)(ngx_conf_t *cf);
// 解析conf文件location關鍵字之前的動作
void  *ngx_http_fly_filter_create_loc_conf(ngx_conf_t *cf)
{ngx_http_filter_conf_t *conf = ngx_palloc(cf->pool, sizeof(ngx_http_filter_conf_t));if (conf == NULL)return NULL;conf->enable = NGX_CONF_UNSET;return conf;
}

merge_loc_conf

char  *ngx_http_fly_filter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{ngx_http_filter_conf_t *prev = (ngx_http_filter_conf_t*)parent;//如果prefix中是on,那么next->enable的值就為1,這個是在ngx_conf_set_flag_slot//函數中設置的,即可以理解為將配置//文件中的on或者off轉換為nginx內存中的1或者0ngx_http_filter_conf_t *next = (ngx_http_filter_conf_t*)child;ngx_conf_merge_value(next->enable, prev->enable, 0);return NGX_CONF_OK;
}

其中 ngx_conf_merge_value

#define ngx_conf_merge_value(conf, prev, default)                            \if (conf == NGX_CONF_UNSET) {                                            \conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \

postconfiguration

  1. 解析完畢conf文件后執行該指令,設置運行時候的回調函數
  2. 使用頭插法,將header_filter 與 body_filter插入filter隊列的頭部
// ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);
// 解析完配置文件之后的動作,也就是解析完http關鍵字模塊之后
ngx_int_t ngx_http_fly_filter_init(ngx_conf_t *cf)
{// 模塊的初始化// http {  }// O->O->O->O// 多個模塊的頭插法,取出最前面的模塊//top指向第一個,next指向第二個ngx_http_next_header_filter = ngx_http_top_header_filter;ngx_http_top_header_filter = ngx_http_fly_header_filter;ngx_http_next_body_filter = ngx_http_top_body_filter;ngx_http_top_body_filter = ngx_http_fly_body_filter;return NGX_OK;
}

修改header信息

這里僅僅修改要回發的內容長度,由于修改了body內容,那么header中的length字段自然要做出相應的修改

static ngx_str_t prefix = ngx_string("<h2>FLY. </h2>");
ngx_int_t ngx_http_fly_header_filter(ngx_http_request_t *r) {if (r->headers_out.status != NGX_HTTP_OK) {// 不正常返回,則進行nextreturn ngx_http_next_header_filter(r);}//r->headers_out.content_type.len == sizeof("text/html")r->headers_out.content_length_n += prefix.len;return ngx_http_next_header_filter(r);
}

修改body信息

ngx_int_t ngx_http_fly_body_filter(ngx_http_request_t *r, ngx_chain_t *chain) {/** 關于ngx_chain_t:* 在nginx中,有一個數據鏈,存放要發送的數據。* O->O->O->O* 每次send的是ngx_chain_t中的一個ngx_buf_t*/// 添加一個chain bufferngx_buf_t *b = ngx_create_temp_buf(r->pool, prefix.len);b->start = b->pos = prefix.data;b->last = b->pos + prefix.len;ngx_chain_t *c1 = ngx_alloc_chain_link(r->pool);c1->buf = b;c1->next = chain;return ngx_http_next_body_filter(r, c1);}

示例代碼

這里主要實現了在返回的網頁中添加一個內容。里面在重點地方附上了詳細注釋。
ngx_http_filter_module.c


#include <ngx_config.h>
#include <ngx_http.h>
#include <ngx_core.h>typedef struct {ngx_flag_t enable;
}ngx_http_filter_conf_t;static ngx_str_t prefix = ngx_string("<h2>FLY. </h2>");static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;// ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r)
// 添加頭,header
ngx_int_t ngx_http_fly_header_filter(ngx_http_request_t *r) {if (r->headers_out.status != NGX_HTTP_OK) {// 不正常返回,則進行nextreturn ngx_http_next_header_filter(r);}//r->headers_out.content_type.len == sizeof("text/html")r->headers_out.content_length_n += prefix.len;return ngx_http_next_header_filter(r);
}// ngx_int_t (*ngx_http_output_body_filter_pt)(ngx_http_request_t *r, ngx_chain_t *chain)
// 添加內容,body
ngx_int_t ngx_http_fly_body_filter(ngx_http_request_t *r, ngx_chain_t *chain) {/** 關于ngx_chain_t:* 在nginx中,有一個數據鏈,存放要發送的數據。* O->O->O->O* 每次send的是ngx_chain_t中的一個ngx_buf_t*/// 添加一個chain bufferngx_buf_t *b = ngx_create_temp_buf(r->pool, prefix.len);b->start = b->pos = prefix.data;b->last = b->pos + prefix.len;ngx_chain_t *c1 = ngx_alloc_chain_link(r->pool);c1->buf = b;c1->next = chain;return ngx_http_next_body_filter(r, c1);}// ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);
// 解析完配置文件之后的動作,也就是解析完http關鍵字模塊之后
ngx_int_t ngx_http_fly_filter_init(ngx_conf_t *cf)
{// 模塊的初始化// http {  }// O->O->O->O// 多個模塊的頭插法,取出最前面的模塊ngx_http_next_header_filter = ngx_http_top_header_filter;ngx_http_top_header_filter = ngx_http_fly_header_filter;ngx_http_next_body_filter = ngx_http_top_body_filter;ngx_http_top_body_filter = ngx_http_fly_body_filter;return NGX_OK;
}// void       *(*create_loc_conf)(ngx_conf_t *cf);
// 解析conf文件location關鍵字之前的動作
void  *ngx_http_fly_filter_create_loc_conf(ngx_conf_t *cf)
{ngx_http_filter_conf_t *conf = ngx_palloc(cf->pool, sizeof(ngx_http_filter_conf_t));if (conf == NULL)return NULL;conf->enable = NGX_CONF_UNSET;return conf;
}// char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
// 解析完配置文件location關鍵字之后的動作
// 模塊可能在多個地方定義,這個函數合并所有的值一起使用
char  *ngx_http_fly_filter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{ngx_http_filter_conf_t *prev = (ngx_http_filter_conf_t*)parent;ngx_http_filter_conf_t *next = (ngx_http_filter_conf_t*)child;// 合并enable的值ngx_conf_merge_value(next->enable, prev->enable, 0);return NGX_CONF_OK;
}/*
struct ngx_command_s {
ngx_str_t             name;
ngx_uint_t            type;
char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t            conf;
ngx_uint_t            offset;
void                 *post;
};
*//*
// conf文件命令解析
char  *ngx_http_fly_filter_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{char *p = conf;// 對應 ngx_http_fly_filter_create_loc_conf函數的conf->enable = NGX_CONF_UNSET;ngx_flag_t *flag = (p + cmd->offset);return NGX_CONF_OK;
}
*/// conf文件中的每一行都是一個指令指令
ngx_command_t ngx_http_fly_filter_module_cmd[] = {{//命令名稱,比如listen,定義了就可以在conf文件中使用,注意不能和其他的起沖突ngx_string("predix"),// 指示name命令放的位置在哪里以及可以帶多少個參數,NGX_CONF_FLAGE表示開關標志// predix on/offNGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_FLAG,// 命令解析,可以使用nginx內部的也可以自己實現ngx_conf_set_flag_slot,//ngx_http_fly_filter_set_slot,NGX_HTTP_LOC_CONF_OFFSET,// offsetof獲取enable在結構體中的偏移位置offsetof(ngx_http_filter_conf_t,enable),NULL,},ngx_null_command
};// 用來解析對應的conf文件,其實表示的就是模塊定義中的上下文
static ngx_http_module_t ngx_http_fly_filter_module_ctx = {NULL,ngx_http_fly_filter_init,NULL,NULL,NULL,NULL,ngx_http_fly_filter_create_loc_conf,ngx_http_fly_filter_merge_loc_conf
};// 模塊定義
ngx_module_t ngx_http_fly_filter_module = {NGX_MODULE_V1,&ngx_http_fly_filter_module_ctx,ngx_http_fly_filter_module_cmd,// http的ascii值,指示是什么模塊NGX_HTTP_MODULE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING	// 填充};

編寫config文件

創建:

touch config

內容:

ngx_addon_name=ngx_http_fly_filter_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_fly_filter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_filter_module.c"

包含三部分信息,一個是模塊的名稱這里名稱需要與代碼中的定義的模塊名稱ngx_module_t一致;第二部分是指定模塊的類型和名稱,這里定義的是一個filter模塊;最后是指定模塊源文件路徑。
注意,config文件要和模塊的代碼在相同目錄。

編譯模塊到Nginx源碼中

(1)配置中添加模塊:

./configure --prefix=/usr/local/nginx --with-http_realip_module 
--with-http_addition_module --with-http_gzip_static_module 
--with-http_secure_link_module --with-http_stub_status_module 
--with-stream --with-pcre=/home/fly/workspace/pcre-8.41 
--with-zlib=/home/fly/workspace/zlib-1.2.11 
--with-openssl=/home/fly/workspace/openssl-1.1.0g 
--add-module=/mnt/hgfs/sourcecode_learning/ngx_http_filter_module

注意模塊路徑要正確。出現如下表示成功:

configuring additional modules
adding module in /mnt/hgfs/sourcecode_learning/ngx_http_filter_module+ ngx_http_fly_filter_module was configured
creating objs/Makefile

(2)查看是否添加模塊到動態代碼中:

vim objs/ngx_modules.c

(3)編譯:

make
sudo make install

執行效果

編譯安裝完成后,在conf文件中添加模塊的開關(predix on):

worker_processes 4;events {worker_connections 1024;
}http {upstream backend {server 192.168.7.146:8889;server 192.168.7.146:8890;}server {listen 8888;location / {proxy_pass http://backend;}}server {listen 8889;}server {listen 8890;predix on;}server {listen 8891;}
}

執行Nginx:

sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/fly.conf 

在網頁輸入IP和端口,執行效果如下:
在這里插入圖片描述
可以看到,返回的網頁中多出來添加的內容(FLY.)。

總結

  1. Nginx中http模塊非常多,每個模塊都會有ngx_http_module_t,為了防止解析過程出現沖突,Nginx編譯的時候會把所有的模塊都集中起來,組織到/obj/ngx_module.c(以數組的方式)。
  2. 在編譯模塊時,需要編寫config文件,這個文件最好不要使用筆記本編輯(notepad),容易造成編碼方式的錯誤。
  3. 網頁中常見的廣告(其實里面存儲了圖片、鏈接、名稱信息)等等其實就是通過nginx過濾器模塊去實現的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/161834.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/161834.shtml
英文地址,請注明出處:http://en.pswp.cn/news/161834.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用OkHttp庫爬取百度云視頻詳細步驟

目錄 摘要 一、OkHttp庫簡介 二、爬蟲基本概念 三、使用OkHttp庫爬取百度云視頻 1、發送HTTP請求 2、處理響應 3、下載文件 四、可能遇到的問題及解決方案 五、注意事項 總結與建議 摘要 本文將詳細介紹如何使用OkHttp庫爬取百度云視頻。文章首先簡要介紹OkHttp庫和…

【collections】Python中的OrderDict

【collections】Python中的OrderDict 文章目錄 【collections】Python中的OrderDict1. 什么是OrderedDict2. Toy Code 1. 什么是OrderedDict 其實很簡單OrderedDict是Python中一個字典dict的變體&#xff0c;它可以按照元素添加的順序來保持鍵值對&#xff08;key-value pair&…

GPIO模式詳解:推挽/開漏/浮空/上拉/下拉/施密特(遲滯)輸入

GPIO(General Purpose Input Output)可用于執行數字輸入或輸出功能。典型的應用包括從/向模擬或數字傳感器/設備讀寫數值、驅動LED、為I2C通信驅動時鐘、生成外部組件的觸發、發出中斷等。 文章目錄 1 GPIO簡介2 輸出模式2.1 推挽輸出2.2 開漏輸出 3 輸入模式3.1 高阻態(浮空)、…

推薦一款適合做智慧旅游的前端模板

目錄 前言 一、功能介紹 二、前端技術介紹 三、功能及界面設計介紹 1、數據概覽 2、車輛監控 3、地圖界面 4、其它功能 四、擴展說明 總結 前言 智慧旅游是一種全新的旅游業務模式&#xff0c;它充分利用先進的信息技術&#xff0c;提升旅游體驗&#xff0c;優化旅游管…

【Axure高保真原型】樹形表格

今天和大家分享樹形表格的原型模板&#xff0c;點擊樹的箭頭可以打開或者收起子節點&#xff0c;點擊表格內容&#xff0c;可以選中該行內容實現高亮變色效果&#xff0c;樹形表格是通過中繼器制作的&#xff0c;使用簡單&#xff0c;只需要按要求填寫中繼器表格即可&#xff0…

2023亞太杯數學建模思路 - 案例:粒子群算法

文章目錄 1 什么是粒子群算法&#xff1f;2 舉個例子3 還是一個例子算法流程算法實現建模資料 # 0 賽題思路 &#xff08;賽題出來以后第一時間在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 什么是粒子群算法&#xff1f; 粒子群算法&#xff08;Pa…

安防監控視頻融合平臺EasyCVR定制化頁面開發

安防監控EasyCVR視頻匯聚平臺基于云邊端智能協同&#xff0c;支持海量視頻的輕量化接入與匯聚、轉碼與處理、全網智能分發、視頻集中存儲等。安防視頻平臺EasyCVR拓展性強&#xff0c;視頻能力豐富&#xff0c;具體可實現視頻監控直播、視頻輪播、視頻錄像、云存儲、回放與檢索…

ConditionObject介紹(二)

1. Condition的signal方法分析 分為了幾個部分&#xff1a; ● 確保執行signal方法的是持有鎖的線程 ● 脫離Condition的隊列 ● 將Node狀態從-2改為0 ● 將Node添加到AQS隊列 ● 為了避免當前Node無法在AQS隊列正常喚醒做了一些判斷和操作 // 線程掛起后&#xff0c;可以基于…

Centos設置nginx開機自啟動設置

Centos設置nginx開機自啟動設置 要設置CentOS中的Nginx開機自啟動&#xff0c;可以按照以下步驟進行操作&#xff1a; 首先&#xff0c;登錄到CentOS服務器上&#xff0c;并以root用戶或具有sudo權限的用戶身份執行以下命令來安裝Nginx&#xff08;如果尚未安裝&#xff09;&a…

字符集合!!!

字符集合&#xff01;&#xff01;&#xff01; 描述 輸入一個字符串&#xff0c;求出該字符串包含的字符集合&#xff0c;按照字母輸入的順序輸出。 數據范圍&#xff1a;輸入的字符串長度滿足 1 \le n \le 100 \1≤n≤100 &#xff0c;且只包含大小寫字母&#xff0c;區分大小…

中國信息通信研究院產業與規劃研究所校招一面、二面內容

本文介紹2024屆秋招中&#xff0c;中國信息通信研究院的數字孿生智慧城市研究員崗位一面、二面的面試基本情況、提問問題等。 10月投遞了中國信息通信研究院的數字孿生智慧城市研究員崗位&#xff0c;所在部門為數字孿生與城市數字化研究部。目前完成了一面與二面&#xff0c;在…

Django 模型和Admin站點管理(三)

一、定義模型 &#xff08;1&#xff09; 創建模型類&#xff0c;必須要繼承自 models.Model from django.db import models# Create your models here. #設計數據庫 #創建模型 class UserModel(models.Model):namemodels.CharField(max_length30) #對應于SQL name varchar(30…

K8s實戰RestartPoliy策略

一、默認策略為Always cmd.yaml apiVersion: v1 kind: Pod metadata:name: myapp-pod labels:app: myapp spec: containers:- name: myapp-container image: busyboxcommand: [sh, -c, echo OK!&& sleep 60]首先我們根據這個yaml創建一個測試的pod 執行命令 kubec…

Vue.observable可以在vue2中給新增的屬性增加響應式

將data中的config數據轉為響應式&#xff1a; data() {return {config: {password1: "YQd^7D1",password2: "YQd^7D2",password3: "YQd^7D3"}}; }, computed: {transformedConfig() {if (this.config) {return Object.keys(this.config).map(k…

C++二維數組中的查找

4. 二維數組中的查找 題目鏈接 牛客網 題目描述 給定一個二維數組,其每一行從左到右遞增排序,從上到下也是遞增排序。給定一個數,判斷這個數是否在該二維數組中。 Consider the following matrix: [[1, 4, 7, 11, 15],[2, 5, 8, 12, 19],[3, 6, 9, 16, 22],[1…

深度之眼Paper帶讀筆記GNN.08.GCN(下)

文章目錄 前言細節四&#xff1a;卷積核介紹圖卷積核初代目圖卷積核二代目契比雪夫多項式例子小結 GCN公式推導 實驗設置和結果分析數據集節點分類任務消息傳遞方式比較運行效率 總結關鍵點創新點啟發點 代碼復現train.pyutil.pymodel.pylayer.py 作業 前言 本課程來自深度之眼…

基于單片機直流電機調速(proteus仿真+源程序)

一、系統方案 1、本設計采用這51單片機作為主控器。 2、轉速值送到液晶1602顯示。 3、按鍵設加減速&#xff0c;開始暫停、正反轉。 二、硬件設計 原理圖如下&#xff1a; 三、單片機軟件設計 1、首先是系統初始化 en0; rw0; write_com(0x01); //lcd初始化 write_com(0x38)…

CQ 社區版 V2.6.0 發布 | SQL閃回、權限看板、新增數據源人大金倉等

前言 HELLO&#xff0c;大家好&#xff0c;又到了 CloudQuery 社區版發版時間&#xff01;本次更新版本為 v2.6.0&#xff0c;亮點多多&#xff0c;我們直入主題一起來看&#xff01; 一、本期亮點 新增 3 種數據源支持 V2.6.0&#xff0c;新增三種國產數據源支持&#xff…

cocos2dx ??Animate3D (一)

3D相關的動畫都是繼承Grid3DAction 本質上是用GirdBase進行創建動畫的小塊。 Shaky3D 晃動特效 // 持續時間(時間過后不會回到原來的樣子) // 整個屏幕被分成幾行幾列 // 晃動的范圍 // z軸是否晃動 static Shaky3D* create(float initWithDuration, const Size& …

內存可見性與指令重排序

文章目錄 內存可見性內存可見性問題代碼演示JMM&#xff08;Java Memory Model&#xff09; 指令重排序指令重排序問題代碼演示指令重排序分析 volatile關鍵字volatile 保證內存可見性 & 禁止指令重排序volatile 不保證原子性 在上一節介紹線程安全問題的過程中&#xff0c…