Nginx入门

1.Nginx简介

•Nginx(“engine x”)是一个高性能的HTTP和反向代理服务器,也是一IMAP/POP3/SMTP代理服务器。
•第一个公开版本0.1.0发布于2004年10月4日。
•其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名
•官方测试nginx能够支支撑5万并发链接,并且cpu、内存等资源消耗却非常低,运行非常稳定

2011年6月1日,nginx1.0.4发布。
•Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like协议下发行。由俄罗斯的程序设计师IgorSysoev所开发,其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:新浪、网易、腾讯等。
在这里插入图片描述

功能:

1.web服务器
2.webreverseproxy
3.smtpreverseproxy
官网:
在这里插入图片描述

2.Nginx和apache(PHP)的优缺点(面试题)

nginx相对于apache的优点:
轻量级,同样起web服务,比apache占用更少的内存及资源
抗并发,nginx处理请求是异步非阻塞的,而apache则是阻塞型的,在高并发下nginx
能保持低资源低消耗高性能
高度模块化的设计,编写模块相对简单
社区活跃,各种高性能模块出品迅速
apache相对于nginx的优点:
rewrite,比nginx的rewrite强大
模块超多,基本想到的都可以找到
少bug,nginx的bug相对较多
Nginx配置简洁,Apache复杂
最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;
nginx是异步的,多个连接(万级别)可以对应一个进程

3.Tengine

Tengine 是nginx的加强版,封装版,淘宝开源 基于Nginx
– 官网http://tengine.taobao.org/
– 动态模块加载(DSO)支持。加入一个模块不再需要重新编译整个Tengine;
– 支持SO_REUSEPORT选项,建连性能提升为官方 nginx的三倍;
– 支持SPDY v3协议,自动检测同一端口的SPDY请求和HTTP请求;
– 流式上传到HTTP后端服务器或FastCGI服务器,大量减少机器的I/O压力;
– 更加强大的负载均衡能力,包括一致性hash模块、会话保持模块,还可以对后端的服务器进行主动健康检查,根据服务器状态自动上线下线,以及动态解析upstream中出现的
域名;
– 输入过滤器机制支持。通过使用这种机制Web应用防火墙的编写更为方便;
– 支持设置proxy、memcached、fastcgi、scgi、uwsgi在后端失败时的重试次数
– 动态脚本语言Lua支持。扩展功能非常高效简单;
– 支持管道(pipe)和syslog(本地和远端)形式的日志以及日志抽样;
– 支持按指定关键字(域名,url等)收集Tengine 运行状态;
– 组合多个CSS、JavaScript文件的访问请求变成一个请求;
– 自动去除空白字符和注释从而减小页面的体积

在这里插入图片描述

在这里插入图片描述

4.面临的问题

• 如何解决高并发和负载均衡
• 如何解决高可用问题
• 如何负载均衡的session一致性问题(后期用redis解决)

5.传统的WEB架构高并发问题

• 单个tomcat支持最高并发
• 怎么解决高并发问题,解决单个服务器过载问题
在这里插入图片描述
• 前端和后端架构
• Tomcat: 一个Servlet和JSP容器
• 前端服务器处理静态页面
在这里插入图片描述

6.Tengine的编译安装

6.1 安装之前准备

上传安装文件到指定目录

在这里插入图片描述
解压安装文件
[root@21 opt]# tar -zxvf tengine-2.1.0.tar.gz
在这里插入图片描述
安装依赖: yum -y install gcc openssl-devel pcre-devel zlib-devel

在这里插入图片描述

6.2编译安装tengine

在tengine目录中执行以下命令:

–prefix=/opt/tengine-2.1.0/ \ 注意:这里需要改为你自己的tengin的安装路径

./configure \
  --prefix=/opt/soft/tengine-2.1.0/ \
  --error-log-path=/var/log/nginx/error.log \
  --http-log-path=/var/log/nginx/access.log \
  --pid-path=/var/run/nginx/nginx.pid  \
  --lock-path=/var/lock/nginx.lock \
  --with-http_ssl_module \
  --with-http_flv_module \
  --with-http_stub_status_module \
  --with-http_gzip_static_module \
  --http-client-body-temp-path=/var/tmp/nginx/client/ \
  --http-proxy-temp-path=/var/tmp/nginx/proxy/ \
  --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \
  --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \
  --http-scgi-temp-path=/var/tmp/nginx/scgi \
  --with-pcre 

执行安装命令:

 make && make install

6.3为tengine准备启动文件

在/etc/init.d目录下,创建一个启动文件名字为 nginx,并粘贴以下脚本:

