一、背景介紹
在C2C電商系統中,用戶可以申請成爲賣家,然後開設自己的店鋪。爲了更利於傳播,讓會員更容易記住,一般會設置店鋪的個性域名。如:良品鋪子在某貓的個性域名是:lppz.xxmall.com。
二、設計原理
在設計這套系統時,主要是通過設置域名的泛解析到二級或者三級域名,然後根據域名前綴,在程序中轉換成對應的店鋪id。從而完成個性化域名跳轉到對應的店鋪網站首頁。
三、程序實現
1. 設置域名泛解析
假設域名是:abc.com,設置所有匹配*.m.abc.com的三級域名解析到服務器上,如下圖:
2. 配置Nginx轉發
這裏將所有的域名,rewrite成指定的url前綴:/site/domain。domain代表*所匹配的三級域名,代碼如下:
server {
listen 80;
server_name *.m.abc.cn;
#設置靜態文件路徑,不轉發
location ~ ^/static/ {
root /webser/test/webapps/test-mobile;
}
#設置域名轉發到固定URL:/site/domain
location / {
set $subdomain default;
if ( $http_host ~* "^(.*)\.m\.abc\.cn"){
set $subdomain $1;
}
rewrite ^/(.*)$ /site/$subdomain/$1 break;
proxy_pass http://localhost:8088;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For
$proxy_add_x_forwarded_for;
}
}
3. 配置Controller
這裏使用Spring-mvc做爲mvc框架。首先定義一個Controller基類BaseController,所有的controller都繼承它。
public class BaseController {
}
再定義店鋪的Controller類SiteController,攔截所有URL包含/site/domain的請求。這樣配置的泛解析域名都會進入這個Controller
@Controller
@RequestMapping("/site/{domain}")
public class SiteController extends BaseController{
}
3.1 實現域名轉換成店鋪id
方法一:在進入SiteController的某個方法後,通過@PathVariable註解,得到domain,然後在數據庫中根據domain查詢出對於的店鋪id。
@Controller
@RequestMapping("/site/{domain}")
@Autowired
private SiteService siteService;
@RequestMapping("/index")
public String index(@PathVariable String domain){
//得到店鋪id
Site site = siteService.findSiteByDomain(domain);
}
}
這樣的寫法有個問題,就是每個方法都要寫這樣的一段代碼。假如我們可以在進入Controller某個方法前,就能通過domain得到店鋪id,那就更好了。那麼Spring-mvc中有沒有在進入某個Controller的方法前執行的註解呢?
方法二:在Spring-mvc的註解中,有個註解會在進入Controller每個方法前執行。這個註解就是@ModelAttribute。被@ModelAttribute註釋的方法會在此controller每個方法執行前被執行,因此對於一個controller映射多個URL的用法來說,要謹慎使用。
有了這個註解,我們可以在BaseController基類中,定義一個方法來實現域名轉換成店鋪id的操作。代碼如下:
public class BaseController {
//定義店鋪id
protected Long siteId = 0L;
@Autowired
private SiteService siteService;
/**
* @Title: getSiteId
* @Description: 將domain轉換成店鋪id
* @throws
*/
@ModelAttribute
public void getSiteId(@PathVariable String domain,Model model,HttpServletRequest request,HttpServletResponse response){
Site site = siteService.findSiteByDomain(domain);
if(site != null){
siteId = site.getId();
}else{
throw new RuntimeException("域名不存在");
}
}
}
在上面的代碼中,我們定義了一個成員變量siteId,即店鋪id,這樣在所有繼承BaseController的基類中,可以直接使用siteId來獲取店鋪的數據。
通過@ModelAttribute註解,在所有進入Controller方法前,將域名domain轉換成店鋪id即siteId。
這裏店鋪我是定義爲Site對象,然後直接從數據庫裏查詢得到。其實可以有優化的方案。通常店鋪的個性域名不會隨意更改,對於改動比較少的數據,我們可以在程序中緩存起來。所以可以將所有店鋪對象和domain對應關係,通過key-value的形式,存在內存map或者redis中,這樣獲取店鋪時,直接通過domain做爲key,就可以得到site對象了。
通過這幾步,即完成了類似淘寶店鋪設置個性化域名的功能。
PS:這裏只是提供了一種設計思路和簡單的實現,在併發量上千萬甚至更大的情況下,這塊會更加複雜。如需瞭解更多,請加我個人微信:jerry0914,請備註csdn博客。