项目概述

此电商项目为本人学习项目,后端 使用nginx实现负载均衡转发请求到多台tomcat服务器,使用多台 redis服务器分布式 缓存用户登录信息。

项目已经部署到阿里云服务器,从阿里云linux服务器租用,到项目前后台代码的完善,前后花费了3个月左右的时间。

项目地址

本人已经部署该项目,租用的阿里云服务器的ip地址为:47.106.172.105,购买的域名地址为:www.xwld.site

商城地址为:http://www.xwld.site

大部分商品详情图片还没有上传,暂时只上传了一个商品用于演示。

地址:http://www.xwld.site/list.html?categoryId=100006

后端所用技术

  • Spring
  • SpringMVC
  • MyBatis
  • MySQL
  • Lombok:省去手动创建setter和getter方法
  • Mycat:数据库分库分表中间件
  • Redis:缓存
  • Jedis:Redis的Java Client
  • Nginx
  • Tomcat
  • Maven
  • 第三方接口
    • 支付宝沙箱测试接口,实现订单支付

前端所用技术

  • Html
  • Css
  • JavaScript
  • Node.js
  • Npm
  • Webpack
  • Charles

项目架构及功能模块图

linux项目运行的shell脚本

首先从码云中拉取项目对应的tag,然后进入项目目录,执行maven打包命令。

[root@izwz918nqae9soh0p70seuz bin]# cat mall_backend.sh
#!/bin/bash
# author xw
# create_date 2018年11月6日
"===========进入git项目mmall目录============="
cd /app/gitRepository/mmall_backend
echo "==================删除之前的tag====================="
rm -rf *
echo "==========git切换分之到mmall-v1.0==============="
git clone --branch back_release_$ git@gitee.com:xwmall/backend.git
echo "===========编译并跳过单元测试===================="
cd backend/mmallv4.
mvn clean package -Pprod -Dmaven.test.skip=true
echo "============删除旧的ROOT.war==================="
rm -rf /soft/tomcat1/webapps/ROOT.war
echo "======拷贝编译出来的war包到tomcat下-ROOT.war======="
cp /app/gitRepository/mmall_backend/backend/mmallv4./target/mmall.war /soft/tomcat1/webapps/ROOT.war echo "============删除tomcat下旧的ROOT文件夹============="
rm -rf /soft/tomcat1/webapps/ROOT echo "====================关闭tomcat====================="
#/soft/tomcat1/bin/shutdown.sh
pkill - java
echo "================sleep 10s========================="
for i in {..}
do
echo $i"s"
sleep 1s
done echo "====================启动tomcat====================="
/soft/tomcat1/bin/startup.sh

github与 码云

码云

此项目本人使用码云来存储项目

每发布一个版本创建一个tag标记,shell 中使用git命令获取相应的tag版本

github

github上面是目前是没什么项目,

由于github上面创建私有的项目需要收费,故而,一直使用码云来存储项目,等到后期项目再进一步完善,再迁移到github中开源。

github地址如下:https://github.com/weiqinshian/mall

项目完整购买流程展示

首页

商品列表页面

暂时只上传了一个商品。

地址:http://www.xwld.site/list.html?categoryId=100006

商品详情页面

地址:http://www.xwld.site/detail.html?productId=27

登录页面

地址:http://www.xwld.site/user-login.html?redirect=http%3A%2F%2Fwww.xwld.site%2Forder-detail.html%3ForderNo%3D1549504659738

测试账号:admin

密码:123456

购物车页面

订单确认页面

生成支付二维码页面

本人手机安装了沙箱测试版支付宝,使用沙箱测试版支付宝扫码支付。

手机扫码支付成功之后

查看订单详情

redis 分布式缓存session

项目中集成redis客户端Jedis

jedis 是redis 的java客户端连接包。

百度搜索【maven】进入,maven中央仓库,搜索jedis 获取到,jedis-client的maven引用。

如,下图:

<dependency>

<groupId>redis.clients</groupId>

<artifactId>jedis</artifactId>

<version>2.6.0</version>

</dependency>

注意版本号一定不要出错,否则,可能会和其他jar 有冲突。

redis连接池的构建及调试

要设置为静态类型,是需要保证在tomcat启动的时候就将jedis 连接池加载进来。

Jedis API封装及调试

