0%

1 OC基础

1.1 分类(category)

使用场景:

  1. 给现有类添加方法

  2. 分解大类,如按功能划分

  3. 声明私有方法

底层实现(Runtime):

1
2
3
4
5
6
7
8
typedef struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
}

tips:分类不能添加对象,但可使用关联对象间接实现

分类和拓展的区别:

  1. 分类为运行时决议,扩展为编译时决议

  2. 分类有声明和实现,而拓展只以声明形式存在,多数情况下寄生于宿主类.m中

  3. 可以为系统类添加分类,但不能为系统类添加拓展

1.2 扩展(Extension)

使用场景:

  1. 声明私有属性
  2. 声明私有方法
  3. 声明私有成员变量

特点:

  1. 编译时决议
  2. 只以声明形式存在,多数情况下寄生于宿主类.m中
  3. 不能为系统类添加扩展

1.3 代理

特点:

  1. 是一种软件设计模式
  2. iOS中以@protocol形式体现
  3. 传递方式一对一

1.4 通知

特点:

  1. 使用观察者模式来实现的用意跨层传递消息的机制
  2. 传递方式为一对多

1.5 KVC(Key-value coding)

使用字符串关联进行赋值

就俩方法:

1
2
-(id)valueForKey:(NSString *)key
-(void)setValue:(id)value forKey:(NSString *)key

1.6 KVO(Key-value Observing)

是观察者模式的又一个实现

使用isa混写(isa-swizzling)实现

触发方法:

  1. 使用setter方法改变值

  2. 使用KVC赋值

  3. 手动触发KVO则需添加如下方法:

    1
    2
    3
    [self willChangeValueForKey:...];
    一般在这里修改值
    [self didChangeValueForKey:...];

1.7 属性关键字

1.7.1 读写权限相关
  1. readonly(只读)
  2. readwrite(读写)
1.7.2 原子性相关
  1. atomic(原子性)
  2. nonatomic(非原子性)
1.7.3 引用计数相关
  1. retain/strong
  2. assign/unsafe_unretained
  3. weak
  4. copy

1.8 Runtime

1.8.1 objc_object

数据结构包含:

1
2
3
4
5
6
7
isa_t isa;

关于isa操作相关

关联对象相关

内存管理相关
1.8.2 objc_class(继承自objc_object)

数据结构包含:

1
2
3
4
5
class superClass;

cache_t cache;

class_data_bits_t bits;
1.8.3 isa相关
1.8.3.1 isa指针类型(共用体isa_t)

指针型isa(isa的值代表class的地址)

非指针型isa(isa的值的部分代表class的地址)

1.8.3.2 指向
  1. 对象的isa指向它的类对象
  2. 类对象的isa指向它的元类对象
1.8.4 cache_t

定义:

  1. 用于快速查找方法执行函数

  2. 是可增量扩展的哈希表结构

  3. 是局部性原理的最佳应用

1.8.5 class_data_bits_t

定义:

  1. 是对class_rw_t的封装
  2. class_rw_t代表了类相关的读写信息,是对class_ro_t的封装
  3. class_ro_t代表了类相关的只读信息
1.8.6 class_rw_t

数据结构包含:

1
2
3
4
5
6
7
class_ro_t

protocols

properties

methods
1.8.7 class_ro_t

数据结构包含:

1
2
3
4
5
6
7
8
9
name

ivars

properties

protocols

methodlist
1.8.8 method_t

数据结构包含:

1
2
3
SEL name; // 名称
const char *types; // 返回值及参数
IMP imp; // 函数体
1.8.9 消息传递
1.8.9.1 objc_msgSend方法
1
2
3
4
5
void obic_msgSend(void /*id self, SEL op, ...*/)
例:
[self class];

objc_msgSend(self,@Selector(class))
1.8.9.2 objc_msgSendSuper方法
1
2
3
4
5
6
7
8
9
10
void objc_msgSendSuper(void /*struct objc_super *super,SEL op, ...*/)
其中super结构如下:
struct objc_super {
_unsafe_unretained id receiver;
...
}
例:
[super class];
即:
objc_msgSendSuper(super,@Selector(class))

面试可能问题:

1、在同一个类中调用[self class] 和 [super class]得到的结果分别是什么?

答案必须都是当前类喽,因为消息的接受者都是当前对象

1.8.10 消息转发

消息转发共有三个阶段:方法解析处理阶段、快速转发阶段、常规转发阶段

