文章目录

  • 配置文件和通用文件
    • 通用结果类型:
    • 返回码:
    • 统一异常处理:
    • 网关
    • feign服务
  • redission配置
  • controller
  • service:热点商品和普通商品分开下单
  • 消息队列监听订单

思路:对于普通商品进行正常下单(直接减去数据库中的库存然后生成订单,对于热点数据加一层redis缓存层,商品放在redis中,用分布式锁加锁,然后可以放入消息队列中排队下单,下单时先检查Redis中库存量是否足够,成功了才减去数据库库存)
在这里还使用了feign用来远程调用服务和网关拦截请求。

下面是代码:/

配置文件和通用文件

@Configuration
public class RedisConfig {/**** 模板操作对象序列化设置* @param redissonConnectionFactory* @return*/@Bean("redisTemplate")public RedisTemplate getRedisTemplate(RedisConnectionFactory redissonConnectionFactory) {RedisTemplate<Object, Object> redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(redissonConnectionFactory);redisTemplate.setValueSerializer(valueSerializer());redisTemplate.setKeySerializer(keySerializer());redisTemplate.setHashKeySerializer(keySerializer());redisTemplate.setHashValueSerializer(valueSerializer());return redisTemplate;}/***** 序列化设置* @return*/@Beanpublic StringRedisSerializer keySerializer() {return new StringRedisSerializer();}/***** 序列化设置* @return*/@Beanpublic RedisSerializer valueSerializer() {Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);return jackson2JsonRedisSerializer;}
}

通用结果类型:

@ApiModel(description = "Result",value = "Result")
public class Result<T> {@ApiModelProperty(value="执行是否成功,true:成功,false:失败",required = true)private boolean flag;//是否成功@ApiModelProperty(value="返回状态码,20000:成功,20001:失败,20002:用户名或密码错误,20003:权限不足,20004:远程调用失败,20005:重复操作,20006:没有对应的数据",required = true)private Integer code;//返回码@ApiModelProperty(value="提示信息",required = true)private String message;//返回消息@ApiModelProperty(value="逻辑数据",required = true)private T data;//返回数据public Result(boolean flag, Integer code, String message, Object data) {this.flag = flag;this.code = code;this.message = message;this.data = (T) data;}public Result(boolean flag, Integer code, String message) {this.flag = flag;this.code = code;this.message = message;}public Result() {this.flag = true;this.code = StatusCode.OK;this.message = "操作成功!";}

返回码:


/*** 返回码*/
public class StatusCode {public static final int OK = 20000;//成功public static final int ERROR = 20001;//失败public static final int LOGINERROR = 20002;//用户名或密码错误public static final int ACCESSERROR = 20003;//权限不足public static final int REMOTEERROR = 20004;//远程调用失败public static final int REPERROR = 20005;//重复操作public static final int NOTFOUNDERROR = 20006;//没有对应的抢购数据//库存递减状态码public static final int DECOUNT_1=1;    //递减成功public static final int DECOUNT_NUM=405;//库存不足public static final int DECOUNT_HOT=205;//商品是热卖商品public static final int DECOUNT_OK=200;//库存递减成功public static final int ORDER_QUEUE=202;//抢购商品正在排队public static final int ORDER_OK=200;//抢单成功//令牌无效public static final int TOKEN_ERROR=401;
}

统一异常处理:

@ControllerAdvice   //所有请求路径,都将被该类处理->过滤器/(拦截器)
public class BaseExceptionHandler {private static Logger logger = LoggerFactory.getLogger(BaseExceptionHandler.class);/**** 异常处理* 当前请求发生了指定异常,则执行该方法处理异常*/@ExceptionHandler(Exception.class)@ResponseBodypublic Result error(Exception ex){StringWriter stringWriter = new StringWriter();PrintWriter writer = new PrintWriter(stringWriter);ex.printStackTrace(writer);ex.printStackTrace();logger.error(stringWriter.toString());return new Result(false, StatusCode.ERROR,ex.getMessage(),stringWriter.toString());}
}

网关

server:port: 8001
spring:application:name: gateway-webcloud:nacos:config:file-extension: yamlserver-addr: nacos-server:8848discovery:#Nacos的注册地址server-addr: nacos-server:8848gateway:routes:#商品- id: goods_routeuri: lb://seckill-goodspredicates:- Path=/api/skuAct/**,/api/activity/**,/api/brand/**,/api/category/**,/api/seckillTime/**,/api/sku/**filters:- StripPrefix=1#订单- id: order_routeuri: lb://seckill-orderpredicates:- Path=/api/order/**filters:- StripPrefix=1#搜索- id: search_routeuri: lb://seckill-searchpredicates:- Path=/api/search/**filters:- StripPrefix=1#用户- id: user_routeuri: lb://seckill-userpredicates:- Path=/api/user/**filters:- StripPrefix=1#管理员- id: manager_routeuri: lb://seckill-managerpredicates:- Path=/api/admin/**filters:- StripPrefix=1#静态页- id: page_routeuri: lb://seckill-pagepredicates:- Path=/api/page/**filters:- StripPrefix=1

网关拦截设置:

@Component
public class AuthorizeFilter implements GlobalFilter,Ordered {//令牌头名字private static final String AUTHORIZE_TOKEN = "Authorization";private static final String ADMINAUTHORIZE_TOKEN = "Admin-Token-Itheima";/**** 过滤拦截* @param exchange* @param chain* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//获取request和responseServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();//获取用户请求的地址String path = request.getURI().getPath();// /api/user/login放行///api/order/add 测试放行if(path.equals("/api/user/login") || path.equals("/api/admin/login") || path.equals("/api/search") || path.equals("/api/activity/times")){//放行return chain.filter(exchange);}HttpMethod method = request.getMethod();System.out.println(method.name());// /sku/xxx   GET方式允许通过if(path.startsWith("/api/sku/") && request.getMethod().name().equalsIgnoreCase("GET")){//放行return chain.filter(exchange);}//获取用户请求头中的令牌String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN); //获取请求头中第1个Authorization参数//如果请求头中没有令牌,则有可能用的是参数传入的if(StringUtils.isEmpty(token)){token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);//获取请求参数中第1个Authorization}//如果请求头和参数中都没有令牌,则直接拒绝用户访问各大微服务if(StringUtils.isEmpty(token)){//从Cookie中获取令牌数据HttpCookie cookie = request.getCookies().getFirst(AUTHORIZE_TOKEN);HttpCookie adminCookie = request.getCookies().getFirst(ADMINAUTHORIZE_TOKEN);if(cookie==null && adminCookie==null){//状态吗  401response.setStatusCode(HttpStatus.UNAUTHORIZED);//结束当前请求return response.setComplete();}//获取令牌if(cookie!=null){token = cookie.getValue();}else{token = adminCookie.getValue();}//将令牌封装到请求头中request.mutate().header(AUTHORIZE_TOKEN,"bearer "+token);}return chain.filter(exchange);}/**** 排序* @return*/@Overridepublic int getOrder() {return 0;}@Beanpublic CorsWebFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();// cookie跨域config.setAllowCredentials(Boolean.TRUE);   //允许Cookie跨域config.addAllowedMethod("*");   //所有提交方法都允许跨域config.addAllowedOrigin("*");   //所有的域名都允许跨域config.addAllowedHeader("*");//跨域解析器UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());source.registerCorsConfiguration("/**", config); //所有请求路径都支持跨域return new CorsWebFilter(source);}
}

feign服务

下面是库存的接口:

@FeignClient(value = "seckill-goods")
public interface SkuFeign {/***** 库存递减*/@PutMapping(value = "/sku/dcount/{id}/{count}")Result<Sku> dcount(@PathVariable(value = "id")String id,@PathVariable(value = "count")Integer count);/**** 热点商品隔离*/@PostMapping(value = "/sku/hot/isolation")Result hotIsolation(@RequestParam List<String> ids);/***** 分页查询-查询总数量*/@GetMapping(value = "/sku/count")Integer count();/***** 分页查询集合列表*/@GetMapping(value = "/sku/list/{page}/{size}")List<Sku> list(@PathVariable(value = "page")Integer page,@PathVariable(value = "size")Integer size);/**** 根据ID查询Sku数据* @param id* @return*/@GetMapping("/sku/{id}")Result<Sku> findById(@PathVariable String id);/**** 商品数据归0*/@GetMapping(value = "/sku/zero/{id}")Result zero(@PathVariable(value = "id") String id);
}

消息通知的接口:

@FeignClient(value = "seckill-message")
public interface MessageFeign {/***** 发送消息*/@GetMapping(value = "/message/{userid}")String send(@PathVariable(value = "userid")String userid,@RequestParam(value = "msg") String msg) throws IOException;
}

redission配置

多个用户实现加锁操作,只允许有一个用户可以获取到对应锁

@Component
public class RedissonDistributedLocker implements DistributedLocker {@Autowiredprivate RedissonClient redissonClient;/**** 加锁,会一直循环加锁,直到拿到锁* @param lockkey* @return*/@Overridepublic RLock lock(String lockkey) {RLock lock = redissonClient.getLock(lockkey);lock.lock();return lock;}/**** 加锁,在指定时间内拿不到锁就会放弃* @param lockkey* @return*/@Overridepublic RLock lock(String lockkey, long timeout) {RLock lock = redissonClient.getLock(lockkey);lock.lock(timeout,TimeUnit.SECONDS);return lock;}/**** 加锁,在指定时间内拿不到锁就会放弃* @param lockkey* @return*/@Overridepublic RLock lock(String lockkey, long timeout, TimeUnit unit) {return null;}/**** 加锁,在指定时间内拿不到锁就会放弃,如果拿到锁,锁最终有效时间为leasetime* @param lockkey* @return*/@Overridepublic boolean tryLock(String lockkey, long timeout, long leasetime, TimeUnit unit) {return false;}/***** 解锁* @param lockkey*/@Overridepublic void unLock(String lockkey) {RLock lock = redissonClient.getLock(lockkey);lock.unlock();}/**** 解锁* @param lock*/@Overridepublic void unLocke(RLock lock) {lock.unlock();}
}

controller

商品SkuController

@RestController
@RequestMapping("/sku")public class SkuController {@Autowiredprivate SkuService skuService;
/***** 库存递减*/@PutMapping(value = "/dcount/{id}/{count}")public Result<Sku> dcount(@PathVariable(value = "id")String id,@PathVariable(value = "count")Integer count){//1.调用业务层实现递减int code = skuService.dcount(id, count);String message="";Sku sku = null;switch (code){case StatusCode.DECOUNT_OK:sku = skuService.findById(id);message="库存递减成功!";break;case StatusCode.DECOUNT_NUM:message="库存不足!";break;case StatusCode.DECOUNT_HOT:message="商品是热点商品!";break;default:}//3.根据状态码,响应不同的提示信息return  new Result<Sku>(true,code,message,sku);}/**** 热点商品隔离*/@PostMapping(value = "/hot/isolation")public Result hotIsolation(@RequestParam List<String> ids){for (String id : ids) {skuService.hotIsolation(id);}return new Result(true,StatusCode.OK,"热点数据隔离成功!");}

订单OrderController:


@RestController
@RequestMapping("/order")
//@CrossOrigin
public class OrderController {@Autowiredprivate OrderService orderService;@Autowiredprivate IdWorker idWorker;/***** 添加订单*/@PostMapping(value = "/add/{id}")public Result add(@PathVariable(value = "id") String id, @RequestHeader(value = "Authorization") String authorization) {String username = null;try {//解析令牌Map<String, Object> tokenMap = JwtTokenUtil.parseToken(authorization);username =tokenMap.get("username").toString();} catch (Exception e) {return new Result(false, StatusCode.TOKEN_ERROR, "令牌无效!");}//封装OrderOrder order = new Order();order.setId("No"+idWorker.nextId());order.setSkuId(id);order.setCreateTime(new Date());order.setUpdateTime(order.getCreateTime());order.setUsername(username);order.setTotalNum(1);//添加订单int code = orderService.add(order);switch (code) {case StatusCode.ORDER_OK:return new Result(true, StatusCode.ORDER_OK, order.getId());case StatusCode.DECOUNT_NUM:return new Result(false, StatusCode.DECOUNT_NUM, "库存不足!");case StatusCode.ORDER_QUEUE:return new Result(true, StatusCode.ORDER_QUEUE, "排队抢购中!");default:return new Result(false, StatusCode.ERROR, "抢单发生异常!");}}/**** Order分页条件搜索实现* @param page* @param size* @return*/@PostMapping(value = "/search/{page}/{size}")public Result<PageInfo> findPage(@RequestBody(required = false) OrderVo orderVo, @PathVariable int page, @PathVariable int size) {//调用OrderService实现分页条件查询OrderOrder order = new Order();BeanUtils.copyProperties(orderVo,order);PageInfo<Order> pageInfo = orderService.findPage(order, page, size);return new Result(true, StatusCode.OK, "查询成功", pageInfo);}/**** 用户订单* @param page* @param size* @return*/@GetMapping(value = "/user/{page}/{size}")public Result<PageInfo> userOrders(@PathVariable int page,@PathVariable int size,@RequestParam(value = "type",defaultValue = "0")Integer type,@RequestHeader("Authorization")String authorization) {Map<String, Object> userMap = JwtTokenUtil.parseToken(authorization);//调用OrderService实现分页条件查询OrderOrder order = new Order();order.setUsername(userMap.get("username").toString());switch (type){case 1:order.setPayStatus("0");break;case 3:order.setPayStatus("1");break;}PageInfo<Order> pageInfo = orderService.findPage(order, page, size);return new Result(true, StatusCode.OK, "查询成功", pageInfo);}}

service:热点商品和普通商品分开下单

这里用了druid工具进行热点数据监控:

@Component
public class MonitorItemsAccess {@Value("${druidurl}")private String druidurl;@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate DruidDataSource dataSource;/******* 定义热点数据标准:*      1.某一件商品访问量>N*      2.最近N小时*/public List<String> loadData() throws Exception{//获取连接对象//Connection connection = (AvaticaConnection) DriverManager.getConnection(druidurl);Connection connection =dataSource.getConnection();//StatementStatement statement = connection.createStatement();//执行查询ResultSet resultSet = statement.executeQuery(druidSQL());//解析结果集List<String> ids = new ArrayList<String>();while (resultSet.next()){String uri = resultSet.getString("uri");uri=uri.replace("/web/items/","").replace(".html","");ids.add(uri);}//关闭资源resultSet.close();statement.close();connection.close();return ids;}/**** SQL组装* @return*/public String druidSQL(){//SQL语句String prefix="SELECT COUNT(*) AS \"viewCount\",uri FROM logsitems WHERE __time>=CURRENT_TIMESTAMP - INTERVAL '1' HOUR";//后部分String suffix=" GROUP BY uri HAVING viewCount>2";//SQL中间部分  AND uri NOT IN ('/web/items/S1235433012716498944.html')//SKU_S1235433012716498944String sql = "";//基于Redis中存的热点商品的key来过滤排除要查询的数据Set<String> keys = redisTemplate.keys("SKU_*");//所有以SKU_开始的key全部查询出来if(keys!=null && keys.size()>0){sql=" AND uri NOT IN (";for (String key : keys) {sql+="'/web/items/"+key.substring(4)+".html',";}sql=sql.substring(0,sql.length()-1);sql+=")";}return prefix+sql+suffix;}
}

然后用定时任务组件来执行上面的监控方法,设置每隔多久执行一次,这里用的是elasticjob第三方工具,实际当中可以用其他的定时任务框架

import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.dangdang.elasticjob.lite.annotation.ElasticSimpleJob;
import com.seckill.goods.feign.SkuFeign;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;
@Component
@ElasticSimpleJob(//这是elasticjob工具的定时任务注解cron = "1/5 * * * * ?",jobName = "monitortask",shardingTotalCount = 1
)
public class MonitorTask implements SimpleJob{@Autowiredprivate MonitorItemsAccess monitorItemsAccess;@Autowiredprivate SkuFeign skuFeign;/**** 执行业务逻辑* @param shardingContext*/@SneakyThrows@Overridepublic void execute(ShardingContext shardingContext) {List<String> ids = monitorItemsAccess.loadData();for (String id : ids) {System.out.println("热点商品ID:"+id);}//热点数据隔离skuFeign.hotIsolation(ids);}
}

订单Service

@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate SkuFeign skuFeign;@Autowiredprivate IdWorker idWorker;@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate KafkaTemplate kafkaTemplate;@Autowiredprivate RedissonDistributedLocker redissonDistributedLocker;@Autowiredprivate MessageFeign messageFeign;/***** 热点商品下单* @param orderMap* @return*/@Overridepublic void hotAdd(Map<String, String> orderMap) throws IOException {//消息封装对象Map<String,Object> messageMap = new HashMap<String,Object>();String username = orderMap.get("username");String id = orderMap.get("id");//Redis中对应的keyString key="SKU_"+id;String lockkey="LOCKSKU_"+id;String userKey="USER"+username+"ID"+id;//如果key在redis缓存,则表示商品信息在Redis中进行操作boolean bo =true;// redissonDistributedLocker.tryLock(lockkey, 10, 10, TimeUnit.MINUTES);if(bo){if(redisTemplate.hasKey(key)){//获取商品数量Integer num = Integer.parseInt(redisTemplate.boundHashOps(key).get("num").toString());if(num<=0){//商品售罄通知messageMap.put("code",20001);messageMap.put("message","商品已售罄");messageFeign.send(username,JSON.toJSONString(messageMap));return;}Result<Sku> skuResult =skuFeign.findById(id);Sku sku = skuResult.getData();//1.创建OrderOrder order = new Order();order.setTotalNum(1);order.setCreateTime(new Date());order.setUpdateTime(order.getCreateTime());order.setId("No"+idWorker.nextId());order.setOrderStatus("0");order.setPayStatus("0");order.setConsignStatus("0");order.setSkuId(id);order.setName(sku.getName());order.setPrice(sku.getSeckillPrice()*order.getTotalNum());orderMapper.insertSelective(order);//2.Redis中对应的num递减num--;if(num==0){skuFeign.zero(id);}//2.清理用户排队信息Map<String,Object> allMap = new HashMap<String,Object>();allMap.put(userKey,0);allMap.put("num",num);redisTemplate.boundHashOps(key).putAll(allMap);//3.记录用户购买过该商品,24小时后过期redisTemplate.boundValueOps(userKey).set("");redisTemplate.expire(userKey,1,TimeUnit.MINUTES);//抢单成功通知messageMap.put("code",200);messageMap.put("message","抢单成功!");messageFeign.send(username,JSON.toJSONString(messageMap));}//释放锁//redissonDistributedLocker.unLock(lockkey);return;}//抢单失败通知messageMap.put("code",20001);messageMap.put("message","抢单失败!");messageFeign.send(username,JSON.toJSONString(messageMap));}/**** 添加订单* @param order* @return*/@GlobalTransactional@Overridepublic int add(Order order) {String userKey="USER"+order.getUsername()+"ID"+order.getSkuId();//1.递减库存Result<Sku> dcount = skuFeign.dcount(order.getSkuId(), order.getTotalNum());//2.递减成功->下单->记录当前用户抢单的时间点,24小时内不能抢购该商品if(dcount.getCode()== StatusCode.DECOUNT_OK){//int q=10/0;Sku sku = dcount.getData();//下单//order.setId("No"+idWorker.nextId());order.setOrderStatus("0");order.setPayStatus("0");order.setConsignStatus("0");order.setSkuId(sku.getId());order.setName(sku.getName());order.setPrice(sku.getSeckillPrice()*order.getTotalNum());orderMapper.insertSelective(order);//记录当前用户抢单的时间点,24小时内不能抢购该商品redisTemplate.boundValueOps(userKey).set("");redisTemplate.boundValueOps(userKey).expire(1, TimeUnit.MINUTES);return StatusCode.ORDER_OK;}else{//3.递减失败//405库存不足if(dcount.getCode()==StatusCode.DECOUNT_NUM){return StatusCode.DECOUNT_NUM;}else if(dcount.getCode()==StatusCode.DECOUNT_HOT){//205商品热点String key = "SKU_"+order.getSkuId();Long increment = redisTemplate.boundHashOps(key).increment(userKey, 1);if(increment==1){//执行排队Map<String,String> queueMap = new HashMap<String,String>();queueMap.put("username",order.getUsername());queueMap.put("id",order.getSkuId());kafkaTemplate.send("neworder", JSON.toJSONString(queueMap));}return StatusCode.ORDER_QUEUE;}//0return dcount.getCode();}
}

消息队列监听订单

@Component
public class RabbitOrderListener {@Autowiredprivate OrderService orderService;/**** 订单消费* @param message*/@RabbitListener(queues = PayOrderMchNotifyMQ.MQ_NAME)public void getMessage(String message) throws IOException {//下单信息Map<String,String> orderMap = JSON.parseObject(message,Map.class);//热点商品下单orderService.hotAdd(orderMap);}
}

微服务秒杀项目整合网关+feign+redis分离热点商品分别下单示例相关推荐

  1. 微服务轮子项目(53) -理论小结

    文章目录 1. 概述 2. 基础知识 2.1 整体架构 2.2 分布式事务 2.3 Sentinel限流熔断 3. 架构设计 3.1 服务认证架构设计 3.2 日志架构设计 3.3 监控架构设计 3. ...

  2. 微服务Spring Boot 整合 Redis 实现 好友关注

    文章目录 ⛅引言 一.Redis 实现好友关注 -- 关注与取消关注 二.Redis 实现好友关注 -- 共同关注功能 ⛵小结 ⛅引言 本博文参考 黑马 程序员B站 Redis课程系列 在点评项目中, ...

  3. 猿创征文 | 微服务 Spring Boot 整合Redis 实战开发解决高并发数据缓存

    文章目录 一.什么是 缓存? ⛅为什么用缓存? ⚡如何使用缓存 二.实现一个商家缓存 ⌛环境搭建 ♨️核心源码 ✅测试接口 三.采用 微服务 Spring Boot 注解开启缓存 ✂️@CacheEn ...

  4. guns 最新开源框架企业版下载_国内比较火的5款Java微服务开源项目

    本文介绍国内比较火的5款Java微服务开源项目,pig是基于Spring Cloud.OAuth2.0.Vue的前后端分离的系统. 通用RBAC权限设计及其数据权限和分库分表 支持服务限流.动态路由. ...

  5. 国内最火5款Java微服务开源项目

    国内最火5款Java微服务开源项目 目录 1.pig 2.zheng 3.Cloud-Platform 4.SpringBlade 5.Guns PIG 开源地址:https://gitee.com/ ...

  6. 十款优质企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)

    Java微服务开源项目 前言 一.pig 二.zheng 三.SpringBlade 四.SOP 五.matecloud 六.mall 七.jeecg-boot 八.Cloud-Platform 九. ...

  7. SpringCloud 各个微服务之间会话共享以及Feign调用会话共享

    目录 1.会话共享应用背景 2.SpringCloud各个微服务 (SpringBoot)应用之间会话共享 2.1.启动类或者Redis配置类加入Redis会话共享注解 2.2.配置Redis基本配置 ...

  8. 万字长文解析:分布式架构、SOA、微服务架构、API网关、ESB服务总线架构之间的关联及演进

    1架构演进 架构十五年:改变的是形态,不变的是目的 业务驱动架构形态变化 过去十几年,随着互联网发展以及业务的多样化,系统的架构也在不断发生变化,总体上来说大体经历了从单体应用架构-垂直应用架构-分布 ...

  9. 企业开发必备的6个Spring Cloud微服务开源项目

    前言 今天介绍六款比较热门的SpringCloud微服务项目,感兴趣的可以clone下来研究一下,相信对你学习微服务架构很有帮助.一键获取源码地址 一.Cloud-Platform 介绍 Cloud- ...

最新文章

  1. python爬虫教程下载-Python爬虫视频教程全集下载
  2. php网页生命周期函数,PHP的生命周期
  3. MySQL数据库之MyISAM与InnoDB的区别
  4. 英特尔回应安全漏洞问题:已在硬件层面解决
  5. linux+即时通讯服务器,linux平台上的即时通讯应用开发
  6. 2022 CVPR 三维人体重建相关论文汇总(3D Human Reconstruction)
  7. 计算机考研网络复试总结
  8. html5妇女节游戏,html5开发三八女王节表白神器
  9. ThinkPhp6+Vue智慧医疗后台管理系统
  10. strstr函数 C++
  11. 学分绩点计算器java_东南大学 学分绩点gpa 计算器 【源码】
  12. 解决专利侵权的另一个想法:消除陪审团审判
  13. H.264 序列参数集(SPS)
  14. 掌握农业信息化核心 物联网助力智慧农业
  15. 7月16日周二晚上,陈勇,【敏捷网络课堂第六期】【免费】敏捷开发早期估算
  16. c++沙盒小游戏1.0
  17. c语言动态结构体数组
  18. 【学习cmake】cmake如何使用链接库 (link_directories, LINK_LIBRARIES, target_link_libraries,FIND_PACKAGE)实践篇2
  19. 软考系统分析师倒计时第7天
  20. MATLAB---CAD显示三角网格

热门文章

  1. app把信息添加到mysql_如何将数据库表中的数据添加到ListView C#Xamarin Android App
  2. 10 windows 启动虚拟机报错_Windows 系统如何安装 Docker
  3. Mysql拐点_InnoDB select性能拐点测试
  4. gitkraen_超详细!Github团队协作教程(Gitkraken版)
  5. 七十四、滑动窗口最值问题
  6. 六、Hbase的构架,安装和基本使用
  7. 六十七、Leetcode数组系列(下篇)
  8. 五、开始Github和码云之旅,新手如何上路
  9. 简单php不用mysql_简单的PHP / MySQL不工作
  10. 小白入门商业数据分析师的课程测评