jedis 是 redis官方推荐的java 客户端,使用java去连接redis的时候,引入jedis相关jar包就可以了。

使用jedis去连接redis ,要手写redis 连接池配置,在项目启动的时候,要初始化连接池。

redis 连接工具类

本示例中展示了两个redis的连接配置,可以执行下面类中mian进行测试,连接两台redis服务。

执行main方法前,先要在阿里云服务器,通过配置文件启动两台端口配置不同的redis服务。

package com.mmall.common;

import java.util.ArrayList;

import java.util.List;

import com.mmall.util.PropertiesUtil;

import redis.clients.jedis.JedisPoolConfig;

import redis.clients.jedis.JedisShardInfo;

import redis.clients.jedis.ShardedJedis;

import redis.clients.jedis.ShardedJedisPool;

import redis.clients.util.Hashing;

import redis.clients.util.Sharded;

/**

* redis 分布式,连接池配置

* @author XW

*

*/

public class RedisShardedPool {

private static ShardedJedisPool pool;//sharded(分片) jedis连接池

private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty("redis.max.total","20")); //最大连接数

private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.max.idle","20"));//在jedispool中最大的idle状态(空闲的)的jedis实例的个数

private static Integer minIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.min.idle","20"));//在jedispool中最小的idle状态(空闲的)的jedis实例的个数

private static Boolean testOnBorrow = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.borrow","true"));//在borrow一个jedis实例的时候,是否要进行验证操作,如果赋值true。则得到的jedis实例肯定是可以用的。

private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.return","true"));//在return一个jedis实例的时候,是否要进行验证操作,如果赋值true。则放回jedispool的jedis实例肯定是可以用的。

private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");

private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));

private static String redis1Pwd = PropertiesUtil.getProperty("redis1.pwd");

private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");

private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));

private static String redis2Pwd = PropertiesUtil.getProperty("redis2.pwd");

private static void initPool(){

JedisPoolConfig config = new JedisPoolConfig();

config.setMaxTotal(maxTotal);

config.setMaxIdle(maxIdle);

config.setMinIdle(minIdle);

config.setTestOnBorrow(testOnBorrow);

config.setTestOnReturn(testOnReturn);

config.setBlockWhenExhausted(true);//连接耗尽的时候,是否阻塞,false会抛出异常,true阻塞直到超时。默认为true。

JedisShardInfo info1 = new JedisShardInfo(redis1Ip,redis1Port,1000*2);

info1.setPassword(redis1Pwd);

JedisShardInfo info2 = new JedisShardInfo(redis2Ip,redis2Port,1000*2);

info2.setPassword(redis2Pwd);//设置redis登录密码

List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>(2);

jedisShardInfoList.add(info1);

jedisShardInfoList.add(info2);

/**

* Hashing.MURMUR_HASH,使用一致性hash算法

*/

pool = new ShardedJedisPool(config,jedisShardInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);

}

static{

initPool();

}

public static ShardedJedis getJedis(){

return pool.getResource();

}

public static void returnBrokenResource(ShardedJedis jedis){

pool.returnBrokenResource(jedis);

}

public static void returnResource(ShardedJedis jedis){

pool.returnResource(jedis);

}

public static void main(String[] args) {

ShardedJedis jedis = pool.getResource();

for(int i =0;i<10;i++){

jedis.set("key"+i,"value"+i);

}

returnResource(jedis);

System.out.println("program is end");

}

}

pool = new ShardedJedisPool(config,jedisShardInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);

配置redis 连接池的时候,可以指定 分布式算法 使用:一致性hash算法

Jackson 封装JsonUtil及测试

以前,在用户登录之后,是将一个user对象,存入session中。

Redis 中不能存储对象,故而,需要将user 对象,先序列化为一个string,然后,将这个string 存入 redis中。

将相关序列化,和反序列化的方法,封装在JsonUtil类中。

JsonUtil类代码

可以运行此类中的main方法,进行测试。

package com.mmall.util;

import java.text.SimpleDateFormat;

import org.apache.commons.lang.StringUtils;

import org.codehaus.jackson.map.DeserializationConfig;

import org.codehaus.jackson.map.ObjectMapper;

import org.codehaus.jackson.map.SerializationConfig;

import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;

import org.codehaus.jackson.type.JavaType;

import org.codehaus.jackson.type.TypeReference;

import com.mmall.pojo.TestPojo;

import lombok.extern.slf4j.Slf4j;

/**

* Created by XW

*/

@Slf4j

public class JsonUtil {

private static ObjectMapper objectMapper = new ObjectMapper();

static{

/*1.下面这些参数,会影响对象序列化的行为*/

//对象的所有字段全部序列化为字符串,不管是否有字段为null

objectMapper.setSerializationInclusion(Inclusion.ALWAYS);

//取消默认转换timestamps形式,带时间戳

objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false);

//忽略空Bean转json的错误,当user对象转json时,user对象中所有属性为null,转化为json对象也不报异常

objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS,false);

//所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss,实体类user中有属性为Date类型,使用此设置能格式化这种类型的格式。

objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));

/*2.下面这些参数,会影响对象反序列化的行为*/

//忽略在json字符串中存在,但是在java对象中不存在对应属性的情况。防止转换抛出错误

objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);

}

/**

* 对象转换为字符串

* @param obj

* @return

*/

public static <T> String obj2String(T obj){

if(obj == null){

return null;

}

try {

return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj);

} catch (Exception e) {

log.warn("Parse Object to String error",e);

return null;

}

}

/**

* 返回格式化好的字符串

* @param obj

* @return

*/

public static <T> String obj2StringPretty(T obj){

if(obj == null){

return null;

}

try {

return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);

} catch (Exception e) {

log.warn("Parse Object to String error",e);

return null;

}

}

/**

* 反序列化方法

* 将字符串转换为T类型对象

* 注意:此方法不能反序列化,集合+泛型类型的字符串,如: List<User>

* @param str

* @param clazz

* @return

*/

public static <T> T string2Obj(String str,Class<T> clazz){

if(StringUtils.isEmpty(str) || clazz == null){

return null;

}

try {

return clazz.equals(String.class)? (T)str : objectMapper.readValue(str,clazz);

} catch (Exception e) {

log.warn("Parse String to Object error",e);

return null;

}

}

/**

* 反序列化方法

* 将字符串转换为T类型对象

* 注意:此方法比上一个强大,能反序列化,集合+泛型类型的字符串,如: List<User>

* List<User> userListObj1 = JsonUtil.string2Obj(userListStr, new TypeReference<List<User>>()

* @param str

* @param typeReference

* @return

*/

public static <T> T string2Obj(String str, TypeReference<T> typeReference){

if(StringUtils.isEmpty(str) || typeReference == null){

return null;

}

try {

return (T)(typeReference.getType().equals(String.class)? str : objectMapper.readValue(str,typeReference));

} catch (Exception e) {

log.warn("Parse String to Object error",e);

return null;

}

}

/**

* 反序列化方法

* 将字符串转换为T类型对象

* 注意:此方法比上一个强大,能反序列化,集合+泛型类型的字符串,如: List<User>

* List<User> userListObj2 = JsonUtil.string2Obj(userListStr,List.class,User.class);

* @param str

* @param collectionClass

* @param elementClasses

* @return

*/

public static <T> T string2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){

JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);

try {

return objectMapper.readValue(str,javaType);

} catch (Exception e) {

log.warn("Parse String to Object error",e);

return null;

}

}