越早处理,所付出的代价越小

1.8.10.1 resolveClassMethod/resolveInstanceMethod

这个阶段,用于通过Runtime方式动态添加方法实现IMP

涉及方法:

1
2
+ (BOOL)resolveClassMethod:(SEL)sel // 类方法
+ (BOOL)resolveInstanceMethod:(SEL)sel // 实例方法

例:

1
2
3
4
5
6
7
8
9
10
11
+(BOOL)resolveInstanceMethod:(SEL)sel{
//判断是否为外部调用的方法
if ([NSStringFromSelector(sel) isEqualToString:@"testFunction"]) {
/**
对类进行对象方法 需要把方法添加进入类内
*/
[LMRuntimeTool addMethodWithClass:[self class] withMethodSel:sel withImpMethodSel:@selector(addDynamicMethod)];
return YES;
}
return [super resolveInstanceMethod:sel];
}
1.8.10.2 forwardingTargetForSelector

这个阶段用于将消息转发给另一个对象接受,该对象应该具备该消息的具体实现

涉及方法:

1
-(id)forwardingTargetForSelector:(SEL)aSelector

例:

1
2
3
4
5
6
-(id)forwardingTargetForSelector:(SEL)aSelector{
if ([NSStringFromSelector(aSelector) isEqualToString:@"testFunction"]) {
return [BackupTestMessage new];
}
return [super forwardingTargetForSelector:aSelector];
}
1.8.10.3 methodSignatureForSelector和forwardInvocation

该阶段可以将消息转发给多个对象

涉及方法:

1
2
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
-(void)forwardInvocation:(NSInvocation *)anInvocation

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
//如果返回为nil则进行手动创建签名
if ([super methodSignatureForSelector:aSelector]==nil) {
NSMethodSignature * sign = [NSMethodSignature signatureWithObjCTypes:"v@:"];
return sign;
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
//创建备用对象
BackupTestMessage * backUp = [BackupTestMessage new];
SEL sel = anInvocation.selector;
//判断备用对象是否可以响应传递进来等待响应的SEL
if ([backUp respondsToSelector:sel]) {
[anInvocation invokeWithTarget:backUp];
}else{
// 如果备用对象不能响应 则抛出异常
[self doesNotRecognizeSelector:sel];
}
}

1.9 Runloop

1.9.1 数据结构

包含数据:

1
2
3
4
5
pthread
currentMode
modes
commonModes
commonModeItems
1.9.2 CFRunloopMode

数据结构包含:

1
2
3
4
5
name	如:NSDefaultRunloopMode
source0 集合
source1 集合
observers 数组
timers 数组
1.9.3 CFRunloopSource
1.9.3.1 source0

需要手动唤醒线程

1.9.3.2 source1

具备唤醒线程的能力

1.9.4 CFRunloopTimer
1.9.5 CFRunloopObserver

观测时间点:

  1. kCFRunloopEntry
  2. kCFRunloopBeforeTimers
  3. kCFRunloopBeforeSources
  4. kCFRunloopBeforeWaiting
  5. kCFRunloopAfterWaiting
  6. KcfRunloopExit

2 设计原则

2.1 单一职责原则

一个类只负责一件事

2.2 依赖倒置原则

抽象不应该依赖于具体实现,具体实现可以依赖于抽象

2.3 开闭原则

对修改关闭,对扩展开放

2.4 里氏替换原则

父类可以被子类无缝替换,且原有功能不受任何影响

2.5 接口隔离原则

使用多个专门的协议,而不是一个庞大臃肿的协议

2.6 迪米特法则

一个对象应当对其他对象有尽可能少的了解,高内聚、低耦合

3 设计模式

3.1 代理模式

3.2 工厂模式

3.3 观察者模式

3.4 单例模式

3.5 MVC

3.6 MVVM

3.7 MVP

4 算法

4.1 不用中间变量,交换A和B的值

1
2
3
4
5
6
7
8
9
10
11
12
13
// 加法
void reserve(int a, int b) {
a = a + b;
b = a - b;
a = a - b;
}

// 异或法(不进位2进制加法)
void reserve(int a, int b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}

4.2 求最大公约数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 遍历法
int method1(int a, int b) {
int max = 0;
for (int i = 0; i <= b; i++) {
if (a % i == 0 && b % i == 0) {
max = i;
}
}
return max;
}

// 辗转相除法(这里要求a>=b)
int method2(int a, int b) {
int r;
while (a % b > 0) {
r = a % b;
a = b;
b = r;
}
return b;
}

