Mall電商項目總結(一)——項目概述


項目概述

此電商項目為本人學習項目,后端 使用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_$1 git@gitee.com:xwmall/backend.git    
echo "===========編譯並跳過單元測試===================="  
cd backend/mmallv4.0  
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.0/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 -9 java    
echo "================sleep 10s========================="  
for i in {1..10}  
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。則放回jedispooljedis實例肯定是可以用的。

 

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);

//忽略空Beanjson的錯誤,當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;

}

}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM