日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
服務(wù)治理:實現(xiàn)服務(wù)的自動注冊與發(fā)現(xiàn)

硬編碼的問題

(1)如果用戶微服務(wù)和商品微服務(wù)的IP地址或者端口號發(fā)生了變化,則訂單微服務(wù)將變得不可用,需要對同步修改訂單微服務(wù)中調(diào)用用戶微服務(wù)和商品微服務(wù)的IP地址和端口號。

雨花網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)公司,雨花網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為雨花上千余家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請找那個售后服務(wù)好的雨花做網(wǎng)站的公司定做!

(2)如果系統(tǒng)中提供了多個用戶微服務(wù)和商品微服務(wù),則無法實現(xiàn)微服務(wù)的負載均衡功能。

(3)如果系統(tǒng)需要支持更高的并發(fā),需要部署更多的用戶微服務(wù)和商品微服務(wù)以及訂單微服務(wù),如果將用戶微服務(wù)和商品微服務(wù)的IP地址和端口硬編碼到訂單微服務(wù),則后續(xù)的維護會變得異常復(fù)雜。

所以,在微服務(wù)開發(fā)的過程中,需要引入服務(wù)治理功能,實現(xiàn)微服務(wù)之間的動態(tài)注冊與發(fā)現(xiàn)。

服務(wù)治理

如果系統(tǒng)采用了微服務(wù)的架構(gòu)模式,隨著微服務(wù)數(shù)量的不斷增多,服務(wù)之間的調(diào)用關(guān)系會變得縱橫交錯,以純?nèi)斯な謩拥姆绞絹砉芾磉@些微服務(wù)以及微服務(wù)之間的調(diào)用關(guān)系是及其復(fù)雜的,也是極度不可取的。

所以,需要引入服務(wù)治理的功能。服務(wù)治理也是在微服務(wù)架構(gòu)模式下的一種最核心和最基本的模塊,主要用來實現(xiàn)各個微服務(wù)的自動注冊與發(fā)現(xiàn)。

引入服務(wù)治理后,微服務(wù)項目總體上可以分為三個大的模塊:服務(wù)提供者、服務(wù)消費者和注冊中心,三者的關(guān)系如下圖所示。

(1)服務(wù)提供者會將自身提供的服務(wù)注冊到注冊中心,并向注冊中心發(fā)送心跳信息來證明自己還存活,其中,心跳信息中就會包含服務(wù)提供者自身提供的服務(wù)信息。

(2)注冊中心會存儲服務(wù)提供者上報的信息,并通過服務(wù)提供者發(fā)送的心跳來更新服務(wù)提供者最后的存活時間,如果超過一段時間沒有收到服務(wù)提供者上報的心跳信息,則注冊中心會認為服務(wù)提供者不可用,會將對應(yīng)的服務(wù)提供者從服務(wù)列表中剔除。

(3)服務(wù)消費者會向注冊中心訂閱自身監(jiān)聽的服務(wù),注冊中心會保存服務(wù)消費者的信息,也會向服務(wù)消費者推送服務(wù)提供者的信息。

(4)服務(wù)消費者從注冊中心獲取到服務(wù)提供者的信息時,會直接調(diào)用服務(wù)提供者的接口來實現(xiàn)遠程調(diào)用。

這里需要注意的是:服務(wù)消費者一般會從注冊中心中獲取到所有服務(wù)提供者的信息,根據(jù)具體情況實現(xiàn)對具體服務(wù)提供者的實例進行訪問。

注冊中心

從上面的分析可以看出,微服務(wù)實現(xiàn)服務(wù)治理的關(guān)鍵就是引入了注冊中心,它是微服務(wù)架構(gòu)模式下一個非常重要的組件,主要實現(xiàn)了服務(wù)注冊與發(fā)現(xiàn),服務(wù)配置和服務(wù)的健康檢測等功能。

服務(wù)注冊與發(fā)現(xiàn)

(1)服務(wù)注冊:注冊中心提供保存服務(wù)提供者和服務(wù)消費者的相關(guān)信息。