4.3 判断质数

质数指只能被1或本身整除的数

1
2
3
4
5
6
7
8
9
10
int check(int a) {
// sqrt用于求非负数的平方根,减少循环次数
for (int i = 2; i <= sqrt(a); i ++) {
if (a % i == 0) {
return 0;
}
}

return 1;
}

4.4 字符串反转

1
2
3
4
5
6
7
8
9
void reserve(char *cha,int length) {
char *begin = cha;
char *end = cha + strlen(cha) - 1;
while (begin < end) {
char temp = *begin;
*(begin ++) = *end;
*(end --) = temp;
}
}

4.5 选择排序

选出最小/最大的数,放在第一位,然后再在剩下的列表数据里再选出最小/最大数放在第二位,以此类推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (NSArray *)sort1:(NSArray *)array {
NSMutableArray<NSNumber *> *res = [array mutableCopy];

for (int i = 0; i < res.count - 1; i ++) {
int index = i;
for (int j = i + 1; j < res.count; j ++) {
if (res[j].intValue < res[index].intValue) {
index = j;
}
}

if (index > i) {
NSNumber *temp = res[index];
res[index] = res[i];
res[i] = temp;
}
}

return [res copy];
}

4.6 冒泡排序

从首位起,与右侧数比较,大的话交换,直到最后一位。然后重新从首位开始同样进行比较及交换操作,直到最后第二位,以此类推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (NSArray *)sort2:(NSArray *)array {
NSMutableArray<NSNumber *> *res = [array mutableCopy];

for (int i = 0; i < res.count - 1; i ++) {
for (int j = 0; j < res.count - i - 1; j ++) {
if (res[j].intValue > res[j + 1].intValue) {
NSNumber *temp = res[j];
res[j] = res[j + 1];
res[j + 1] = temp;
}
}
}

return [res copy];
}

4.7 折半查找

数组必须是有序的

搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int findKey(int *arr, int length, int key) {
int min = 0, max = length - 1, mid;
while (min <= max) {
mid = (min + max) / 2; //计算中间值
if (key > arr[mid]) {
min = mid + 1;
} else if (key < arr[mid]) {
max = mid - 1;
} else {
return mid;
}
}
return -1;
}

4.8 快速排序

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];

while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}

5 性能优化

参考文章:https://www.jianshu.com/p/4e9c6a048f6f

5.1 卡顿优化

  1. 尽可能减少CPU、GPU资源消耗

  2. 按照60FPS的刷帧率,每隔16ms就会有一次VSync信号

  3. 尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用CALayer取代UIView

  4. 不要频繁地调用UIView的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改

  5. 尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性

  6. Autolayout会比直接设置frame消耗更多的CPU资源

  7. 图片的size最好刚好跟UIImageView的size保持一致

  8. 控制一下线程的最大并发数量

  9. 尽量把耗时的操作放到子线程

  10. 文本处理(尺寸计算、绘制)

  11. 图片处理(解码、绘制)

  12. 尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示

  13. GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用CPU资源进行处理,所以纹理尽量不要超过这个尺寸

  14. 尽量减少视图数量和层次

  15. 减少透明的视图(alpha<1),不透明的就设置opaque为YES

  16. 尽量避免出现离屏渲染

离屏渲染消耗性能的原因:

  1. 需要创建新的缓冲区
  2. 离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕

离屏渲染的触发场景:

  1. 光栅化,layer.shouldRasterize = YES
  2. 遮罩,layer.mask
  3. 圆角,同时设置layer.masksToBounds = YES、layer.cornerRadius大于0
  4. 阴影,layer.shadowXXX

卡顿检测:

可以添加Observer到主线程RunLoop中,通过监听RunLoop状态切换的耗时,以达到监控卡顿的目的

5.2 耗电优化

5.2.1 尽可能降低CPU、GPU功耗
  1. 少用定时器
5.2.2 优化I/O操作
  1. 尽量不要频繁写入小数据,最好批量一次性写入
  2. 读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的API。用dispatch_io系统会优化磁盘访问
  3. 数据量比较大的,建议使用数据库(比如SQLite、CoreData)

5.3 网络优化

  1. 减少、压缩网络数据
  2. 如果多次请求的结果是相同的,尽量使用缓存
  3. 使用断点续传,否则网络不稳定时可能多次传输相同的内容
  4. 网络不可用时,不要尝试执行网络请求
  5. 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间
  6. 批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果下载广告,一次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载

