多级缓存实现亿级流量

 

目录

为什么要用多级缓存?

JVM进行缓存

 进程缓存:

实现商品的查询的本地进程缓存

Lua语法

 Lua中的数据类型

 lua语法+函数

 lua条件控制

 案例:自定义函数,打印table,条件参数为nil打印提示信息

 多级缓存实现

实践测试

 请求参数处理

 案例返回商品数据:

 我们的nginx怎么去请求到tomcat中呢?

我们的请求处理(service)需要编写一个lua脚本在openresty下的lualib中

common.lua获取nginx.conf的http请求作出处理响应 

 Tomcat集群的负载均衡

添加redis缓存的需求

 查询Redis缓存

在openresty中添加一个本地缓存

 案例:优先openresty本地缓存


为什么要用多级缓存?

首先我们来说说传统缓存的问题

1.用户请求达到tomcat后,先查询redis再到数据库,但是tomcat的连接是<redis的,所以并发度较低,那么就会造成一个Tomcat的性能成为整个系统的瓶颈

2.Redis缓存失效后,会直接对数据库进行冲击(也就是缓存击穿现象:没有并且对着没有的地方进行冲击)

nginx跟tomcat差不多,除了做反向代理之外还能自己编写业务->这里作为缓存;

1.我们的浏览器作为客户端的缓存,比如静态资源,当访问相同的静态资源时做出响应;

2.nginx:缓存我们的动态数据,在请求还没有到底tomcat时候,如果nginx中缓存了该数据就响应,如果没查到就去redis查,之前是在tomcat后查,现在是在nginx后查;

3.进程缓存:在redis缓存没有被命中的时候->到达tomcat,在服务器内部利用类似map保存数据,如果命中就返回;

好处:

1.这样层层缓存到达tomcat的请求就会减少,而Tomcat的性能就不会成为系统的瓶颈,我们之前的层层剥削也就是为了解决因为tomcat而导致并发量较低的情况——>所以说,我们在一定程度上还提高了并发量

2.减少对于数据库的冲击,在一定程度上防止了缓冲穿透

细节:

需要实现JVM进程缓存与Lua语法+缓存同步策略

JVM进行缓存

docker run \
 -p 3306:3306 \
 --name mysql \
 -v $PWD/conf:/etc/mysql/conf.d \
 -v $PWD/logs:/logs \
 -v $PWD/data:/var/lib/mysql \
 -e MYSQL_ROOT_PASSWORD=123 \
 --privileged \
 -d \
 mysql:5.7.25

为什么要分库分表?

比如字段较多时候,例如商品,你商品库存是经常发生变动的,那么你的缓存就会经常更新,导致未命中概率增高;

1.然后我们导入工程访问数据

我们的目的是利用nginx反向代理向后面的本地缓存中查询数据然后渲染到客户端保存的页面中

 2.然后我们执行nginx,注意配置文件nginx,访问数据,发现nginx代理请求到本地

start nginx.exe

访问localhost/item.html?id=10001

 然后我们看看后台,发现前台请求了一个ajax请求,并且这是一个nginx代理请求,发送到我们的nginx缓存中

 看看nginx的配置文件,帮助我们的请求接口做了一个负载均衡(nginx-cluster)

如果我们这个本地缓存nginx有多个,那么我们上面负载均衡就配置多个端口 

 进程缓存:

 像我们这种进程缓存,他只是针对本地这台机子上的JVM缓存,不同机子是访问不了的,而且数据量不能太大,不然项目启动会出问题

 1.用Caffenine

 Caffenine的API

 @Test
    void test01(){
        //1创建缓存对象
        Cache<String, String> cache = Caffeine.newBuilder().build();
        cache.put("Curry","30");
        cache.put("FOX","5");

        //2取数据,如果不存在则返回null
        String fox = cache.getIfPresent("FOX");
        System.out.println("name="+fox);

        String deFaultName = cache.get("deFaultName", key -> {
            return "你最喜欢的是库里";
        });
        System.out.println("defaultName="+deFaultName);

    }

 设置缓存策略

 /*
     基于大小设置驱逐策略:
     */
    @Test
    void testEvictByNum() throws InterruptedException {
        // 创建缓存对象
        Cache<String, String> cache = Caffeine.newBuilder()
                // 设置缓存大小上限为 1
                .maximumSize(1)
                .build();
        // 存数据
        cache.put("gf1", "柳岩");
        cache.put("gf2", "范冰冰");
        cache.put("gf3", "迪丽热巴");
        // 延迟10ms,给清理线程一点时间
        Thread.sleep(10L);
        // 获取数据
        System.out.println("gf1: " + cache.getIfPresent("gf1"));
        System.out.println("gf2: " + cache.getIfPresent("gf2"));
        System.out.println("gf3: " + cache.getIfPresent("gf3"));
    }