(2)服務(wù)發(fā)現(xiàn):也可以理解為服務(wù)訂閱,服務(wù)調(diào)用者也就是服務(wù)消費者,向注冊中心訂閱服務(wù)提供者的信息,注冊中心會向服務(wù)消費者推送服務(wù)提供者的信息。

服務(wù)配置

(1)配置訂閱:服務(wù)的提供者和消費者都可以向注冊中心訂閱微服務(wù)相關(guān)的配置信息。

(2)配置下發(fā):注冊中心能夠?qū)⑽⒎?wù)相關(guān)的配置信息主動推送給服務(wù)的提供者和消費者。

服務(wù)健康檢測

注冊中心會定期檢測存儲的服務(wù)列表中服務(wù)提供者的健康狀況,例如服務(wù)提供者超過一定的時間沒有上報心跳信息,則注冊中心會認為對應(yīng)的服務(wù)提供者不可用,就會將服務(wù)提供者踢出服務(wù)列表。

常見的注冊中心

能夠?qū)崿F(xiàn)注冊中心功能的組件有很多,但是常用的組件大概包含:Zookeeper、Eureka、Consul、Etcd、Nacos等。這里,就給大家簡單介紹下這些能夠?qū)崿F(xiàn)注冊中心功能的框架或組件。

(1)Zookeeper

接觸過分布式或者大數(shù)據(jù)開發(fā)的小伙伴應(yīng)該都知道,Zookeeper是Apache Hadoop的一個子項目,它是一個分布式服務(wù)治理框架,主要用來解決應(yīng)用開發(fā)中遇到的一些數(shù)據(jù)管理問題,例如:分布式集群管理、元數(shù)據(jù)管理、分布式配置管理、狀態(tài)同步和統(tǒng)一命名管理等。在高并發(fā)環(huán)境下,也可以通過Zookeeper實現(xiàn)分布式鎖功能。

(2)Eureka

Eureka是Netflix開源的SpringCloud中支持服務(wù)注冊與發(fā)現(xiàn)的組件,但是后來閉源了。

(3)Consul

Consul 是 HashiCorp 公司推出的開源產(chǎn)品,用于實現(xiàn)分布式系統(tǒng)的服務(wù)發(fā)現(xiàn)、服務(wù)隔離、服務(wù)配置,這些功能中的每一個都可以根據(jù)需要單獨使用,也可以同時使用所有功能。

(4)Etcd

etcd 是一個高度一致的分布式鍵值存儲,它提供了一種可靠的方式來存儲需要由分布式系統(tǒng)或機器集群訪問的數(shù)據(jù)。它可以優(yōu)雅地處理網(wǎng)絡(luò)分區(qū)期間的領(lǐng)導(dǎo)者選舉,即使在領(lǐng)導(dǎo)者節(jié)點中也可以容忍機器故障。

(5)Nacos

這里,我們重點說下Nacos。Nacos是阿里巴巴開源的一款更易于構(gòu)建云原生應(yīng)用的支持動態(tài)服務(wù)發(fā)現(xiàn)、配置管理和服務(wù)管理的平臺,其提供了一組簡單易用的特性集,能夠快速實現(xiàn)動態(tài)服務(wù)發(fā)現(xiàn)、服務(wù)配置、服務(wù)元數(shù)據(jù)及流量管理,主要如下所示。

  • 服務(wù)注冊:Nacos Client會通過發(fā)送REST請求的方式向Nacos Server注冊自己的服務(wù),提供自身的元數(shù)據(jù),比如IP地址、端口等信 息。Nacos Server接收到注冊請求后,就會把這些元數(shù)據(jù)信息存儲在一個雙層的內(nèi)存Map中。
  • 服務(wù)心跳:在服務(wù)注冊后,Nacos Client會維護一個定時心跳來持續(xù)通知Nacos Server,說明服務(wù)一直處于可用狀態(tài),防止被剔除。默認5s發(fā)送一次心跳。
  • 服務(wù)健康檢查:Nacos Server會開啟一個定時任務(wù)用來檢查注冊服務(wù)實例的健康情況,對于超過15s沒有收到客戶端心跳的實例會將它的healthy屬性置為false(客戶端服務(wù)發(fā)現(xiàn)時不會發(fā)現(xiàn)),如果某個實例超過30秒沒有收到心跳,直接剔除該實例(被剔除的實例如果恢復(fù)發(fā)送心跳則會重新注冊)
  • 服務(wù)發(fā)現(xiàn):服務(wù)消費者(Nacos Client)在調(diào)用服務(wù)提供者的服務(wù)時,會發(fā)送一個REST請求給Nacos Server,獲取上面注冊的服務(wù)清 單,并且緩存在Nacos Client本地,同時會在Nacos Client本地開啟一個定時任務(wù)定時拉取服務(wù)端最新的注冊表信息更新到本地存。
  • 服務(wù)同步:Nacos Server集群之間會互相同步服務(wù)實例,用來保證服務(wù)信息的一致性。