5.4 定位优化

  1. 如果只是需要快速确定用户位置,最好用CLLocationManager的requestLocation方法。定位完成后,会自动让定位硬件断电
  2. 如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务
  3. 尽量降低定位精度,比如尽量不要使用精度最高的kCLLocationAccuracyBest
  4. 需要后台定位时,尽量设置pausesLocationUpdatesAutomatically为YES,如果用户不太可能移动的时候系统会自动暂停位置更新
  5. 尽量不要使用startMonitoringSignificantLocationChanges,优先考虑startMonitoringForRegion:

5.5 硬件检测优化

用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件

5.6 APP的启动优化

通过添加环境变量可以打印出APP的启动时间分析(Edit scheme -> Run -> Arguments)
DYLD_PRINT_STATISTICS设置为1
如果需要更详细的信息,那就将DYLD_PRINT_STATISTICS_DETAILS设置为1

APP的冷启动可以概括为3大阶段

  1. dyld
  2. runtime
  3. main

APP的启动 - dyld

dyld(dynamic link editor),Apple的动态链接器,可以用来装载Mach-O文件(可执行文件、动态库等)

  • 启动APP时,dyld所做的事情有
  • 装载APP的可执行文件,同时会递归加载所有依赖的动态库
  • 当dyld把可执行文件、动态库都装载完毕后,会通知Runtime进行下一步的处理

APP的启动 - runtime

  • 启动APP时,runtime所做的事情有
  • 调用map_images进行可执行文件内容的解析和处理
  • 在load_images中调用call_load_methods,调用所有Class和Category的+load方法
  • 进行各种objc结构的初始化(注册Objc类 、初始化类对象等等)
  • 调用C++静态初始化器和attribute((constructor))修饰的函数
  • 到此为止,可执行文件和动态库中所有的符号(Class,Protocol,Selector,IMP,…)都已经按格式成功加载到内存中,被runtime 所管理

总结一下

  • APP的启动由dyld主导,将可执行文件加载到内存,顺便加载所有依赖的动态库
  • 并由runtime负责加载成objc定义的结构
  • 所有初始化工作结束后,dyld就会调用main函数
  • 接下来就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法

APP的启动优化

dyld阶段

  • 减少动态库、合并一些动态库(定期清理不必要的动态库)
  • 减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类)
  • 减少C++虚函数数量
  • Swift尽量使用struct

runtime阶段

  • 用+initialize方法和dispatch_once取代所有的attribute((constructor))、C++静态构造器、ObjC的+load

main阶段

  • 在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在finishLaunching方法中
  • 按需加载

5.7 安装包瘦身

  1. 采取无损压缩
  2. 去除没有用到的资源
  3. 编译器优化
    • Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default设置为YES
    • 去掉异常支持,Enable C++ Exceptions、Enable Objective-C Exceptions设置为NO, Other C Flags添加-fno-exceptions
    • 利用AppCode检测未使用的代码:菜单栏 -> Code -> Inspect Code
    • 编写LLVM插件检测出重复代码、未被调用的代码

木有笔试流程,直接面试,共三轮

第一二轮-技术面+负责人面

  • 响应链相关

  • 启动优化方案

  • 卡顿优化方案

  • 动态配置方案

  • IM如果在同一时间接受到巨大数量的消息时,如何控制消息列表页面刷新,避免卡顿等问题。后到的消息显示在前面

  • 页面上有一table,需要对cell进行曝光埋点,在什么位置埋点

  • 如何手动实现多重继承

  • 阐述对KVC、KVO的理解

  • 模块化方案

  • 解耦方式

  • natomic的理解

  • 对mutable对象使用copy修饰符修饰,会造成什么影响

  • @synthesize和@dynamic的作用及使用场景

以及简历上的项目相关问题

第三轮-hr面

  • 薪资待遇
  • 离职原因
  • 职业规划

环境:腾讯云 Center OS 7.5.1804

web框架:koa2

操作日期:2020.6.21

1 安装node

1.1 下载node

node官方下载网址:http://nodejs.cn/download/

wget https://npm.taobao.org/mirrors/node/v14.4.0/node-v14.4.0-linux-x64.tar.xz

1.2 解压

tar -xvf node-v14.4.0-linux-x64.tar.xz

1.3 建立软连接

1
2
3
ln -s /root/development/node-v14.4.0-linux-x64/bin/node /usr/local/bin/node