Mall电商项目总结(一)——项目概述的更多相关文章

  1. Java生鲜电商平台-微服务架构概述

    Java生鲜电商平台-微服务架构概述 单体架构存在的问题 在传统的软件技术架构系统中,基本上将业务功能集中在单一应用内,或者是单一进程中.尽管现代化的软件架构理论以及设计原则已推广多年,但实际技术衍化 ...

  2. Mall电商实战项目发布重大更新,全面支持SpringBoot 2.3.0

    1. 前言 前面近一个月去写自己的mybatis框架了,对mybatis源码分析止步不前,此文继续前面的文章.开始分析mybatis一,二级缓存的实现. 附上自己的项目github地址:https:/ ...

  3. Mall电商项目总结(二)——nginx负载均衡配置和策略

    1. nginx配置文件 用户在浏览器上输入,http://www.xwld.site/ 实际上是在访问服务器80端口,nginx 监听80端口,将用户的请求转发到8080和9080端口 . upst ...

  4. Flutter实战视频-移动电商-09.首页_项目结构建立和获取数据

    09.首页_项目结构建立和获取数据 在config下创建service_url.dart 用来配置我们后端接口的配置文件 一个变量存 接口地址,一个接口方法地址 所有后天请求数据的方法都放在这个文件夹 ...

  5. Java 18套JAVA企业级大型项目实战分布式架构高并发高可用微服务电商项目实战架构

    Java 开发环境:idea https://www.jianshu.com/p/7a824fea1ce7 从无到有构建大型电商微服务架构三个阶段SpringBoot+SpringCloud+Solr ...

  6. 电商CRM的痛点在哪里?

    观电商风云,风起云涌,如何寻找新客户及维护老客户,抢占市场,此时迫在眉睫.在大家所认为的CRM就是发发短信,发发邮件等形式去推送活动信息,但在三疯看来,做CRM的关键词是“互动”,而不是简单的促销. ...

  7. 14 微服务电商【黑马乐优商城】:day04-项目搭建(一)

    本项目的笔记和资料的Download,请点击这一句话自行获取. day01-springboot(理论篇) :day01-springboot(实践篇) day02-springcloud(理论篇一) ...

  8. GitHub Vue项目推荐|Vue+Element实现的电商后台管理系统功能丰富

    GitHub Vue项目推荐|mall-admin-web是一个电商后台管理系统的前端项目基于Vue+Element实现 主要包括商品管理.订单管理.会员管理.促销管理.运营管理.内容管理.统计报表. ...

  9. 如何一步一步用DDD设计一个电商网站(二)—— 项目架构

    阅读目录 前言 六边形架构 终于开始建项目了 DDD中的3个臭皮匠 CQRS(Command Query Responsibility Segregation) 结语 一.前言 上一篇我们讲了DDD的 ...

随机推荐

  1. mysql 源码 jin-yang.github.io

    https://jin-yang.github.io/post/mysql-group-commit.html

  2. cocos2d-x step by step(3) Doub le Kill简单的一些小动画

    在触控厮混了两年多,不过达到了自己的初衷以及目的. 目前从事cocos2d的更改和调优移植工作. 1 简单的一个图片放大和缩小 auto sprite = Sprite::create("l ...

  3. dubbo开发者指南

    开发者指南 参与 流程 任务 版本管理 源码构建 框架设计 整体设计 模块分包 依赖关系 调用链 暴露服务时序 引用服务时序 领域模型 基本原则 扩展点加载 扩展点配置 扩展点自动包装 扩展点自动装配 ...

  4. java中finalkeyword

    在java中有3个地方须要用finalkeyword: 1.假设一个不希望被继承,那么用final来修饰这个类 2.假设一个方法不须要被重写.那么这种方法用final来修饰 3.假设一个变量的值不希望 ...

  5. BIEE11G系统数据源账号过期问题(默认安装步骤)

    BIEE默认完毕安装后处于安全的考虑会对BI系统账户设定180天的有效期设置.例如以下图所看到的: 当账户超过时间后会自己主动口令失效.而造成BI系统启动失败.无法正常訪问等相关问题,到时候又一次设置 ...

  6. H2数据库集群

    H2数据库集群 1. H2数据库简单介绍 1.1 H2数据库优势 经常使用的开源数据库:H2,Derby,HSQLDB.MySQL,PostgreSQL. 当中H2,HSQLDB相似,十分适合作为嵌入 ...

  7. 苦逼IT才能看懂的笑话

    这是苦逼IT才能看懂的笑话1.栈和队列的区别是啥? 吃多了拉就是队列:吃多了吐就是栈 2.世界上最遥远的距离不是生与死,而是你亲手制造的BUG就在你眼前,你却怎么都找不到她... 3.<c++程 ...

  8. spring 接收_header 作为get请求的httpheader

    今天项目遇到一个问题,我们项目用户验证和权限验证的信息(licence)是在http头中设置的,百度了一下,只有ajax才能设置头信息,form表单是无法设置的,但是我突然想起springMVC关于f ...

  9. Java内部类之间的闭包和回调详解

    前言 闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域.通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象(创建内部类的作用域)的信息,还自 ...

  10. Mapreduce实战:序列化与反序列化 int,int[],string[][]

    最新一期<中国IT产业发展报告>在2016中国(深圳)IT领袖峰会上正式发布,数字中国联合会常务理事李颖称.中国IT产业完毕了从要素驱动向效率驱动的过渡,眼下正在由效率驱动向创新驱动发展. ...