這里,我們選用的注冊中心就是阿里巴巴開源的Nacos。

搭建Nacos環(huán)境

(1)到鏈接

https://github.com/alibaba/nacos/releases 下載Nacos的安裝包,我這里下載的安裝包為:nacos-server-1.4.3.zip。

(2)解壓Nacos安裝包,并在命令行進入到Nacos的bin目錄下執(zhí)行如下命令以單機的方式啟動Nacos。

startup.cmd -m standalone

注意:如果需要以單機的方式啟動Nacos,則需要添加 -m standalone 參數(shù),否則,Nacos會以集群的方式啟動。

(3)啟動Nacos之后,在瀏覽器中輸入鏈接http://localhost:8848/nacos 來訪問Nacos的管理界面,默認的用戶名和密碼都是Nacos,如下所示。

輸入用戶名和密碼進入Nacos的管理界面,如下所示。

這里,我們進入到Nacos的服務(wù)管理-服務(wù)列表菜單下,如下所示。

可以看到,在Nacos的服務(wù)管理-服務(wù)列表菜單下還沒有任何服務(wù),接下來,我們就對項目的代碼進行改造。

集成Nacos注冊中心

引入Nacos注冊中心時,我們需要對項目的代碼進行一定的改造,以便利用Nacos實現(xiàn)服務(wù)的注冊與發(fā)現(xiàn)功能。

改造用戶微服務(wù)

(1)在用戶微服務(wù)的pom.xml文件中添加nacos的服務(wù)注冊與發(fā)現(xiàn)依賴,如下所示。


com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery

(2)在用戶微服務(wù)的resources目錄下的application.yml文件中添加Nacos注冊中心的服務(wù)地址配置,如下所示。

spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848

(3)在用戶微服務(wù)的啟動類io.binghe.shop#UserStarter上標(biāo)注@EnableDiscoveryClient注解,如下所示。

/**
* @author binghe
* @version 1.0.0
* @description 啟動用戶服的類
*/
@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(value = { "io.binghe.shop.user.mapper" })
@EnableDiscoveryClient
public class UserStarter {

public static void main(String[] args){
SpringApplication.run(UserStarter.class, args);
}
}

此時,就完成了對用戶微服務(wù)的代碼改造。

(4)啟動用戶微服務(wù),并刷新Nacos頁面,如下所示。

可以看到,用戶微服務(wù)已經(jīng)成功注冊到Nacos中。

改造其他微服務(wù)

我們可以用同樣的方式來改造商品微服務(wù)和訂單微服務(wù)的代碼,改造好之后,分別啟動商品微服務(wù)和訂單微服務(wù),并再次刷新Nacos的頁面,如下所示。

可以看到,用戶微服務(wù)、商品微服務(wù)和訂單微服務(wù)都已成功注冊到Nacos。

實現(xiàn)服務(wù)發(fā)現(xiàn)

按照整個項目的執(zhí)行流程,用戶執(zhí)行下單操作時,訂單微服務(wù)會調(diào)用用戶微服務(wù)的接口獲取用戶的基本信息,會調(diào)用商品微服務(wù)的接口獲取商品的基本信息。