ln -s /root/development/node-v14.4.0-linux-x64/bin/npm /usr/local/bin/npm

tips:如果建立错了,可以使用 rm /usr/local/bin/node 方式删除,命令中前面的地址为你解压node压缩包后的绝对路径

完成以上步骤,就安装完成啦!试试 npm -v 和 node -v吧!

2 将代码工程上传

个人采用git来拉取

2.1 安装git

1
yum install -y git

2.2 拉取工程

1
git clone ….

2.3 安装

一般情况下需cd到工程根目录,执行 npm install

3 nginx

3.1 安装nginx

1
sudo yum install -y nginx

3.2 配置nginx开机运行

1
2
3
sudo systemctl start nginx.service

sudo systemctl enable nginx.service

3.3 配置nginx

1
vi /etc/nginx/nginx.conf

在http下面查看是否存在 include /etc/nginx/conf.d/*.conf; (没有的话加上)

然后新建配置 touch /etc/nginx/conf.d/**.conf(号随便命名)

然后修改该文件,写入如下内容(注释部分仅用于https,http不需要)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
upstream nodejs {

​ server 127.0.0.1:3000; #这是你工程运行后的访问端口

​ keepalive 64;

}

server {

​ listen 80; # 这里如果配置https的话写443 ssl,并打开下面其他注释行

​ server_name 你的域名;

\# ssl_certificate 域名的nginx目录下的crt后缀文件[;](http://www.lyj.center_bundle.crt;/)

\# ssl_certificate_key 域名的nginx目录下的key后缀文件[;](http://www.lyj.center.key;/)

\# ssl_session_timeout 5m;

\# ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;

\# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

\# ssl_prefer_server_ciphers on;

​ location / {

​ proxy_set_header X-Real-IP $remote_addr;

​ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

​ proxy_set_header Host $http_host;

​ proxy_set_header X-Nginx-Proxy true;

​ proxy_set_header Connection "";

​ proxy_pass [http://nodejs;](http://nodejs;/)

​ }

}

\#server {

\# listen 80;

\# server_name 你的域名;

\# rewrite ^/(.*) https://$server_name$1 permanent;

\#}

3.4 重新加载Nginx配置

1
2
3
4
5
6
7
8
9
10
11
12
13
nginx -s reload

tips:nginx常用命令

nginx -s reopen #重启Nginx

nginx -s reload #重新加载Nginx配置文件,然后以优雅的方式重启Nginx

nginx -s stop #强制停止Nginx服务

nginx -s quit #优雅地停止Nginx服务(即处理完所有请求后再停止服务)

nginx -t #检测配置文件是否有语法错误,然后退出

4 pm2部署

4.1 安装pm2

1
npm install -g pm2

4.2 建立软连接

1
ln -s /root/development/node-v14.4.0-linux-x64/bin/pm2 /usr/local/bin/pm2

4.3 执行(这里直接使用了koa2脚手架的默认部署脚本,其他配置方式请自行百度)

1
pm2 start bin/www --watch

5 域名解析

记得在腾讯云域名解析那里加上解析记录(在”我的域名”->点击对应域名右边的“解析”)

选“快速添加网站/邮箱解析” -> “网站解析” -> 填写主机外网ip地址 就ok了

tips:第一列主机记录可自行更改,具体情况会有提示滴

完成以上配置应该就能在外网通过你的域名访问啦

django线上部署

基于腾讯云服务器ubuntu+Django+uwsgi+nginx

由于懒得麻烦,我使用git将项目提交,并在ubuntu上使用git pull拉取来传项目至ubuntu服务器。避免传输文件的复杂操作。

1、安装 nginx

1
sudo apt-get install python-dev nginx

2、安装 uwsgi

1
sudo pip install uwsgi --upgrade

3、配置uwsgi

在项目中manage.py同级目录下,生成一个mysite_uwsgi.ini(mysite字段为项目名)
配置内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# mysite_uwsgi.ini file
[uwsgi]

# Django-related settings

socket = :8000 # 这里指的是本地端口,非外网端口,需与nginx内配置的一致

# the base directory (full path)
chdir = /home/ubuntu/myWeb/mysite # 这里是django项目的根路径

# Django s wsgi file
module = mysite.wsgi # 项目名.wsgi

# process-related settings
# master
master = true

# maximum number of worker processes
processes = 4

# ... with appropriate permissions - may be needed
# chmod-socket = 664
# clear environment on exit
vacuum = true

4、配置nginx

修改 /etc/nginx/nginx.conf 文件,在http配置项内增加如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server {
listen 80; # 这里是监听的对外端口,默认为80
server_name www.abc.com; # 这里是你申请的域名
charset UTF-8;
access_log /var/log/nginx/mysite_access.log; # 日志文件,文件名为 项目名_access.log
error_log /var/log/nginx/mysite_error.log; # 日志文件,文件名为 项目名_error.log

client_max_body_size 75M;

location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:8000; # 注意,这里配置的端口8000,必须与上面uwsgi内的socket端口一致
uwsgi_read_timeout 2;
}
location /static {
expires 30d;
autoindex on;
add_header Cache-Control private;
alias /home/ubuntu/myWeb/mysite/static/; # 这里是static文件夹的路径
}
}

5、启动uwsgi

确认当前uwsgi进程关闭,然后启动

(1)查看uwsgi进程
1
ps aux | grep uwsgi
(2)关闭uwsgi进程
1
pkill -f -9 uwsgi
(3)启动uwsgi
1
uwsgi --ini mysite_uwsgi.ini (这里需要ini文件完整路径,或者cd到项目目录执行这行命令)

6、启动nginx

(1)启动命令
1
sudo /etc/init.d/nginx start
(2)关闭命令
1
sudo /etc/init.d/nginx stop
(3)重启命令
1
sudo /etc/init.d/nginx restart

7、腾讯云 安全组配置

有个选项叫:放通22,80,443,3389端口和ICMP协议-201804141602585324,对!就选这个!

8、额外写点域名购买配置的过程

(1)购买域名

直接在腾讯云购买域名,付完钱就ok了

(2)我的域名

好像要先添加解析吧,把你的域名和你服务器的外网ip绑定
然后如果看到服务状态不可用的话,你必须要实名认证
至于备案,我反正还木有成功,但是已经可以正常访问我的域名了

FAQ

错误1:locale.Error: unsupported locale setting

解决方案:
1
终端执行命令:export LC_ALL=C

错误2:django线上部署后,admin样式丢失

解决方案:
1
2
3
1、setting.py中加上 
STATIC_ROOT = os.path.join(BASE_DIR,'static')
2、python manage.py collectstatic

错误3:nginx: [emerg] “server” directive is not allowed here in E:\nginx-1.7.5/conf/nginx.conf:1

解决方案:
1
这是因为nginx.conf中的配置格式错误,本人造成这个错误是由于server配置写在了最外层,与http同级。正确的server配置应当写在http配置内

错误4:/etc/nginx/nginx.conf文件只读,不可修改

解决方案:
1
2
3
1、修改时先 sudo chmod 777 /etc/nginx/nginx.conf
2、修改配置 vim /etc/nginx/nginx.conf
3、修改完成后 sudo chmod 644 /etc/nginx/nginx.conf

1、申请腾讯云免费SSL证书

腾讯云可以申请一次免费的SSL证书,使用期限是1年

申请的地方:腾讯云->云产品->域名与网站->SSL证书管理

进入证书管理页面后,点击申请证书按钮,弹出来的框里就有那个免费的选项亚洲诚信免费版DVSSL证书

点击确定后,输入你要配置https的域名(注意不要填错,必须是要使用https的那个域名),然后输入邮箱地址等等。

申请一般很快的,我等了半小时就申请下来了。

2、下载证书

等申请下来证书后,下载证书。是一个压缩包,解压后可以看到目录是:

1
2
3
4
5
Apache
IIS
Nginx
Tomcat
你的域名.csr

3、配置Nginx

取上面下载的证书中Nginx文件夹下的两个证书文件,应当分别是.crt.key两个格式的证书文件。具体操作如下:

第一步
上传这两个文件到服务器

第二步
修改/etc/nginx/nginx.conf文件

1
2
3
sudo chmod 777 /etc/nginx/nginx.conf
vim /etc/nginx/nginx.conf
sudo chmod 644 /etc/nginx/nginx.conf

修改内容为:

1
2
3
4
5
6
7
8
9
server {
listen 443;

ssl on;
ssl_certificate 这里写.crt格式的证书路径;
ssl_certificate_key 这里写.key格式的证书路径;

# 其他原有的配置不变
}

仅需要改一下端口从原来的80,变成443,然后开启ssl的配置就行。

第三步

重启Nginx

1
sudo /etc/init.d/nginx restart