#!/bin/bash
#
# chkconfig: - 85 15
# description: nginx is a World Wide Web server. It is used to serve
# Source function library.
. /etc/rc.d/init.d/functions
 
# Source networking configuration.
. /etc/sysconfig/network
 
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
 #这里为需要修改为安装路径
nginx="/opt/soft/tengine-2.1.0/sbin/nginx"
prog=$(basename $nginx)
NGINX_CONF_FILE="/opt/soft/tengine-2.1.0/conf/nginx.conf"
 
#[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
 
lockfile=/var/lock/subsys/nginx
 
#make_dirs() {
#   # make required directories
#   user=`nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`
#   options=`$nginx -V 2>&1 | grep 'configure arguments:'`
#   for opt in $options; do
#       if [ `echo $opt | grep '.*-temp-path'` ]; then
#           value=`echo $opt | cut -d "=" -f 2`
#           if [ ! -d "$value" ]; then
#               # echo "creating" $value
#               mkdir -p $value && chown -R $user $value
#           fi
#       fi
#   done
#}
 
start() {
    [ -x $nginx ] || exit 5
    [ -f $NGINX_CONF_FILE ] || exit 6
#    make_dirs
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}
 
stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}
 
restart() {
    configtest || return $?
    stop
    sleep 1
    start
}
 
reload() {
    configtest || return $?
    echo -n $"Reloading $prog: "
#  -HUP是nginx平滑重启参数  
    killproc $nginx -HUP
    RETVAL=$?
    echo
}
 
force_reload() {
    restart
}
 
configtest() {
  $nginx -t -c $NGINX_CONF_FILE
}
 
rh_status() {
    status $prog
}
 
rh_status_q() {
    rh_status >/dev/null 2>&1
}
 
case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart|configtest)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
            ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
        exit 2
esac

在这里插入图片描述
给启动脚本可执行权限
[root@21 init.d]# chmod 755 nginx

创建该目录:
[root@21 init.d]# mkdir -p /var/tmp/nginx/client/

启动nginx
[root@bruce1 init.d]# systemctl start nginx

在这里插入图片描述
测试:http://192.168.220.21
在这里插入图片描述

注意:重新启动后会报

nginx: [emerg] open() "/var/run/nginx/nginx.pid" failed (2: No such file or directory)

需要在nginx的配置文件中
在这里插入图片描述
打开pid:配置nginx的安装路径.

7.Tengine的配置文件详解

在tengine的安装文件中,有一个核心的配置文件
在这里插入图片描述

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}

# load modules compiled as Dynamic Shared Object (DSO)
#
#dso {
#    load ngx_http_fastcgi_module.so;
#    load ngx_http_rewrite_module.so;
#}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

配置文件解析

–#定义Nginx运行的用户和用户组
–user www www;

–#nginx进程数,建议设置为等于CPU总核心数。
–worker_processes 8;

–#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]
–error_log /var/log/nginx/error.log info;

–#进程文件
–pid /var/run/nginx.pid;

–#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值
ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的
值保持一致。
–worker_rlimit_nofile 65535;

#工作模式与连接数上限
events
{
	#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ];
epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在
FreeBSD上面,就用kqueue模型。
	use epoll;
	#单个进程最大连接数(最大连接数=连接数*进程数)
	worker_connections 65535;
}

event下的一些配置及其意义
–	#单个后台worker process进程的最大并发链接数
–	worker_connections  1024;
–	# 并发总数是 worker_processes 和 worker_connections 的乘积
–	# 即 max_clients = worker_processes * worker_connections
–	# 在设置了反向代理的情况下,max_clients = worker_processes *
worker_connections / 4  为什么
–	# 为什么上面反向代理要除以4,应该说是一个经验值
–	# 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 =
32000
–	# worker_connections 值的设置跟物理内存大小有关
–	# 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数

–   # 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的
文件数大约是10万左右
–	# 我们来看看360M内存的VPS可以打开的文件句柄数是多少:
–	# $ cat /proc/sys/fs/file-max
–	# 输出 34336
–	# 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操
作系统可以承受的范围之内
–	# 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以
打开的最大文件总数进行适当地进行设置
–	# 使得并发总数小于操作系统可以打开的最大文件数目
–	# 其实质也就是根据主机的物理CPU和内存进行配置
–	# 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需
要消耗系统资源。
–	# ulimit -SHn 65535

 http下的一些配置及其意义