在訂單微服務(wù)中校驗用戶的合法性和校驗商品庫存是否充足,如果用戶合法并且商品庫存充足,就會向訂單數(shù)據(jù)表中記錄訂單信息并調(diào)用商品微服務(wù)的接口來扣減商品的庫存。

用戶微服務(wù)和商品微服務(wù)作為服務(wù)的提供者,而訂單微服務(wù)作為服務(wù)的消費者,如果要實現(xiàn)服務(wù)的發(fā)現(xiàn)功能,我們還需要對訂單微服務(wù)的代碼進行改造。將訂單微服務(wù)中硬編碼的用戶微服務(wù)和商品微服務(wù)的IP地址和端口號修改成從Nacos中獲取。

為了讓小伙伴們能夠更好的對比修改前和修改后的代碼,這里,并沒有在訂單微服務(wù)的 io.binghe.shop.order.service.impl#OrderServiceImpl 類上直接修改,還是將其重命名為 io.binghe.shop.order.service.impl.OrderServiceV1Impl 類,同時,再次將其復(fù)制一份并命名為io.binghe.shop.order.service.impl.OrderServiceV2Impl類,在后續(xù)的開發(fā)過程中,如果涉及到大的代碼變動,都會以這種方式進行更新。

注入服務(wù)發(fā)現(xiàn)類

(1)在io.binghe.shop.order.service.impl.OrderServiceV2Impl 類中首先注入DiscoveryClient類的對象,如下所示。

@Autowired
private DiscoveryClient discoveryClient;

創(chuàng)建動態(tài)服務(wù)地址方法

在io.binghe.shop.order.service.impl.OrderServiceV2Impl 類中創(chuàng)建一個從Nacos中通過服務(wù)名稱獲取IP和端口號的方法getServiceUrl(),并在getServiceUrl()方法中將IP和端口號拼接成IP:PORT的形式,如下所示。

private String getServiceUrl(String serviceName){
ServiceInstance serviceInstance = discoveryClient.getInstances(serviceName).get(0);
return serviceInstance.getHost() + ":" + serviceInstance.getPort();
}

具體的實現(xiàn)方式就是調(diào)用DiscoveryClient對象的getInstances()方法,并傳入服務(wù)的名稱,從Nacos注冊中心中獲取一個ServiceInstance類型的List集合,從List集合中獲取第1個元素,也就是從List集合中獲取到一個ServiceInstance對象,從ServiceInstance對象中獲取到IP地址和端口號,并將其拼接成IP:PORT的形式。

定義服務(wù)提供者名稱

在io.binghe.shop.order.service.impl.OrderServiceV2Impl 類中定義兩個成員變量userServer和productServer,表示用戶微服務(wù)和商品微服務(wù)的服務(wù)名稱,并將其分別復(fù)制為server-user和server-product。

private String userServer = "server-user";
private String productServer = "server-product";

注意:userServer的值需要與用戶微服務(wù)下的application.yml文件中的如下配置的值相同。

spring:
application:
name: server-user

productServer的值需要與商品微服務(wù)下的application.yml文件中的如下配置的值相同。

spring:
application:
name: server-product

修改提交訂單邏輯

在io.binghe.shop.order.service.impl.OrderServiceV2Impl 類的saveOrder()方法中,將硬編碼的用戶微服務(wù)和商品微服務(wù)的IP和端口修改成從Nacos注冊中心中獲取,涉及改動的代碼片段如下所示。

(1)添加獲取用戶微服務(wù)與商品微服務(wù)的IP和端口號的代碼片段,如下所示。

//從Nacos服務(wù)中獲取用戶服務(wù)與商品服務(wù)的地址
String userUrl = this.getServiceUrl(userServer);
String productUrl = this.getServiceUrl(productServer);

(2)修改使用restTemplate獲取用戶信息的代碼片段,修改前的代碼片段如下所示。

User user = restTemplate.getForObject("http://localhost:8060/user/get/" + orderParams.getUserId(), User.class);
if (user == null){
throw new RuntimeException("未獲取到用戶信息: " + JSONObject.toJSONString(orderParams));
}

修改后的代碼片段如下所示。