因为速度比较快,还没来的及驱逐完毕 

 基于时间的驱逐策略

 /*
     基于时间设置驱逐策略:
     */
    @Test
    void testEvictByTime() throws InterruptedException {
        // 创建缓存对象
        Cache<String, String> cache = Caffeine.newBuilder()
                .expireAfterWrite(Duration.ofSeconds(1)) // 设置缓存有效期为 10 秒
                .build();
        // 存数据
        cache.put("gf", "柳岩");
        // 获取数据
        System.out.println("gf: " + cache.getIfPresent("gf"));
        // 休眠一会儿
        Thread.sleep(1200L);
        System.out.println("gf: " + cache.getIfPresent("gf"));
    }

 

 我们也可以将我们的缓存自定义,然后对外暴露给其他人使用

实现商品的查询的本地进程缓存

1.先配置一个缓存配置类

package com.heima.item.config;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.heima.item.pojo.Item;
import com.heima.item.pojo.ItemStock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author diao 2022/6/16
 */
@Configuration
public class CaffeineConfig {


    /**
     * 定义缓存
     * @return
     */
    @Bean
    public Cache<Long, Item>itemCache(){
        return Caffeine.newBuilder()
                .initialCapacity(100)
                .maximumSize(10_000)
                .build();
    }

    @Bean
    public Cache<Long, ItemStock>stockCache(){
        return Caffeine.newBuilder()
                .initialCapacity(100)
                .maximumSize(10_000)
                .build();
    }

}

 2.控制层对于本地缓存的实现

  @GetMapping("/{id}")
    public Item findById(@PathVariable("id") Long id) {
        itemCache.get(id, key -> {
            itemService.query().
                    ne("status", 3).eq("id", key)
                    .one();
            return itemService.query().ne("status",3)
                    .eq("id",id)
                    .one();
        });

        return itemService.query()
                .ne("status", 3).eq("id", id)
                .one();
    }

  @GetMapping("/stock/{id}")
    public ItemStock findStockById(@PathVariable("id") Long id) {
      //先根据id来查本地缓存,如果没有命中->再根据数据库来查

        return stockCache.get(id,key->
            stockService.getById(key)
        );
    }

第一次会走数据库,以后都是走本地缓存

Lua语法

 

 测试:

 

 Lua中的数据类型

 直接输入lua进入lua的控制台

 lua声明变量,定义数组+map集合并且打印里面的值(注意lua中数组下标是没有0这个概念的)

 我们可以通过解析数组来得到数组中的所有值

数组下标对应着键,value为其值,数组为ipairs,table为pairs

 

 lua语法+函数

没有大括号直接返回

 

 lua条件控制

then代表大括号开始,end代表结束

if()里面默认代表判断是否为nil

 案例:自定义函数,打印table,条件参数为nil打印提示信息


 多级缓存实现

初识OpenResty

OpenResty® - Official Site

菜鸟

OpenResty 使用介绍 | 菜鸟教程 (runoob.com)

配置完后,openresty文件默认是在/user/local/下

lualib里面都是第三方模块:redis、mysql之类的

我们可以发现openresty是基于Nginx,除了nginx的执行文件其他都有

我们进入openresty的bin目录发现可执行文件就是nginx的可执行文件

利用了一个软引用,所以直接启动nginx下的执行文件也是可以的

 所以我们这里还需要配置nginx的环境变量

然后利用source命令在当前环境下读取配置文件

然后nginx启动,先进入openresty中nginx下的配置目录将配置修改

 然后启动nginx

实践测试

我们反向代理的最终地址就是openresty集群中的一个地址 (openresty的业务集群)

所以我们需要openresty接收请求

这里我们也明确openresty的用意:就是为了实现本地缓存版本的nginx

