【云商城】高性能门户网构建
第3章 高性能门户网构建
网站门户就是首页
1.OpenResty 百万并发站点架构
1).OpenResty 特性介绍
2).搭建OpenResty
3).Web站点动静分离方案剖析
2.Lua语法学习
1).Lua基本语法
3.多级缓存架构实战
1).多级缓存架构分析
用户请求网站,最开始经过代理层nginx,经过tomcat,最后才到我们的java项目
2).Lua操作Redis实战
3).首页推广产品异步高效加载实战
4.Nginx代理缓存
1).Nginx代理缓存学习
2).Nginx代理缓存热点数据应用
3).Cache_Purge代理缓存清理
5.缓存一致性
1).Canal原理讲解
2).Canal安装
3).多级缓存架构缓存一致性实战
1 OpenResty高性能Web站点架构
http://openresty.org/en/
http://openresty.org/cn/
OpenResty® 是一款基于 NGINX 和 LuaJIT 的 Web 平台
1.1 OpenResty简介
OpenResty 是一个基于 Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关
OpenResty® 通过汇聚各种设计精良的 Nginx模块(主要由 OpenResty 团队自主开发),从而将 Nginx有效地变成一个强大的通用 Web 应用平台
OpenResty® 的目标是让你的Web服务直接跑在 Nginx服务内部,充分利用 Nginx的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL 以及 Redis 等都进行一致的高性能响应
区别:
Nginx:并发能力强、稳定、消耗资源小
Lua:所有脚本语言中性能最好的
1.2 OpenResty搭建
关于OpenResty的搭建,可以参考官方提供的网址进行搭建。http://openresty.org/cn/installation.html,我们采用源码安装的方式进行安装。
官方提供了源码安装的方式:http://openresty.org/cn/linux-packages.html
安装OpenResty:
1)安装依赖库:
yum install libtermcap-devel ncurses-devel libevent-devel readline-devel pcre-devel gcc openssl openssl-devel per perl wget
2)下载安装包:
wget https://openresty.org/download/openresty-1.11.2.5.tar.gz
3)解压安装包
tar -xf openresty-1.11.2.5.tar.gz
4)进入安装包,并安装
#进入安装包
cd openresty-1.11.2.5#安装
./configure --prefix=/usr/local/openresty --with-luajit --without-http_redis2_module --with-http_stub_status_module --with-http_v2_module --with-http_gzip_static_module --with-http_sub_module --add-module=/usr/local/gupao/ngx_cache_purge-2.3/#编译并安装
make && make install
说明:
--prefix=/usr/local/openresty:安装路径--with-luajit:安装luajit相关库,luajit是lua的一个高效版,LuaJIT的运行速度比标准Lua快数十倍。--without-http_redis2_module:现在使用的Redis都是3.x以上版本,这里不推荐使用Redis2,表示不安装redis2支持的lua库--with-http_stub_status_module:Http对应状态的库--with-http_v2_module:对Http2的支持--with-http_gzip_static_module:gzip服务端压缩支持--with-http_sub_module:过滤器,可以通过将一个指定的字符串替换为另一个字符串来修改响应--add-module=/usr/local/gupao/ngx_cache_purge-2.3/:Nginx代理缓存清理工具
关于每个模块的具体作用,大家可以参考腾讯云的开发者手册:https://cloud.tencent.com/developer/doc/1158
如下图安装完成后,在/usr/local/openrestry/nginx
目录下是安装好的nginx,以后我们将在该目录的nginx下实现网站发布
5)配置环境变量:
vi /etc/profileexport PATH=/usr/local/openresty/nginx/sbin:$PATHsource /etc/profile
6)开机启动:
linux系统结构/lib/systemd/system/
目录,该目录自动存放启动文件的配置位置,里面一般包含有xxx.service
,例如systemctl enable nginx.service
,就是调用 /lib/systemd/system/nginx.service
文件,使nginx开机启动。
我们可以创建/usr/lib/systemd/system/nginx.service
,在该文件中编写启动nginx脚本:
[Service]
Type=forking
PIDFile=/usr/local/openresty/nginx/logs/nginx.pid
ExecStartPre=/usr/local/openresty/nginx/sbin/nginx -t
ExecStart=/usr/local/openresty/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true[Install]
WantedBy=multi-user.target
执行systemctl daemon-reload
:重新加载某个服务的配置文件
执行systemctl enable nginx.service
:开机启动
执行systemctl start nginx.service
:启动nginx
访问http://192.168.100.130/
,效果如下:
1.3 动静分离站点架构
1.3.1 动静分离架构分析
我们打开京东商城,搜索手机,查看网络可以发现响应页面后,页面又会发起很多请求,还没有查看多少信息就已经有393个请求发出了,而多数都是图片,一个人请求如此,人多了对后端造成的压力是非比寻常的,该如何降低静态资源对服务器的压力呢?
如下图:
项目完成后,项目上线如果所有请求都经过Tomcat,并发量很大的时候,对项目而言将是灭顶之灾,电商项目中一个请求返回的页面往往会再次发起很多请求,而绝大多数都是图片或者是css样式、js等静态资源,如果这些静态资源都去查询Tomcat,Tomcat的压力会增加数十倍甚至更高,这时候我们需要采用动静分离的策略:
1.所有静态资源,经过Nginx,Nginx直接从指定磁盘中获取文件,然后IO输出给用户
2.如果是需要查询数据库数据的请求,就路由到Tomcat集群中,让Tomcat处理,并将结果响应给用户
1.3.2 门户静态站点发布
门户front:
点击index.html,就是一个商城的首页
修改本地文件C:\Windows\System32\drivers\etc\HOSTS
文件,将案例演示域名www.gpshopvip.com
解析到192.168.100.130
服务器,在HOSTS文件中添加如下配置即可:
192.168.100.130 www.gpshopvip.com
将front上传到/usr/local/gupao/web/static
目录下,再修改/usr/local/openresty/nginx/conf/nginx.conf
,配置如下:
用户请求www.gpshopvip.com这个网站下的所有路径,直接跳转到/usr/local/gupao/web/static/frant的文件
#门户发布
server { //虚拟机listen 80;server_name www.gpshopvip.com;location / {root /usr/local/gupao/web/static/frant;}
}
访问http://www.gpshopvip.com/
效果如下:
2 Lua语法学习
Lua 是一个小巧的脚本语言, 其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。
Lua特性:
1.一个小巧的脚本语言
2.设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能
3.所有操作系统和平台上都可以编译、运行Lua脚本
4.所有脚本引擎中,Lua的速度是最快的
应用场景:
1.游戏开发
2.独立应用脚本
3.高性能Web应用(天猫、京东都有应用)
4.扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
2.1 Lua常用语法
2.1.1 Lua安装
首先我们准备一个linux虚拟机来安装Lua,在linux系统中按照如下步骤进行安装:
curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
tar xf lua-5.3.5.tar.gz
cd lua-5.3.5
make linux test
出现如下界面,表示安装成功:
版本查看:lua -v
我们可以发现,Lua版本还是原来系统自带的版本,我们需要替换原来系统自带的lua,执行如下命令:
rm -rf /usr/bin/lua
ln -s /usr/local/gupao/lua-5.3.5/src/lua /usr/bin/lua
此时版本信息如下:
2.1.2 Lua常用操作
Lua 提供了交互式编程模式。我们可以在命令行中输入程序并立即查看效果,这种编程模式类似我们控制台操作,Lua 交互式编程模式可以通过命令 lua -i 或 lua 来启用:
[root@server1 lua-5.3.5]# lua -i
Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
>
1)打印
print("springcloud alibaba")
2)数据类型
类型测试:
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string
3)变量
变量在使用前,需要在代码中进行声明,即创建该变量。Lua 变量有三种类型:全局变量、局部变量、表中的域
全局变量定义:
> age=19
> print(age)
19
局部变量定义:
> local username=wangwu
> print(username)
nil
此时username不是全局变量,一般在某个方法中使用,不能全局使用,所以输出nil
4)对象(table)
> --定义对象resp
> resp = {}
> --往对象resp中添加属性name,赋值为zhangsan
> resp["name"]="zhangsan"
> --往对象resp中添加属性address,赋值为hunanchangsha
> resp["address"]="hunanchangsha"
> --输出对象resp中的name属性值
> print(resp["name"])
zhangsan
5)函数
创建一个函数,其实就是创建一个方法,函数以function开始,end结束,可以在end之前有返回值,也可以有入参,定义一个方法如下:
> --定义userinfo方法,入参为age
> function userinfo(age)
>> --age在原有基础上+1
>> age=age+1
>> --返回变化后的age
>> return age
>> --结束
>> end
> print(userinfo(19))
20
6)拼接
在上面方法调用上拼接一段字符串,可以使用亮点来做…,如下:
> print(userinfo(19).."岁了")
20岁了
7)逻辑判断
我们经常会做一些条件判断,在lua中也可以实现,lua中有if xx then else end
的流程判断语法。
> function userinfo(age)
>> if age>=18 then
>> return "成年人"
>> else
>> return "未成年"
>> end
>> end
> print(userinfo(17))
未成年
8)脚本编程
我们可以像写java一样,将lua脚本写到一个文件中,并且可以在一个脚本文件引入另外一个脚本文件,类似java中的导包。
创建course.lua
,代码如下:
--定义一个对象
local course = {}--定义一个方法
function course.courseName(id)if id==1 thenreturn "java"elsereturn "UI"end
end
return course
创建student.lua
,代码如下:
--导入course.lua
local cr = require("course")--调用courseName方法
local result = cr.courseName(1)print(result)
执行student.lua
:
[root@server1 lua]# lua student.lua
java
3 多级缓存架构实战
项目运行过程中往往为了提升项目对数据加载效率,一般都会增加缓存,但缓存如何加载效率最高?如何加载对后端服务造成的压力最小?我们需要设计一套完善的缓存架构体系
3.1 多级缓存架构分析
用户请求到达后端服务,先经过代理层nginx,nginx将请求路由到后端tomcat服务,tomcat去数据库中取数据,这是一个非常普通的流程,但在大并发场景下,需要做优化,而缓存是最有效的手段之一。缓存优化有,执行过程如下:
1:请求到达Nginx,Nginx抗压能力极强
2:Tomcat抗压能力很弱,如果直接将所有请求路由给Tomcat,Tomcat压力会非常大,很有可能宕机。我们可以在Nginx这里设置2道缓存,第1道是Redis缓存,第2道是Nginx缓存(nginx自身也是有缓存cache的)
3:先加载Redis缓存,如果Redis没有缓存,则加载Nginx缓存,Nginx如果没有缓存,则将请求路由到Tomcat
4:Tomcat发布的程序会加载数据,加载完成后需要做缓存的,及时将数据存入Redis缓存,再响应数据给用户
5:用户下次查询的时候,查询Redis缓存或Nginx缓存
6:后面用户请求的时候,就可以直接从Nginx缓存拿数据了,这样就可以实现后端Tomcat发布的服务被调用的次数大幅减少,负载大幅下降
上面这套缓存架构被多个大厂应用,除了可以有效提高加载速度、降低后端服务负载之外,还可以防止缓存雪崩,为服务稳定健康打下了坚实的基础,这也就是鼎鼎有名的多级缓存架构体系
3.2 推广商品高效加载
首页很多商品优先推荐展示,这些其实都是推广商品,并非真正意义上的热门商品,首页展示这些商品数据需要加载效率极高,并且商城首页访问频率也是极高,我们需要对首页数据做缓存处理,我们首先想到的就是Redis缓存
3.2.1 表结构分析
推广商品并非只在首页出现,有可能在列表页、分类搜索页多个地方出现,因此可以设计一张表用于存放不同位置展示不同商品的表,推广产品推荐表如下:
CREATE TABLE `ad_items` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(100) DEFAULT NULL,`type` int(3) DEFAULT NULL COMMENT '分类,1首页推广,2列表页推广',`sku_id` varchar(60) DEFAULT NULL COMMENT '展示的产品(对应Sku)',`sort` int(11) DEFAULT NULL COMMENT '排序',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.2.2 推广商品异步加载
1)Bean创建
在goods-api
中创建com.gupaoedu.vip.mall.goods.model.AdItems
:
@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus表映射注解
@TableName(value = "ad_items")
public class AdItems {@TableId(type = IdType.AUTO)private Integer id;private String name;private Integer type;private String skuId;private Integer sort;
}
2)Mapper
创建com.gupaoedu.vip.mall.goods.mapper.AdItemsMapper
:
public interface AdItemsMapper extends BaseMapper<AdItems> {
}
3)Service
接口:修改com.gupaoedu.vip.mall.goods.service.SkuService
,增加如下方法:
public interface SkuService extends IService<Sku> {/**** 根据推广产品分类ID查询Sku列表*/List<Sku> typeSkuItems(Integer id);
}
实现类:修改com.gupaoedu.vip.mall.goods.service.impl.SkuServiceImpl
增加实现方法:
@Service
public class SkuServiceImpl extends ServiceImpl<SkuMapper,Sku> implements SkuService {@Autowiredprivate AdItemsMapper adItemsMapper;@Autowiredprivate SkuMapper skuMapper;/**** 根据推广产品分类ID查询Sku列表*/@Overridepublic List<Sku> typeSkuItems(Integer id) {//查询所有分类下的推广QueryWrapper<AdItems> adItemsQueryWrapper=new QueryWrapper<AdItems>();adItemsQueryWrapper.eq("type",id);List<AdItems> adItems = adItemsMapper.selectList(adItemsQueryWrapper);//获取所有SkuIdList<String> skuIds = adItems.stream().map(adItem -> adItem.getSkuId()).collect(Collectors.toList());//批量查询SkuList<Sku> skus = skuMapper.selectBatchIds(skuIds);return skus;}
}
4)Controller
修改com.gupaoedu.vip.mall.goods.controller.SkuController
增加方法:
@RestController
@RequestMapping(value = "/sku")
@CrossOrigin
public class SkuController {@Autowiredprivate SkuService skuService;/***** 指定分类下的推广产品列表*/@GetMapping(value = "/aditems/type/{id}")public List<Sku> typeItems(@PathVariable(value = "id")Integer id){//查询List<Sku> adSkuItems = skuService.typeSkuItems(id);return adSkuItems;}
}
用Postman测试http://localhost:8081/sku/aditems/type/1
效果如下:
3.2.3 缓存常用注解
先实现redis缓存加载(第一部分是右边redis)
@EnableCaching
:
开关性注解,在项目启动类或某个配置类上使用此注解后,则表示允许使用注解的方式进行缓存操作
@Cacheable
:
可用于类或方法上;在目标方法执行前,会根据key先去缓存中查询看是否有数据,有就直接返回缓存中的key对应的value值。不再执行目标方法;无则执行目标方法,并将方法的返回值作为value,并以键值对的形式存入缓存
@CacheEvict
:
可用于类或方法上;在执行完目标方法后,清除缓存中对应key的数据(如果缓存中有对应key的数据缓存的话)
@CachePut
:
可用于类或方法上;在执行完目标方法后,并将方法的返回值作为value,并以键值对的形式存入缓存中
@Caching
:
此注解即可作为@Cacheable、@CacheEvict、@CachePut三种注解中的的任何一种或几种来使用
@CacheConfig
:
可以用于配置@Cacheable、@CacheEvict、@CachePut这三个注解的一些公共属性,例如cacheNames、keyGenerator
注意:
@EnableCaching:如果方法有返回值就加到缓存中去,下次查询先去查询缓存,缓存有数据直接返回数据
@CachePut:每次查询就直接查询数据库,有数据就添加到缓存中
3.2.4 推广产品缓存操作
1)配置缓存链接
修改bootstrap.yml
,增加配置Redis缓存链接,如下:
server:port: 8081
spring:application:name: mall-goods #服务名datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/shop_goods?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: 123456cloud:nacos:config:file-extension: yamlserver-addr: 192.168.1.11:8848discovery:#Nacos的注册地址server-addr: 192.168.1.11:8848#Redis配置redis:host: 127.0.0.1port: 6379password: 123456
2)开启缓存
在com.gupaoedu.vip.mall.MallGoodsServiceApplication
上添加缓存开启注解:
package com.gupaoedu.vip.mall;/*** 商品服务启动类*/
@SpringBootApplication
@MapperScan(basePackages = {"com.gupaoedu.vip.mall.goods.mapper"})
@EnableCaching //开启缓存
public class MallGoodsApplication {public static void main(String[] args) {SpringApplication.run(MallGoodsApplication.class,args);}}
3.2.4.1 推广产品缓存加载
在com.gupaoedu.vip.mall.goods.service.impl.SkuServiceImpl
添加@Cacheable
注解,代码如下:
完整代码如下:
/**** 根据推广产品分类ID查询Sku列表* cacheNames = "ad-items-skus":当前缓存对应的以一级命名空间* key ="#id":入参id作为缓存的key,使用的是SpEL表达式*ad-items-skus::id*/
@Cacheable(cacheNames = "ad-items-skus",key ="#id")
@Override
public List<Sku> typeSkuItems(Integer id) {//查询所有分类下的推广QueryWrapper<AdItems> adItemsQueryWrapper=new QueryWrapper<AdItems>();adItemsQueryWrapper.eq("type",id);List<AdItems> adItems = adItemsMapper.selectList(adItemsQueryWrapper);//获取所有SkuIdList<String> skuIds = adItems.stream().map(adItem -> adItem.getSkuId()).collect(Collectors.toList());//批量查询SkuList<Sku> skus = skuMapper.selectBatchIds(skuIds);return skus;
}
注意:做缓存时,Bean实体类要序列化Serializable,不然会报错
package com.gupaoedu.vip.mall.goods.model;
@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus表映射注解
@TableName(value = "ad_items")
public class AdItems implements Serializable {@TableId(type = IdType.AUTO)private Integer id;private String name;private Integer type;private String skuId;private Integer sort;
}
请求http://localhost:8081/sku/aditems/type/1
此时Redis缓存数据如下:
我们可以发现上面存储的数据是二进制数据,我们很难阅读,而且占空间极大,我们可以使用FastJSON将每次存入到Redis中的数据转成JSON字符串,此时我们需要把RedisConfig.java
,其他工程也有可能需要,我们可以写到mall-service-dependency
工程的com.gupaoedu.vip.mall.config
包下
package com.gupaoedu.vip.mall.config;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(factory);GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();// 值采用json序列化redisTemplate.setValueSerializer(serializer);//使用StringRedisSerializer来序列化和反序列化redis的key值redisTemplate.setKeySerializer(new StringRedisSerializer());// 设置hash key 和value序列化模式redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(serializer);redisTemplate.afterPropertiesSet();return redisTemplate;}@Beanpublic RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(12))//设置默认缓存时间.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);}
}
此时清理再执行加载缓存后,效果如下:
3.2.4.2 推广产品缓存清理
1)Service
接口:添加清理缓存方法com.gupaoedu.vip.mall.goods.service.SkuService#delTypeSkuItems
/**** 清理分类ID下的推广产品*/
void delTypeSkuItems(Integer id);
实现类:添加实现方法com.gupaoedu.vip.mall.goods.service.impl.SkuServiceImpl#delTypeSkuItems
/***** 清理缓存* @param id*/
@CacheEvict(cacheNames = "ad-items-skus",key ="#id")
@Override
public void delTypeSkuItems(Integer id) {
}
2)Controller
添加删除缓存方法com.gupaoedu.vip.mall.goods.controller.SkuController#deleteTypeItems
/***** 删除指定分类下的推广产品列表*/
@DeleteMapping(value = "/aditems/type")
public RespResult deleteTypeItems(@RequestParam(value = "id") Integer id){//清理缓存skuService.delTypeSkuItems(id);return RespResult.ok();
}
3.2.4.3 注解缓存操作优化
使用@CacheConfig
优化注解,可以将cacheNames
挪到类上,每个方法上就不用重复写cacheNames
了。
其他地方肯定会调用这几个方法用于实现缓存更新,我们可以在goods-api
中添加feigin接口。
在mall-api
中引入common工具包和feign依赖包:
<!--工具包-->
<dependency><groupId>com.gupaoedu.vip.mall</groupId><artifactId>mall-common</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency><!--openfeign-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.5.RELEASE</version>
</dependency>
在goods-api
中创建com.gupaoedu.vip.mall.goods.feign.SkuFeign
,代码如下:
@FeignClient(value = "mall-goods")
public interface SkuFeign {/***** 指定分类下的推广产品列表*/@GetMapping(value = "/sku/aditems/type")public List<Sku> typeItems(@RequestParam(value = "id") Integer id);/***** 删除指定分类下的推广产品列表*/@DeleteMapping(value = "/sku/aditems/type/{id}")public RespResult deleteTypeItems(@PathVariable(value = "id")Integer id);/***** 修改指定分类下的推广产品列表*/@PutMapping(value = "/sku/aditems/type/{id}")public RespResult updateTypeItems(@PathVariable(value = "id")Integer id);
}
3.3 多级缓存-Lua+Redis
按照上面分析的架构,可以每次在Nginx的时候使用Lua脚本查询Redis,如果Redis有数据,则将数据存入到Nginx缓存,再将数据响应给用户,此时我们需要实现使用Lua将数据从Redis中加载出来。
我们在/usr/local/openresty/nginx/lua
中创建文件aditem.lua
,脚本如下:
--数据响应类型JSON
ngx.header.content_type="application/json;charset=utf8"
--Redis库依赖
local redis = require("resty.redis");
local cjson = require("cjson");--获取id参数(type)
local id = ngx.req.get_uri_args()["id"];
--key组装
local key = "ad-items-skus::"..id
--创建链接对象
local red = redis:new()
--设置超时时间
red:set_timeout(2000)
--设置服务器链接信息
red:connect("192.168.100.130", 6379)
--查询指定key的数据
local result=red:get(key);--关闭Redis链接
red:close()if result==nil or result==null or result==ngx.null thenreturn true
else--输出数据ngx.say(result)
end
修改nginx.conf
添加如下配置:(最后记得将content_by_lua_file改成rewrite_by_lua_file)
#推广产品查询
location /sku/aditems/type {content_by_lua_file /usr/local/openresty/nginx/lua/aditem.lua;
}
访问http://www.gpshopvip.com/sku/aditems/type?id=1
效果如下:
4 Nginx代理缓存
proxy_cache 是用于 proxy 模式的缓存功能,proxy_cache 在 Nginx 配置的 http 段、server 段中分别写入不同的配置。http 段中的配置用于定义 proxy_cache 空间,server 段中的配置用于调用 http 段中的定义,启用对server 的缓存功能。
使用:
1、定义缓存空间
2、在指定地方使用定义的缓存
4.1 Nginx代理缓存学习
1)开启Proxy_Cache缓存:
我们需要在nginx.conf中配置才能开启缓存:
proxy_cache_path /usr/local/openresty/nginx/cache levels=1:2 keys_zone=proxy_cache:10m max_size=1g inactive=60m use_temp_path=off;
参数说明:
【proxy_cache_path】指定缓存存储的路径,缓存存储在/usr/local/openresty/nginx/cache目录【levels=1:2】设置一个两级目录层次结构存储缓存,在单个目录中包含大量文件会降低文件访问速度,因此我们建议对大多数部署使用两级目录层次结构。如果 levels 未包含该参数,Nginx 会将所有文件放在同一目录中。【keys_zone=proxy_cache:10m】设置共享内存区域,用于存储缓存键和元数据,例如使用计时器。拥有内存中的密钥副本,Nginx 可以快速确定请求是否是一个 HIT 或 MISS 不必转到磁盘,从而大大加快了检查速度。1 MB 区域可以存储大约 8,000 个密钥的数据,因此示例中配置的 10 MB 区域可以存储大约 80,000 个密钥的数据。【max_size=1g】设置缓存大小的上限。它是可选的; 不指定值允许缓存增长以使用所有可用磁盘空间。当缓存大小达到限制时,一个称为缓存管理器的进程将删除最近最少使用的缓存,将大小恢复到限制之下的文件。【inactive=60m】指定项目在未被访问的情况下可以保留在缓存中的时间长度。在此示例中,缓存管理器进程会自动从缓存中删除 60 分钟未请求的文件,无论其是否已过期。默认值为 10 分钟(10m)。非活动内容与过期内容不同。Nginx 不会自动删除缓存 header 定义为已过期内容(例如 Cache-Control:max-age=120)。过期(陈旧)内容仅在指定时间内未被访问时被删除。访问过期内容时,Nginx 会从原始服务器刷新它并重置 inactive 计时器。【use_temp_path=off】表示NGINX会将临时文件保存在缓存数据的同一目录中。这是为了避免在更新缓存时,磁盘之间互相复制响应数据,我们一般关闭该功能。
2)Proxy_Cache属性:
proxy_cache:设置是否开启对后端响应的缓存,如果开启的话,参数值就是zone的名称,比如:proxy_cache。proxy_cache_valid:针对不同的response code设定不同的缓存时间,如果不设置code,默认为200,301,302,也可以用any指定所有code。proxy_cache_min_uses:指定在多少次请求之后才缓存响应内容,这里表示将缓存内容写入到磁盘。proxy_cache_lock:默认不开启,开启的话则每次只能有一个请求更新相同的缓存,其他请求要么等待缓存有数据要么限时等待锁释放;nginx 1.1.12才开始有。
配套着proxy_cache_lock_timeout一起使用。proxy_cache_key:缓存文件的唯一key,可以根据它实现对缓存文件的清理操作。
4.2 Nginx代理缓存热点数据应用
1)开启代理缓存
修改nginx.conf
,添加如下配置:
proxy_cache_path /usr/local/openresty/nginx/cache levels=1:2 keys_zone=proxy_cache:10m max_size=1g inactive=60m use_temp_path=off;
修改nginx.conf
,添加如下配置:
#门户发布
server {listen 80;server_name www.gpshopvip.com;#推广产品查询location /sku/aditems/type {#先找Nginx缓存rewrite_by_lua_file /usr/local/openresty/nginx/lua/aditem.lua;#启用缓存openresty_cacheproxy_cache proxy_cache;#针对指定请求缓存#proxy_cache_methods GET;#设置指定请求会缓存proxy_cache_valid 200 304 60s;#最少请求1次才会缓存proxy_cache_min_uses 1;#如果并发请求,只有第1个请求会去服务器获取数据#proxy_cache_lock on;#唯一的keyproxy_cache_key $host$uri$is_args$args;#动态代理proxy_pass http://192.168.100.1:8081;}#其他所有请求location / {root /usr/local/gupao/web/static/frant;}
}
重启nginx或者重新加载配置文件nginx -s reload
,再次测试,可以发现下面个规律:
1:先查找Redis缓存
2:Redis缓存没数据,直接找Nginx缓存
3:Nginx缓存没数据,则找真实服务器
我们还可以发现cache目录下多了目录和一个文件,这就是Nginx缓存:
4.3 Cache_Purge代理缓存清理
很多时候我们如果不想等待缓存的过期,想要主动清除缓存,可以采用第三方的缓存清除模块清除缓存 nginx_ngx_cache_purge
。安装nginx的时候,需要添加purge
模块,purge
模块我们已经下载了,在/usr/local/gupao
目录下,添加该模块--add-module=/usr/local/gupao/ngx_cache_purge-2.3/
,这一个步骤我们在安装OpenRestry
的时候已经实现了。
安装好了后,我们配置一个清理缓存的地址:http://192.168.100.130/purge/sku/aditems/type?id=1
#清理缓存
location ~ /purge(/.*) {#清理缓存proxy_cache_purge proxy_cache $host$1$is_args$args;
}
此时访问http://www.gpshopvip.com/purge/sku/aditems/type?id=1,表示清除缓存,如果出现如下效果表示清理成功:
5 缓存一致性
上面我们虽然实现了多级缓存架构,但是问题也出现了,如果数据库中数据发生变更,如何更新Redis缓存呢?如何更新Nginx缓存呢?
我们可以使用阿里巴巴的技术解决方案Canal来实现,通过Canal监听数据库变更,并实时消费变更数据,并更新缓存。
canal [kə’næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费
学习地址:https://github.com/alibaba/canal
早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。
基于日志增量订阅和消费的业务包括
- 数据库镜像
- 数据库实时备份
- 索引构建和实时维护(拆分异构索引、倒排索引等)
- 业务 cache 刷新
- 带业务逻辑的增量数据处理
当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x。
5.1 Canal原理讲解
MySQL主备复制原理
- MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
- MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
- MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
Canal 工作原理
- canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
- MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
- canal 解析 binary log 对象(原始为 byte 流)
5.2 Canal安装
5.2.1 MySQL开启binlog
对于MySQL , 需要先开启 Binlog 写入功能,配置 binlog-format 为 ROW 模式,my.cnf 中配置如下
docker exec -it mysql /bin/bash
cd /etc/mysql/mysql.conf.d
vi mysqld.cnf
在最文件尾部添加如下配置:
log-bin=mysql-bin # 开启 binlog
binlog-format=ROW # 选择 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
**注意:**针对阿里云 RDS for MySQL , 默认打开了 binlog , 并且账号默认具有 binlog dump 权限 , 不需要任何权限或者 binlog 设置,可以直接跳过这一步。
授权 canal 链接 MySQL 账号具有作为 MySQL slave 的权限, 如果已有账户可直接 grant:
CREATE USER canal IDENTIFIED BY 'canal';GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES;
重启mysql容器
docker restart canal
查看是否开启binlog:
show variables like 'log_bin';
5.2.2 Canal安装
我们采用docker安装方式:
docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server
进入容器,修改核心配置canal.properties 和instance.properties,canal.properties 是canal自身的配置,instance.properties是需要同步数据的数据库连接配置。
修改配置如下:
# position info
canal.instance.master.address=192.168.100.130:3306
另一处配置:
# table regex
#canal.instance.filter.regex=.*\\..*
#监听配置
canal.instance.filter.regex=shop_goods.ad_items
配置完成后,重启canal
容器
docker restart canal
5.3 多级缓存架构缓存一致性实战
5.3.1 Canal微服务搭建
工程坐标:
<groupId>com.gupaoedu.vip.mall</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>mall-canal-service</artifactId>
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>mall-service</artifactId><groupId>com.gupaoedu.vip.mall</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>mall-canal-service</artifactId><description>Canal微服务</description><dependencies><!--springboot-canal快速构建依赖包--><dependency><groupId>top.javatool</groupId><artifactId>canal-spring-boot-starter</artifactId><version>1.2.1-RELEASE</version></dependency><!--依赖mall-goods-api--><dependency><groupId>com.gupaoedu.vip.mall</groupId><artifactId>goods-api</artifactId><version>0.0.1-SNAPSHOT</version></dependency></dependencies>
</project>
bootstrap.yml:
server:port: 8083
spring:application:name: mall-canalcloud:nacos:config:file-extension: yamlserver-addr: 192.168.100.130:8848discovery:#Nacos的注册地址server-addr: 192.168.100.130:8848
#Canal配置
canal:server: 192.168.100.130:11111destination: example
#日志配置
logging:pattern:console: "%msg%n"level:root: error
创建监听类:com.gupaoedu.vip.canal.listener.AdItemsHandler
@CanalTable(value = "ad_items")
@Component
public class AdItemsHandler implements EntryHandler<AdItems> {@Autowiredprivate SkuFeign skuFeign;@Overridepublic void insert(AdItems adItems) {//加载缓存skuFeign.updateTypeItems(adItems.getType());}/**** 修改* @param before* @param after*/@Overridepublic void update(AdItems before, AdItems after) {//分类不同,则重新加载之前的缓存if(before.getType().intValue()!=after.getType().intValue()){//修改缓存skuFeign.updateTypeItems(before.getType());}//加载缓存skuFeign.updateTypeItems(after.getType());}@Overridepublic void delete(AdItems adItems) {//删除缓存skuFeign.deleteTypeItems(adItems.getType());}
}
创建启动类:com.gupaoedu.vip.canal.MallCanalApplication
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients(basePackages = {"com.gupaoedu.vip.mall.goods.feign"})
public class MallCanalApplication {public static void main(String[] args) {SpringApplication.run(MallCanalApplication.class,args);}
}
相关文章:
【云商城】高性能门户网构建
第3章 高性能门户网构建 网站门户就是首页 1.OpenResty 百万并发站点架构 1).OpenResty 特性介绍 2).搭建OpenResty 3).Web站点动静分离方案剖析 2.Lua语法学习 1).Lua基本语法 3.多级缓存架构实战 1).多级缓存架构分析 用户请求网站,最开始…...
IvorySQL 升级指南:从 3.x 到 4.0 的平滑过渡
日前,IvorySQL 4.0 重磅发布,全面支持 PostgreSQL 17,并且增强了对 Oracle 的兼容性。关于 IvorySQL 4.0 的介绍,各位小伙伴可以通过这篇文章回顾:IvorySQL 4.0 发布:全面支持 PostgreSQL 17. 在 IvorySQL…...
GESP202312 四级【小杨的字典】题解(AC)
》》》点我查看「视频」详解》》》 [GESP202312 四级] 小杨的字典 题目描述 在遥远的星球,有两个国家 A 国和 B 国,他们使用着不同的语言:A 语言和 B 语言。小杨是 B 国的翻译官,他的工作是将 A 语言的文章翻译成 B 语言的文章…...
数据库_解决SQL Server数据库log日志过大,清理日志文件方法
SQL Server数据库日志文件过大的原因主要有几个方面: 事务日志记录了所有对数据库进行修改的操作,如插入、更新和删除,这些操作会不断增加日志文件的大小。 长时间运行且未正确结束的事务会持续占用事务日志中的空间,导致日志文…...
Java 的单例模式详解及优化
💖 欢迎来到我的博客! 非常高兴能在这里与您相遇。在这里,您不仅能获得有趣的技术分享,还能感受到轻松愉快的氛围。无论您是编程新手,还是资深开发者,都能在这里找到属于您的知识宝藏,学习和成长…...
关于 webservice 日志中 源IP是node IP的问题,是否能解决换成 真实的客户端IP呢
本篇目录 1. 问题背景2. 部署gitlab 17.52.1 添加repo源2.2 添加repo源 下载17.5.0的charts包2.3 修改values文件2.3.1 hosts修改如下2.3.2 appConfig修改如下2.3.3 gitlab下的sidekiq配置2.3.4 certmanager修改如下2.3.5 nginx-ingress修改如下2.3.6 <可选> prometheus修…...
[python3]xlrd不支持Excel xlsx文件类型
https://xlrd.readthedocs.io/en/latest/ xlrd is a library for reading data and formatting information from Excel files in the historical .xls format. 解决办法:指定支持的版本1.2.0,pip3 install xlrd1.20 pip3 install xlrd1.2.0 Looking …...
React中createRoot函数原理解读——Element对象与Fiber对象、FiberRootNode与HostRootNode
【2024最新版】React18 核心源码分析教程(全61集) Element对象与Fiber对象 在 React 中,Element 对象 和 Fiber 对象 是核心概念,用于实现 React 的高效渲染和更新机制。以下是它们的详细解读: 1. Element 对象 定…...
【网络协议】动态路由协议
前言 本文将概述动态路由协议,定义其概念,并了解其与静态路由的区别。同时将讨论动态路由协议相较于静态路由的优势,学习动态路由协议的不同类别以及无类别(classless)和有类别(classful)的特性…...
DevOps:CI、CD、CB、CT、CD
目录 一、软件开发流程演化快速回顾 (一)瀑布模型 (二)原型模型 (三)螺旋模型 (四)增量模型 (五)敏捷开发 (六)DevOps 二、走…...
网络安全 | WAF防护开通流程与技术原理详解
关注:CodingTechWork 引言 随着互联网安全形势的日益严峻,Web应用防火墙(WAF, Web Application Firewall)逐渐成为网站和应用的标准防护措施。WAF能够有效识别和防止如SQL注入、跨站脚本攻击(XSS)、恶意流…...
【我的 PWN 学习手札】IO_FILE 之 FSOP
FSOP:File Stream Oriented Programming 通过劫持 _IO_list_all 指向伪造的 _IO_FILE_plus,进而调用fake IO_FILE 结构体对象中被伪造的vtable指向的恶意函数。 目录 前言 一、glibc-exit函数浅析 二、FSOP 三、Largebin attack FSOP (…...
Spring——自动装配
假设一个场景: 一个人(Person)有一条狗(Dog)和一只猫(Cat),狗和猫都会叫,狗叫是“汪汪”,猫叫是“喵喵”,同时人还有一个自己的名字。 将上述场景 抽象出三个实体类&…...
C语言——文件IO 【文件IO和标准IO区别,操作文件IO】open,write,read,dup2,access,stat
1.思维导图 2.练习 1:使用C语言编写一个简易的界面,界面如下 1:标准输出流 2:标准错误流 3:文件流 要求:按1的时候,通过printf输出数据,按2的时候,通过p…...
人工智能知识分享第九天-机器学习_集成学习
集成学习 概念 集成学习是机器学习中的一种思想,它通过多个模型的组合形成一个精度更高的模型,参与组合的模型称为弱学习器(基学习器)。训练时,使用训练集依次训练出这些弱学习器,对未知的样本进行预测时…...
腾讯云AI代码助手编程挑战赛-武器大师
作品简介 对话过程能够介绍二战 各种武器 冷战 武器 现代的 各种武器装备,陆海空三军都知道。 技术架构 使用全后端分离的架构,前端使用Vue脚手架,腾讯云修改样式css 开发环境、开发流程 系统:win11 开发工具:VS…...
Python机器学习笔记(十八、交互特征与多项式特征)
添加原始数据的交互特征(interaction feature)和多项式特征(polynomial feature)可以丰富特征表示,特别是对于线性模型。这种特征工程可以用统计建模和许多实际的机器学习应用中。 上一次学习:线性模型对w…...
java_深入理解main方法
深入理解main方法 在 main()方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性。但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,[举例说明]…...
NLP中常见的分词算法(BPE、WordPiece、Unigram、SentencePiece)
文章目录 一、基本概念二、传统分词方法2.1 古典分词方法2.2 拆分为单个字符 三、基于子词的分词方法(Subword Tokenization)3.1 主要思想3.2 主流的 Subword 算法3.3 Subword 与 传统分词方法的比较 四、Byte Pair Encoding (BPE)4.1 主要思想4.2 算法过…...
HTTP/HTTPS ②-Cookie || Session || HTTP报头
这里是Themberfue 上篇文章介绍了HTTP报头的首行信息 本篇我们将更进一步讲解HTTP报头键值对的含义~~~ ❤️❤️❤️❤️ 报头Header ✨再上一篇的学习中,我们了解了HTTP的报头主要是通过键值对的结构存储和表达信息的;我们已经了解了首行的HTTP方法和UR…...
前端 动图方案
1、vue3lottie 解析使用Bodymovin导出为json格式的Adobe After Effects动画 vue3lottie的使用神器:vue3-lottie (electron也适用)-CSDN博客 2、MP4文件 //template部分 <video class"header-bg" src"../../assets/images/screen/layout/heade…...
C#语言的字符串处理
C#语言的字符串处理 引言 在现代编程中,字符串处理是一项重要的技能,几乎在所有编程语言中都有应用。C#语言作为一种强类型的、面向对象的编程语言,提供了丰富的字符串处理功能。这使得开发人员能够方便地进行文本操作,比如字符…...
【shell编程】报错信息:bash: bad file descriptor(包含6种解决方法)
大家好,我是摇光~ 在运行 Shell 脚本时,遇到 bash: bad file descriptor 错误通常意味着脚本尝试对一个无效或不可用的文件描述符(file descriptor)执行了读写操作。 以下是一些可能导致这个问题的原因、详细案例以及相应的解决…...
vscode 配置c/c++环境 中文乱码
D:\MIscrobingDownload\mingw64\binmingw配置到环境变量中 测试一下,按winr输入cmd打开终端 gcc -v g -v安装插件 一 二 run code 因为run code 插件配置实质上是用它提供的指令进行编译执行,因此无法直接使用断点调试功能,需要对配置进行…...
leetcode 面试经典 150 题:两数之和
链接两数之和题序号1题型数组解题方法1. 哈希表,2. 暴力法难度简单熟练度✅✅✅✅✅ 题目 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输…...
【Unity报错】error Cs0103: The name ‘keyCode‘ does not exist in the current context
报错提示: 解决方法: KeyCode K大写...
家用万兆网络实践:紧凑型家用服务器静音化改造(二)
大家好,这篇文章我们继续分享家里网络设备的万兆升级和静音改造经验,希望对有类似需求的朋友有所帮助。 写在前面 在上一篇《家用网络升级实践:低成本实现局部万兆(一)》中,我们留下了一些待解决的问题。…...
“AI智能实训系统:让学习更高效、更轻松!
大家好,作为一名资深产品经理,今天我来跟大家聊聊一款备受瞩目的产品——AI智能实训系统。在这个人工智能技术飞速发展的时代,AI智能实训系统应运而生,为广大学习者提供了全新的学习体验。那么,这款产品究竟有哪些亮点…...
【Linux 之一 】Linux常用命令汇总
Linux常用命令 ./catcd 命令chmodclearcphistoryhtoplnmkdirmvpwdrmtailunamewcwhoami 我从2021年4月份开始才开始真正意义上接触Linux,最初学习时是一脸蒙圈,啥也不会,啥也不懂,做了很多乱七八糟,没有条理的笔记。不知…...
Git 从入门到精通
一、环境配置 下载地址:https://git-scm.com/downloads/ 二、用户配置 找到git bash git --version 查看当前版本 git config --global user.name szhipeng625 设置用户名 git config --global user.email szhipeng625gmail.com 设置邮箱 git config --global …...
【Uniapp-Vue3】创建自定义页面模板
大多数情况下我们都使用的是默认模板,但是默认模板是Vue2格式的,如果我们想要定义一个Vue3模板的页面就需要自定义。 一、我们先复制下面的模板代码(可根据自身需要进行修改): <template><view class"…...
Ansible之批量管理服务器
文章目录 背景第一步、安装第二步、配置免密登录2.1 生成密钥2.2 分发公钥2.3 测试无密连接 背景 Ansible是Python强大的服务器批量管理 第一步、安装 首先要拉取epel数据源,执行以下命令 yum -y install epel-release安装完毕如下所示。 使用 yum 命令安装 an…...
android compose 串口通信
1.添加依赖 implementation("io.github.xmaihh:serialport:2.1.1") 2.添加SerialHelper派生类 class SerialPortHelper(portName:String,baudRate:Int): SerialHelper(portName,baudRate) {var receivedDataBuffer mutableListOf<Byte>()override fun onDa…...
ios脚本巨魔商店多巴胺越狱基本操作教程
准备工作 确认设备兼容性:A9-A11(iPhone6s-X):iOS15.0-16.6.1;A12-A14(iPhoneXR-12PM):iOS15.0-16.5.1;A15-A16(iPhone13-…...
NLP项目实战——基于Bert模型的多情感评论分类(附数据集和源码)
在当今数字化的时代,分析用户评论中的情感倾向对于了解产品、服务的口碑等方面有着重要意义。而基于强大的预训练语言模型如 Bert 来进行评论情感分析,能够取得较好的效果。 在本次项目中,我们将展示如何利用 Python 语言结合transformers库&…...
.NET framework、Core和Standard都是什么?
对于这些概念一直没有深入去理解,以至于经过.net这几年的发展进化,概念越来越多,越来越梳理不容易理解了。内心深处存在思想上的懒惰,以为自己专注于Unity开发就好,这些并不属于核心范畴,所以对这些概念总是…...
Mybatis原理简介
看到Mybatis的框架图,可以清晰的看到Mybatis的整体核心对象,我更喜欢用自己的图来表达Mybatis的整个的执行流程。如下图所示: 原理详解: MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根…...
腾讯云AI代码助手-公司职位分析AI助手
作品简介 腾讯云AI代码助手是一款智能工具,专注于为公司提供职位分析服务。通过自然语言处理和机器学习技术,它能快速解析职位描述,提取关键信息,并提供数据驱动的洞察,帮助公司优化招聘流程和职位设计。 技术架构 …...
腾讯云AI代码助手编程挑战赛-解忧助手
作品简介 何以解忧,唯有杜康。而随着Ai的发展,解忧不再只有杜康还有Ai,使用的是腾讯云AI代码助手来生成的所有代码,使用方便,快捷,高效。 技术架构 采用了全后端分离的架构,前端使用Vue.js,腾讯云的AI服务处理自然语…...
Sentinel服务保护 + Seata分布式事务
服务保护 【雪崩问题】微服务调用链路中某个服务,引起整个链路中所有微服务都不可用。 【原因】: 微服务相互调用,服务提供者出现故障。服务调用这没有做好异常处理,导致自身故障。调用链中所有服务级联失败,导致整个…...
【Leetcode·中等·数组】59. 螺旋矩阵 II(spiral matrix ii)
题目描述 英文版描述 Given a positive integer n, generate an n x n matrix filled with elements from 1 to n(2) in spiral order. Example 1: Input: n 3 Output: [[1,2,3],[8,9,4],[7,6,5]] 提示: 1 < n < 20 英文版地址 https://leetcode.com…...
WebSocket 扩展生态:协议与框架
在前七篇文章中,我们深入探讨了 WebSocket 的基础原理、开发实践和实战案例。今天,让我们把视野扩展到 WebSocket 的生态系统,看看有哪些扩展协议和框架可以帮助我们更好地开发 WebSocket 应用。我曾在一个大型即时通讯项目中,通过合理使用这些工具,将开发效率提升了 50%。 扩…...
MySQL —— 在CentOS9下安装MySQL
MySQL —— 在CentOS9下安装MySQL 1.查看自己操作系统的版本2.找到对应的安装源3.上传我们在windows下,下载的文件,解压4.执行rpm命令,启用MySQL8仓库5.执行dnf install -y mysql-community-server6.设置开机自启动7.获得初始密码8.登录MySQL…...
用VS C#构建Windows服务【纯操作版,附带项目地址】
1.点击“创建新项目”,选择“Windows 服务(.NET Framework)” 2、给项目命名 3、双击“Service1.cs”,右键,选择“添加安装程序”,就会生成一个“ProjectInstaller.cs”文件 4、双击“ProjectInstaller.cs”文件,右键“serviceProcessInstaller1”,选择“属性…...
1.UGUI相关
1.这一种UIcanvas下的组件,会显示在3d物体之前 2.可以设置3d物体在UI界面之前。选中第二个模式。这时候会指定一个摄像机。一般情况下,不用主摄像机。需要新建一个专门给UI的摄像机。相当于设置距离摄像机的远近。两个layer 可以理解成 章节,关卡。相同…...
大语言模型训练的数据集从哪里来?
继续上篇文章的内容说说大语言模型预训练的数据集从哪里来以及为什么互联网上的数据已经被耗尽这个说法并不专业,再谈谈大语言模型预训练数据集的优化思路。 1. GPT2使用的数据集是WebText,该数据集大概40GB,由OpenAI创建,主要内…...
Android 来电白名单 只允许联系人呼入电话
客户需求只允许通讯录中联系人可以呼入电话。参考自带的黑名单实现 CallsManager.java类中的onSuccessfulIncomingCall方法有一些过滤器,可以仿照黑名单的方式添加自己的过滤器。 packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java …...
StarRocks Awards 2024 年度贡献人物
在过去一年,StarRocks 在 Lakehouse 与 AI 等关键领域取得了显著进步,其卓越的产品功能极大地简化和提升了数据分析的效率,使得"One Data,All Analytics" 的愿景变得更加触手可及。 虽然实现这一目标的道路充满挑战且漫…...
plane开源的自托管项目
Plane 是一个开源的自托管项目规划解决方案,专注于问题管理、里程碑跟踪以及产品路线图的设计。作为一款开源软件,Plane 的代码托管在 GitHub 平台上,允许任何人查看和贡献代码。它为用户提供了便捷的项目创建与管理手段,并配备了…...
WebLogic安全基线
WebLogic安全基线 一、 用户权限1 、检查weblogic 的启动用户2 、用户权限整改3 、使用普通用户重启weblogic 二、账户共用1 、检查weblogic 控制台的账户2 、账户共用整改3 、测试登录weblogic 控制台新账户 三、 账户清理1 、检查weblogic 控制台的账户2 、帐户清理整改 四、…...