User user = restTemplate.getForObject("http://" + userUrl + "/user/get/" + orderParams.getUserId(), User.class);
if (user == null){
throw new RuntimeException("未獲取到用戶信息: " + JSONObject.toJSONString(orderParams));
}

可以看到,訂單微服務(wù)獲取用戶微服務(wù)信息時,不再是硬編碼用戶微服務(wù)的IP地址和端口號了。

(3)修改使用restTemplate獲取商品信息的代碼片段,修改前的代碼片段如下所示。

Product product = restTemplate.getForObject("http://localhost:8070/product/get/" + orderParams.getProductId(), Product.class);
if (product == null){
throw new RuntimeException("未獲取到商品信息: " + JSONObject.toJSONString(orderParams));
}

修改后的代碼片段如下所示。

Product product = restTemplate.getForObject("http://" + productUrl + "/product/get/" + orderParams.getProductId(), Product.class);
if (product == null){
throw new RuntimeException("未獲取到商品信息: " + JSONObject.toJSONString(orderParams));
}

可以看到,訂單微服務(wù)獲取商品微服務(wù)信息時,不再是硬編碼商品微服務(wù)的IP地址和端口號了。

(4)修改使用restTemplate扣減商品庫存的代碼片段,修改前的代碼片段如下所示。

Result result = restTemplate.getForObject("http://localhost:8070/product/update_count/" + orderParams.getProductId() + "/" + orderParams.getCount(), Result.class);
if (result.getCode() != HttpCode.SUCCESS){
throw new RuntimeException("庫存扣減失敗");
}

修改后的代碼片段如下所示。

Result result = restTemplate.getForObject("http://" + productUrl + "/product/update_count/" + orderParams.getProductId() + "/" + orderParams.getCount(), Result.class);
if (result.getCode() != HttpCode.SUCCESS){
throw new RuntimeException("庫存扣減失敗");
}

可以看到,訂單微服務(wù)調(diào)用商品微服務(wù)的扣減商品庫存接口時,不再是硬編碼商品微服務(wù)的IP地址和端口號了。

注意:修改后的io.binghe.shop.order.service.impl.OrderServiceV2Impl 類的完整源碼,小伙伴們可自行查看項目代碼,冰河在這里不再贅述。

至此,整個項目就改造完成了。接下來,我們進行測試。

測試項目

開發(fā)完成后,我們對快速搭建并開發(fā)完成的三大微服務(wù)進行簡單的測試,在測試之前我們需要先在數(shù)據(jù)表中添加一些測試數(shù)據(jù)。

添加測試數(shù)據(jù)

(1)在用戶表中添加一條id為1001的記錄,如下所示。

INSERT INTO `shop`.`t_user`(`id`, `t_username`, `t_password`, `t_phone`, `t_address`) VALUES (1001, 'binghe', 'c26be8aaf53b15054896983b43eb6a65', '13212345678', '北京');

(2)在商品數(shù)據(jù)表中添加幾條商品記錄,如下所示。

INSERT INTO `shop`.`t_product`(`id`, `t_pro_name`, `t_pro_price`, `t_pro_stock`) VALUES (1001, '華為', 2399.00, 100);
INSERT INTO `shop`.`t_product`(`id`, `t_pro_name`, `t_pro_price`, `t_pro_stock`) VALUES (1002, '小米', 1999.00, 100);
INSERT INTO `shop`.`t_product`(`id`, `t_pro_name`, `t_pro_price`, `t_pro_stock`) VALUES (1003, 'iphone', 4999.00, 100);

測試庫存不足的情況

(1)分別啟動用戶微服務(wù)、商品微服務(wù)和訂單微服務(wù)。

(2)查詢id為1001的商品信息,如下所示。

mysql> select * from t_product where id = 1001;
+------+------------+-------------+-------------+
| id | t_pro_name | t_pro_price | t_pro_stock |
+------+------------+-------------+-------------+
| 1001 | 華為 | 2399.00 | 100 |
+------+------------+-------------+-------------+
1 row in set (0.00 sec)

可以看到,id為1001的商品的庫存為100。

(3)查詢訂單表和訂單條目表中的數(shù)據(jù),如下所示。

  • 查詢訂單表