1.我们在nginx配置下添加对/api/item监听当访问这个负载均衡接口,响应一个lua/item.lua的文件,响应类型为json数据

添加两个加载模块+路径监听以及配置

 然后我们在lua写业务(类似service)

 加载openresty的两个模块:扩展nginx的作用,并且location根据接口路径响应lua内容(这些在openresty中nginx.conf中进行配置)

#user  nobody;
worker_processes  1;
error_log  logs/error.log;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

   #lua 模块
   lua_package_path "/usr/local/openresty/lualib/?.lua;;";
   #c模块     
   lua_package_cpath "/usr/local/openresty/lualib/?.so;;";  

    server {
        listen       8081;
        server_name  localhost;

        location /api/item {
	# 默认的响应类型
	  default_type application/json;
        # 响应结果由lua/item.lua文件决定
          content_by_lua_file lua/item.lua;  
	}

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

 lua会去nginx所在目录下找,创建item.lua即可

然后nginx -s reload再次请求成功——>说明我们的数据是由openresty给的

而这个openresty相当于nginx的缓存版本

 请求参数处理

我们的openresty返回的数据不能是假数据

1.~就是正则表达式的一个匹配,我们的正则表达式的元素值会被储存到数组中,我们可以利用数组获取里面的元素

2.Get、Post、JSON的参数值可以直接从uri,表单,body中获取

 案例返回商品数据:

1.先修改openresty下nginx的配置文件,将接口+占位符

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

   #lua 模块
   lua_package_path "/usr/local/openresty/lualib/?.lua;;";
   #c模块     
   lua_package_cpath "/usr/local/openresty/lualib/?.so;;";  

    server {
        listen       8081;
        server_name  localhost;

        location ~/api/item/(\d+) {
	# 默认的响应类型
	  default_type application/json;
        # 响应结果由lua/item.lua文件决定
          content_by_lua_file lua/item.lua;  
	}

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

2.修改nginx下lua下的lua文件,然后获取占位符内容

local id=ngx.var[1]

ngx.say('"id":'..id..',"name":"SALSA","title":"RIMOWA 21寸托运箱拉杆箱 SALSA AIR系列果绿色
 820.70.36.4","price":"999","image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp","category":"拉杆箱","brand":"RIMOWARIMOWA","spec":"{"颜色": "红色", "尺码": "26寸"}","status":1')

查询Tomcat

我们先去除中间的redis部分,openresty直接访问tomcat

 我们的nginx怎么去请求到tomcat中呢?

 这里需要用到反向代理,nginx内部提供.location.capture()的api,可以将我们的目标请求进行响应,但是这里只是一个nginx内部的server监听并且监听,那我们要请求到Tomcat服务器,就要编写一个server对这个路径进行反向代理,我们这里代理的也应该就是controller下的请求;

我们响应的内容就是里面这个body

resp.body:响应体,响应数据

 我们最后这个proxy_pass就是服务tomcat的ip

我们的请求处理(service)需要编写一个lua脚本在openresty下的lualib中

我们回顾一下之前在openresty中nginx的conf配置中是不是配置了加载lua模块与c模块,意思就是配置目录下的.lua都会被加载

所以我们这里下面common.lua一定会被加载 

common.lua获取nginx.conf的http请求作出处理响应 

local function read_http(path, params)
    local resp = ngx.location.capture(path,{
        method = ngx.HTTP_GET,
        args = params,
    })
    if not resp then
        -- 记录错误信息,返回404
        ngx.log(ngx.ERR, "http not found, path: ", path , ", args: ", args)
        ngx.exit(404)
    end
    return resp.body
end
-- 将方法导出
local _M = {  
    read_http = read_http
}  
return _M

细节处理:

之前我们的请求/api/item不是会路由到指定的lua上吗,我们lua返回json数据,之前我们item.lua中是假数据,需要实现

导入common.lua函数库,查询商品+库存信息

 完成openresty目录下nginx中item.lua缓存数据获取的配置

一个商品信息一个库存信息,信息从common.lua中获取(里面放入请求的数据和信息)

item.lua的编写,目的就是将缓存数据进行返回->通过过read_http,而这个方法又是common.lua中的,会读取请求路径

我们的common.lua中配置了请求路径,然后反向代理到我们的tomcat服务器,最后返回body数据

local common = require('common')
local read_http = common.read_http
 
local id = ngx.var[1]

local itemJSON = read_http("/item/" .. id,nil)

local stockJSON = read_http("/item/stock/" .. id,nil)

ngx.say(itemJSON)

 JSON结果处理

 Tomcat集群的负载均衡

 当有多台tomcat时,我们本地缓存openresty,也就是nginx对其进行访问,可能会出现在一台服务器上有缓存,而在另外一台服务器上没缓存的情况

两种思路:1.我认为是可以搭建一个redis的,我们的tomcat请求的数据存入redis中,然后openresty封装的本地缓存从redis中取,相当于说redis就是一个大杂烩,连接着本地缓存与进程缓存;

2.我们可以利用一个hash算法,也就是说,根据我们的请求路径,进行取模运算得到请求到哪一个服务器上,不会请求到其他服务器,这样就保证了缓存获取的一个作用,可以一直命中

 openresty下nginx配置:

#user  nobody;
worker_processes  1;
error_log  logs/error.log;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

   #lua 模块
   lua_package_path "/usr/local/openresty/lualib/?.lua;;";
   #c模块     
   lua_package_cpath "/usr/local/openresty/lualib/?.so;;";  

   #搭建一个tomcat集群
   upstream tomcat-cluster{
     hash $request_uri;
     server 192.168.184.1:8081;
     server 192.168.184.1:8082;
   }

   server {
        listen       8081;
        server_name  localhost;
        location /item{
          proxy_pass http://tomcat-cluster;
        } 

        location ~/api/item/(\d+) {
	# 默认的响应类型
	  default_type application/json;
        # 响应结果由lua/item.lua文件决定
          content_by_lua_file lua/item.lua;  
	}

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

配置好tomcat集群后重启openresty下nginx,进行访问,发现缓存生效


添加redis缓存的需求

 缓存预热主要防止缓存击穿,过期热点数据访问

1.启动redis容器

docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes

2.实现项目一启动就进行缓存初始化

每次初始化都会开启redis缓存 

package com.heima.item.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.heima.item.pojo.Item;
import com.heima.item.pojo.ItemStock;
import com.heima.item.service.IItemService;
import com.heima.item.service.IItemStockService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author diao 2022/6/17
 */
@Component
public class RedisHandler implements InitializingBean {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private IItemService itemService;
    @Autowired
    private IItemStockService stockService;

    private static final ObjectMapper MAPPER=new ObjectMapper();

    @Override
    public void afterPropertiesSet() throws Exception {
        /**
         * 1.查询商品详情信息
         */
        List<Item> itemList = itemService.list();
        //放入缓存
        for (Item item : itemList) {
            String json = MAPPER.writeValueAsString(item);
            redisTemplate.opsForValue().set("item:id:"+item.getId(),json);
        }

        /**
         * 2.查询商品库存信息
         */
        List<ItemStock> stockList = stockService.list();
        //放入缓存中
        for (ItemStock stock : stockList) {
            String json = MAPPER.writeValueAsString(stock);
            redisTemplate.opsForValue().set("item:stock:id:"+stock.getId(),json);
        }
    }
}

 

3.启动docker exec ...打开redis客户端

 docker exec -it redis redis-cli

 然后redis-cli客户端即可操作

发现数据全部都在缓存中

 查询Redis缓存

目的:openresty优先查询Redis缓存中数据再访问tomcat

  openresty中的Redis模块:

我们需要再common.lua中引入redis模块

1.引入Redis模块,创建Redis对象;

2.封装函数用于释放Redis连接,将Redis放入连接池

common.lua总配置 

还需要暴露函数,供给itema.lua获取 

 操作模块,从redis读取数据返回——>itema.lua

然后我们去/lua/itema.lua中修改配置,将读取数据的方式修改为优先redis读取后tomcat本地缓存读取

-- 导入common函数库
local common = require('common')
local read_http = common.read_http
local read_redis = common.read_redis

-- 导入cjson函数库
local cjjson = require('cjson')

-- 封装查询函数
function read_data(key,path,params)
  local resp = read_redis("127.0.0.1",6379,key)
  if not resp then
     ngx.log("redis查询失败,尝试查询http,key:",key)
     resp = read_http(path,params)
  end
  return resp
end

local id = ngx.var[1]

local itemJSON = read_http("item:id:" .. id,"/item/" .. id,nil)

local stockJSON = read_http("item:stock:id:" .. id,"/item/stock/" .. id,nil)

local item = cjson.decode(itemJSON)
local stock = cjson.decode(stockJSON)
item.stock = stock.stock
item.sold = stock.sold

ngx.say(cjson.encode(item))

在openresty中添加一个本地缓存

在itema.lua中导入本地缓存 

 

然后我们的查询函数需要更改:先本地缓存查询再redis再Tomcat 

 案例:优先openresty本地缓存

common.lua配置

local redis = require('resty.redis')
local red = redis:new()
red:set_timeouts(1000,1000,1000)

local function close_redis(red)
    local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    if not ok then
        ngx.log(ngx.ERR, "放入redis连接池失败: ", err)
    end
end

local function read_redis(ip, port, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
    end
    --得到的数据为空处理
    if resp == ngx.null then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
    end
    close_redis(red)
    return resp
end

local function read_http(path, params)
    local resp = ngx.location.capture(path,{
        method = ngx.HTTP_GET,
        args = params,
    })
    if not resp then
       
        ngx.log(ngx.ERR, "http not found, path: ", path , ", args: ", args)
        ngx.exit(404)
    end
    return resp.body
end

local _M = {  
    read_http = read_http
    read_redis = read_redis
}  
return _M

 itema.lua配置

-- 导入common函数库
local common = require('common')
local read_http = common.read_http
local read_redis = common.read_redis

-- 导入cjson函数库
local cjjson = require('cjson')
-- 导入本地缓存共享词库
local item_cache = ngx.shared.item_cache

-- 封装查询函数
function read_data(key,expire,path,params)
  local val = item_cahce:get(key)
  if not val then
    ngx.log(ngx.ERR,"本地缓存查询失败,尝试redis,key:",key)

    val = read_redis("127.0.0.1",6379,key)
  if not val then
     ngx.log("redis查询失败,尝试查询http,key:",key)
     val = read_http(path,params)
  end
  return val
end
 
local id = ngx.var[1]

-- 查询商品信息
local itemJSON = read_http("item:id:" .. id,1800,"/item/" .. id,nil)

local stockJSON = read_http("item:stock:id:" .. id,60,"/item/stock/" .. id,nil)

local item = cjson.decode(itemJSON)
local stock = cjson.decode(stockJSON)
item.stock = stock.stock
item.sold = stock.sold

ngx.say(cjson.encode(item))

nginx.conf配置

#user  nobody;
worker_processes  1;
error_log  logs/error.log;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

   #lua 模块
   lua_package_path "/usr/local/openresty/lualib/?.lua;;";
   #c模块     
   lua_package_cpath "/usr/local/openresty/lualib/?.so;;";  
   #添加共享词典,本地缓存
   lua_shared_dict item_cache 150m; 

   #搭建一个tomcat集群
   upstream tomcat-cluster{
     hash $request_uri;
     server 192.168.184.1:8081;
     server 192.168.184.1:8082;
   }

   server {
        listen       8081;
        server_name  localhost;
        location /item{
          proxy_pass http://tomcat-cluster;
        } 

        location ~/api/item/(\d+) {
	# 默认的响应类型
	  default_type application/json;
        # 响应结果由lua/item.lua文件决定
          content_by_lua_file lua/item.lua;  
	}

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

 

开日志进行查看

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
网新中英文企业手机电脑一体化建站高级版 v4.8 更新日志 客服中心增加MSN链接客服功能 欢迎使用网新中英文响应式企业手机、电脑建站系统,解压后请将整个程序所有文件复制到本地电脑IIS根目录下或者上传到虚拟主机根目录下即可运行。 温馨提示:“本程序以IE高版本内核开发,后台环境不支持IE9.0及以下版本,请使用IE10.0以上版本浏览器或兼容火狐、谷歌、猎豹等浏览器,推荐使用兼容火狐浏览器登录后台管理, 请勿使用aspweb、小旋风或NETBOX等IIS测试工具进行本地电脑测试,否则本程序将无法正常运行。” 后台管理地址: 网新中英文企业手机电脑一体化建站标准版程序简介 网新中英文响应式企业手机、电脑建站系统是专业为个人和企业网站建设而开发的一款智能化程序。该程序基于ASP ACCESS环境开发,拥有完善的网站前台和后台全智能化管理功能,完全由后台操作(如添加、修改网站基本信息、产品、企业新闻动态等)静态生成前台界面HTML格式网页文件, 是个人和企业智能化网站建设首选! 网新中英文企业手机电脑一体化建站标准版程序十大特色功能: 1、程序全站静态:全站生成.html格式静态网页文件,减少服务器运行压力,网页执行速度快,方便给百度、谷歌等搜索引擎收录网页,为您的企业带来潜在客户; 2、程序智能化SEO设计:特别针对百度、谷歌等搜索引擎进行关键字优化设计,让客户快速找到您的网站; 3、程序操作简单:拥有完善的网站前台和后台全智能化管理功能,所有网站信息均可在后台进行更新,非专业人士也能对网站进行轻松操作管理; 4、程序采用中英繁三语设计,方便客户在前台自由选择所需语言浏览您的网站; 5、程序内置红、蓝、绿3套模版,可以自由在后台选择喜欢的模版切换到前台展示给您的客户看,满足您的个性化需求; 6、程序采用ASP ACCESS环境开发,不限域名绑定,完全开放源代码,未任何加密,网页文件可以个性化修改; 7、程序采用UTF-8编码,全球打开网站不乱码; 8、程序后台使用KEditor国际流行编辑器,Word格式自由排版; 9、程序集成最新搜索引擎蜘蛛来访记录系统,更好了解搜索引擎蜘蛛来访情况; 10、网站导航等栏目及子栏目实现自主管理,可自由添加、修改、删除栏目。 安全建议 为确保您的网站安全,请修改以下默认设置: 1、更改默认的后台用户名和密码admin; 2、更改默认的后台管理文件夹名Myadmin,直接修改根目录下的文件夹名称即可; 3、更改默认的数据库文件夹Data21293及数据库名称,请确保inc文件夹下的数据库连接文件conn.asp内也作相应修改。 网新中英文企业手机电脑一体化建站标准版程序功能 1、企业简介功能,自由添加、修改、删除企业简介及联系方式等功能; 2、新闻发布功能,自由添加、修改、删除企业新闻、行业动态等功能; 3、产品分类及产品发布功能,全新开发产品数据批量修改接口,自由添加、修改、删除企业产品分类及产品功能; 4、图片展示功能,自由添加、修改、删除企业产品、案例展示及公司荣誉图片等功能; 5、访客留言功能,自由修改、删除企业客户前台留言审核回复功能; 6、在线客服系统,自由添加、修改、删除QQ、MSN、Skype及旺旺等在线沟通方式; 7、友情链接,自由添加、修改、删除企业友情链接功能; 8、导航栏管理,自由添加、修改、删除导航栏目及子导航栏目; 9、栏目管理,自由添加、修改、删除产品分类、新闻分类等任意栏目及子栏目; 10、SEO设置,前台每个页面均可在后台进行自定义title,keywords,description等SEO优化功能; 11、具备传统企业网站基本、高级用户交互功能(公司介绍、新闻、产品、案例、下载、营销网络、客户留言、人才、订单、会员、站内搜索等); 12、提供公司Google地图设置接口,可在后台进行公司方位标注,在前台同步显示您所设置的公司方位地图; 13、多项为系统优化而设置的功能接口,如站内链接(站内链接的合理建造是搜索引擎优化的重要技术之一,它的优化能使网站整体获得搜索引擎的价值认可, 这个优化措施主要是建立方便、直接、全面的浏览导航链接,使每一页有次序地首尾相接。生成谷歌Google SiteMap、生成百度XML、智能生成静态Html页面、 自动生成拼音形式文件名、自定义生成静态页面文件名、自定义静态页面存放目录、站内流量统计接口、搜索引擎登录接口; 14、独有精心设计的前台产品展示模块,可方便地在后台进行开启、关闭子功能;后台采用产品属性管理接口,使产品添加、管理更加智能化;    15、企业广告宣传幻灯片后台管理接口,并提供多个Flash幻灯片管理参数设置接口;    16、健壮的内核设

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fairy要carry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值