–	#设定http服务器
–	http
–	{
–	include mime.types; #文件扩展名与文件类型映射表
–	default_type application/octet-stream; #默认文件类型
–	#charset utf-8; #默认编码
–	server_names_hash_bucket_size 128; #服务器名字的hash表大小
–	client_header_buffer_size 32k; #上传文件大小限制
–	large_client_header_buffers 4 64k; #设定请求缓
–	client_max_body_size 8m; #设定请求缓
–	sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来
输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置
为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常
把这个改成off。
–	autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。
–	tcp_nopush on; #防止网络阻塞
–	tcp_nodelay on; #防止网络阻塞
–	keepalive_timeout 120; #长连接超时时间,单位是秒

gzip的一些配置及其意义
–   #gzip模块设置
gzip on; #开启gzip压缩输出
gzip_min_length 1k; #最小压缩文件大小
gzip_buffers 4 16k; #压缩缓冲区
gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
gzip_comp_level 2; #压缩等级
gzip_types text/plain application/x-javascript text/css application/xml;
#压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题
,但是会有一个warn。
gzip_vary on;
#limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用

虚拟主机一些配置及其意义
–   #虚拟主机的配置
server
{
#监听端口
listen 80;
#域名可以有多个,用空格隔开
server_name www.ha97.com ha97.com;
index index.html index.htm index.jsp;
root /data/www/ha97;
location ~ .*\.(php|php5)?$
{
  fastcgi_pass 127.0.0.1:9000;
  fastcgi_index index.jsp;
  include fastcgi.conf;
}

8.反向代理

8.1 反向代理的概念

通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定

代理服务器,并将本来要直接发送到Web服务器上的http请求发送到代理服务器中由代理服务器向Internet上的web服务器发起请求,最终达到客户机上网的的。而反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
在这里插入图片描述

8.2 经典的反向代理结构

在这里插入图片描述

upstream mynginx{
       server  192.168.221.22:8080;
       server  192.168.221.23:8080;
    }
    server {
        listen        80;
        server_name  www.nginx1.cn;

        location / {
            proxy_pass http://mynginx;
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

测试
在这里插入图片描述

8.4 反向代理的健康检查模块

 upstream mynginx{
       server  192.168.221.132:8080;
       server  192.168.221.133:8080;
#这里配置
       check interval=3000 rise=2 fall=5 timeout=1000 type=http;
       check_http_send "HEAD / HTTP/1.0\r\n\r\n";
       check_http_expect_alive http_2xx http_3xx;
    }
    server {
        listen       80;
        server_name  www.nginx1.cn;

        location / {
            proxy_pass http://mynginx;
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
#这里访问路径
        location /ngx_status{
          check_status;
         }

    }

测试
在这里插入图片描述
当其中的一个服务器宕机
在这里插入图片描述

9.Nginx的session一致性问题

http协议是无状态的,即你连续访问某个网页100次和访问1次对服务器来说是没有区别对待的,因为它记不住你。那么,在一些场合,确实需要服务器记住当前用户怎么办?比
如用户登录邮箱后,接下来要收邮件、写邮件,总不能每次操作都让用户输入用户名和密码吧,为了解决这个问题,session的方案就被提了出来,事实上它并不是什么新技术,
而且也不能脱离http协议以及任何现有的web技术。
session的常见实现形式是会话cookie(session cookie),即未设置过期时间的cookie,这个cookie的默认生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。
实现机制是当用户发起一个请求的时候,服务器会检查该请求中是否包含sessionid,如果未包含,则系统会创造一个名为JSESSIONID的输出 cookie返回给浏览器(只放入内存,并不
存在硬盘中),并将其以HashTable的形式写到服务器的内存里面;当已经包含sessionid是,服务端会检查找到与该session相匹配的信息,如果存在则直接使用该sessionid,若不存在则重新生成新的 session。这里需要注意的是session始终是有服务端创建的,并非浏览器自己生成的。但是浏览器的cookie被禁止后session就需要用get方法的URL重写的
机制或使用POST方法提交隐藏表单的形式来实现。

Session共享:

首先我们应该明白,为什么要实现共享,如果你的网站是存放在一个机器上,那么是不存在这个问题的,因为会话数据就在这台机器,但是如果你使用了负载均衡把请求分发到不同的机器呢?这个时候会话id在客户端是没有问题的,但是如果用户的两次请求到了两台不同的机器,而它的session数据可能存在其中一台机器,这个时候就会出现取不到session数据的情况,于是session的共享就成了一个问题。

10.Nginx的session一致性问题解决方案

1、session复制

tomcat 本身带有复制session的功能。(不用)

2、共享session

需要专门管理session的软件,memcached 缓存服务,可以和tomcat整合,帮助tomcat

共享管理session。

10.1 使用memcached解决session一致性问题

安装memcached
yum install -y memcached
启动memcached

service memcached start

在这里插入图片描述
测试memcached
在这里插入图片描述
退出 quit

向tomcat服务器lib中放入jar包,目的是让tomcat往缓存数据库中存取session,将以下jar包拷贝到lib目录下
在这里插入图片描述
修改server.xml里面修改Engine标签,添加jvmRoute属性,目的是查看sessionid里面带有tomcat的名字,就是这里配置的jvmRoute

    <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

在这里插入图片描述
配置tomcat的conf目录下的context.xml

<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
        memcachedNodes="n1:192.168.17.9:11211"
    sticky="false"
    lockingMode="auto"
    sessionBackupAsync="false"
        requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
sessionBackupTimeout="1000" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />

配置memcachedNodes属性,配置memcached数据库的ip和端口,默认11211,多个的话用空格隔开

目的?让tomcat服务器从memcached缓存里面拿session或者是放session
测试session是否一致
在这里插入图片描述

10.2使用redis解决session一致性问题(常用)

安装redis

启动时,制定配置文件

[root@22 redis]# ./bin/redis-server ./redis.conf

在这里插入图片描述
Redis默认端口:6379,通过当前服务进行查看

[root@22 redis]# ps -ef | grep -i redis
在这里插入图片描述
正确停止Redis的方式应该是向Redis发送SHUTDOWN命令,方法为(关闭默认的端口)

[root@22 redis]# ./bin/redis-cli shutdown
在这里插入图片描述
Ping,测试客户端与Redis的连接是否正常,如果连接正常,回收到pong
在这里插入图片描述

[root@22 redis]# ./bin/redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> ping
PONG

修改redis配置文件

安装redis缓存数据库,修改配置文件vi /etc/redis.conf将bind的127.0.0.1修改为本机地址,否则只能本机访问了
在这里插入图片描述
导入redislib中三个jar包到tomcat中
在这里插入图片描述
配置tomcat的conf目录下的context.xml

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         host="192.168.221.22"
         port="6379"
         database="0"
         maxInactiveInterval="60" />

测试session是否一致
在这里插入图片描述

11.Tengine的会话保持

11.1 Tengine的会话保持模块简介

ngx_http_upstream_session_sticky_module

该模块是一个负载均衡模块,通过cookie实现客户端与后端服务器的会话保持, 在一定条件下可以保证同一个客户端访问的都是同一个后端服务器.
**mode****设置cookie的模式
Ø insert: 在回复中本模块通过Set-Cookie头直接插入相应名称的cookie。
Ø prefix: 不会生成新的cookie,但会在响应的cookie值前面加上特定的前缀,当浏览器带着这个有特定标识的cookie再次请求时,模块在传给后端服务前先删除加入的前缀,后端服务拿到的还是原来的cookie值,这些动作对后端透明。如:“Cookie: NAME=SRV~VALUE”。
Ø rewrite: 使用服务端标识覆盖后端设置的用于session sticky的cookie。如果后端服务在响应头中没有设置该cookie,则认为该请求不需要进行session sticky,使用这种模式,后端服务可以控制哪些请求需要sesstion sticky,哪些请求不需要。
参考文档:

http://tengine.taobao.org/document_cn/http_upstream_session_sticky_cn.html
配置示例:
在这里插入图片描述
配置

   upstream mynginx{
    !!   session_sticky cookie=uid fallback=on path=/ mode=insert option=indirect;
       server  192.168.221.132:8080;
       server  192.168.221.133:8080;

       check interval=3000 rise=2 fall=5 timeout=1000 type=http;
       check_http_send "HEAD / HTTP/1.0\r\n\r\n";
       check_http_expect_alive http_2xx http_3xx;
    }
    server {
        listen       80;
        server_name  www.nginx1.cn;

        location / {
      !!      session_sticky_hide_cookie upstream=mynginx;
            proxy_pass http://mynginx;
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        location /ngx_status{
          check_status;
         }
    }
#insert + indirect模式:
upstream test {
    session_sticky cookie=uid domain=www.xxx.com fallback=on path=/ mode=insert option=indirect;
    server  127.0.0.1:8080;
}
server {
    location / {
        #在insert + indirect模式或者prefix模式下需要配置session_sticky_hide_cookie
        #这种模式不会将保持会话使用的cookie传给后端服务,让保持会话的cookie对后端透明
        session_sticky_hide_cookie upstream=test;
        proxy_pass http://test;
    }
}

测试:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值