mysql> select * from t_order;
Empty set (0.00 sec)

可以看到,訂單數(shù)據(jù)表的數(shù)據(jù)為空。

  • 查詢訂單條目表
mysql> select * from t_order_item;
Empty set (0.00 sec)

可以看到,訂單條目數(shù)據(jù)表的數(shù)據(jù)為空。

(4)在瀏覽器中調(diào)用訂單微服務(wù)的下單接口,傳入的商品數(shù)量為1001,如下所示。

可以看到,返回的信息中,code為500,codeMsg輸出的信息為執(zhí)行失敗,data返回的結(jié)果為商品庫存不足,并且輸出了提交的參數(shù)信息。

(5)再次查詢id為1001的商品信息,如下所示。

mysql> select * from t_product where id = 1001;
+------+------------+-------------+-------------+
| id | t_pro_name | t_pro_price | t_pro_stock |
+------+------------+-------------+-------------+
| 1001 | 華為 | 2399.00 | 100 |
+------+------------+-------------+-------------+
1 row in set (0.00 sec)

可以看到,商品id為1001的商品庫存仍為100,并沒有減少。

(6)再次查詢訂單表和訂單條目表中的數(shù)據(jù),如下所示。

  • 查詢訂單表
mysql> select * from t_order;
Empty set (0.00 sec)

可以看到,訂單數(shù)據(jù)表的數(shù)據(jù)為空。

  • 查詢訂單條目表
mysql> select * from t_order_item;
Empty set (0.00 sec)

可以看到,訂單條目數(shù)據(jù)表的數(shù)據(jù)為空。

綜上,當(dāng)提交訂單時傳入的商品數(shù)量大于商品的庫存數(shù)量時,系統(tǒng)會拋出異常,并不會執(zhí)行提交訂單和扣減庫存的操作。

測試正常下單的情況

(1)在測試庫存不足的情況的基礎(chǔ)上,我們將調(diào)用提交訂單的接口時傳入的商品數(shù)量修改為10,如下所示。

可以看到,當(dāng)商品庫存充足時,調(diào)用訂單微服務(wù)的下單接口,返回的數(shù)據(jù)為success表示下單成功。

(2)再次查詢id為1001的商品信息,如下所示。

mysql> select * from t_product where id = 1001;
+------+------------+-------------+-------------+
| id | t_pro_name | t_pro_price | t_pro_stock |
+------+------------+-------------+-------------+
| 1001 | 華為 | 2399.00 | 90 |
+------+------------+-------------+-------------+
1 row in set (0.00 sec)

可以看到,id為1001的商品庫存由原來的100變更為90,減少了10個庫存。

(3)再次查詢訂單表和訂單條目表中的數(shù)據(jù),如下所示。

  • 查詢訂單表
mysql> select * from t_order;
+------------------+-----------+-------------+-------------+-----------+---------------+
| id | t_user_id | t_user_name | t_phone | t_address | t_total_price |
+------------------+-----------+-------------+-------------+-----------+---------------+
| 3270016896208896 | 1001 | binghe | 13212345678 | 北京 | 23990.00 |
+------------------+-----------+-------------+-------------+-----------+---------------+
1 row in set (0.00 sec)

可以看到,訂單數(shù)據(jù)表中成功記錄了訂單的信息

  • 查詢訂單條目表
mysql> select * from t_order_item;
+------------------+------------------+----------+------------+-------------+----------+
| id | t_order_id | t_pro_id | t_pro_name | t_pro_price | t_number |
+------------------+------------------+----------+------------+-------------+----------+
| 3270017277890560 | 3270016896208896 | 1001 | 華為 | 2399.00 | 10 |
+------------------+------------------+----------+------------+-------------+----------+
1 row in set (0.00 sec)

可以看到,訂單條目數(shù)據(jù)表中成功記錄了訂單條目的信息。

至此,項目的測試完畢。


本文題目:服務(wù)治理:實現(xiàn)服務(wù)的自動注冊與發(fā)現(xiàn)
分享網(wǎng)址:http://www.dlmjj.cn/article/djhjgco.html