溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

SpringCloud服務(wù)發(fā)現(xiàn)注冊(cè)Eureka +Ribbon + Feign

發(fā)布時(shí)間:2020-07-26 00:25:01 來(lái)源:網(wǎng)絡(luò) 閱讀:405 作者:慕容千羽 欄目:編程語(yǔ)言

 在本期將學(xué)習(xí)以下知識(shí)點(diǎn):

  • 什么是服務(wù)注冊(cè)和發(fā)現(xiàn)?
  • 基于Eureka的注冊(cè)服務(wù)器
  • 服務(wù)生產(chǎn)者
  • 結(jié)合Ribbon服務(wù)消費(fèi)者
  • 結(jié)合Feign的服務(wù)生產(chǎn)者和消費(fèi)者

什么是服務(wù)注冊(cè)和發(fā)現(xiàn)

順便給大家推薦一個(gè)Java技術(shù)交流群:908676731,里面會(huì)分享一些資深架構(gòu)師錄制的視頻資料:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識(shí)體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多!

   假設(shè)有2個(gè)微服務(wù)A和B分別在端點(diǎn)http:// localhost:8181 /和http:// localhost:8282 /上運(yùn)行,如果想要在A服務(wù)中調(diào)用B服務(wù),那么我們需要在A服務(wù)中鍵入B服務(wù)的url,這個(gè)url是負(fù)載均衡器分配給我們的,包括負(fù)載平衡后的IP地址,那么很顯然,B服務(wù)與這個(gè)URL硬編碼耦合在一起了,如果我們使用了服務(wù)自動(dòng)注冊(cè)機(jī)制,就可以使用B服務(wù)的邏輯ID,而不是使用特定IP地址和端口號(hào)來(lái)調(diào)用服務(wù)。

   我們可以使用Netflix Eureka Server創(chuàng)建Service Registry服務(wù)器,并將我們的微服務(wù)同時(shí)作為Eureka客戶(hù)端,這樣一旦我們啟動(dòng)微服務(wù),它將自動(dòng)使用邏輯服務(wù)ID向Eureka Server注冊(cè)。然后,其他微服務(wù)(同樣也是Eureka客戶(hù)端)就可以使用服邏輯務(wù)ID來(lái)調(diào)用REST端點(diǎn)服務(wù)了。

   Spring Cloud使用Load Balanced RestTemplate創(chuàng)建Service Registry并發(fā)現(xiàn)其他服務(wù)變得非常容易。

   除了使用Netflix Eureka Server作為服務(wù)發(fā)現(xiàn),也可以使用Zookeeper,但是根據(jù)CAP定理,在需要P網(wǎng)絡(luò)分區(qū)容忍性情況下,強(qiáng)一致性C和高可用性A只能選擇一個(gè),Zookeeper是屬于CP,而Eureka是屬于AP,在服務(wù)發(fā)現(xiàn)方面,高可用性才是更重要,否則無(wú)法完成服務(wù)之間調(diào)用,而服務(wù)信息是否一致則不是最重要,A服務(wù)發(fā)現(xiàn)B服務(wù)時(shí),B服務(wù)信息沒(méi)有及時(shí)更新,可能發(fā)生調(diào)用錯(cuò)誤,但是調(diào)用錯(cuò)誤總比無(wú)法連接到服務(wù)注冊(cè)中心要強(qiáng)。否則,服務(wù)注冊(cè)中心就成為整個(gè)系統(tǒng)的單點(diǎn)故障,存在極大的單點(diǎn)風(fēng)險(xiǎn),這是我們?yōu)槭裁葱枰植际较到y(tǒng)的首要原因。

基于Eureka的注冊(cè)服務(wù)器

  讓我們使用Netflix Eureka創(chuàng)建一個(gè)Service Registry,它只是一個(gè)帶有Eureka Server啟動(dòng)器的SpringBoot應(yīng)用程序。

  使用Intellij的Idea開(kāi)發(fā)工具是非常容易啟動(dòng)Spring cloud的:

SpringCloud服務(wù)發(fā)現(xiàn)注冊(cè)Eureka +Ribbon + Feign

可以從https://start.spring.io/網(wǎng)址,選擇相應(yīng)組件即可。

由于我們需要建立一個(gè)注冊(cè)服務(wù)器,因此選擇Eureka Server組件即可,通過(guò)這些自動(dòng)工具實(shí)際上是能自動(dòng)生成Maven的配置:

<dependency>

????<groupId>org.springframework.cloud</groupId>

????<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>

</dependency>

我們需要給SpringBoot啟動(dòng)類(lèi)添加@EnableEurekaServer注釋?zhuān)允刮覀兊腟pringBoot應(yīng)用程序成為基于Eureka Server的Service Registry。

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer

@SpringBootApplication

public class ServiceRegistryApplication {

????public static void main(String[] args) {

????????SpringApplication.run(ServiceRegistryApplication.class, args);

????}

}

默認(rèn)情況下,每個(gè)Eureka服務(wù)器也是Eureka客戶(hù)端,客戶(hù)端一定會(huì)需要一個(gè)服務(wù)器URL來(lái)定位,否則就會(huì)不斷報(bào)錯(cuò),由于我們只有一個(gè)Eureka Server節(jié)點(diǎn)(獨(dú)立模式),我們將通過(guò)在application.properties文件中配置以下屬性來(lái)禁用此客戶(hù)端行為。

SpringCloud有properties和YAML兩種配置方式,這兩種配置方式其實(shí)只是形式不同,properties配置信息格式是a.b.c,而YAML則是a:b:c:,兩者本質(zhì)是一樣的,只需要其中一個(gè)即可,這里以properties為案例:

spring.application.name=jdon-eureka-server
server.port=1111
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

現(xiàn)在運(yùn)行ServiceRegistryApplication并訪(fǎng)問(wèn)http:// localhost:1111,如果不能訪(fǎng)問(wèn),說(shuō)明沒(méi)有正常啟動(dòng),請(qǐng)檢查三個(gè)環(huán)節(jié):pom.xml是否配置正確?需要Eureka和配置

SpringBoot的注釋@EnableEurekaServer是否增加了?

最后,application.properties是否配置?

SpringCloud其實(shí)非常簡(jiǎn)單,約定大于配置,默認(rèn)只要配置服務(wù)器端口就可以了,然后是一條注釋@EnableEurekaServer,就能啟動(dòng)Eurek服務(wù)器了。

服務(wù)器準(zhǔn)備好后,我們就要準(zhǔn)備服務(wù)生產(chǎn)者,向服務(wù)器里面注冊(cè)自己,服務(wù)消費(fèi)者則是從服務(wù)器中發(fā)現(xiàn)注冊(cè)的服務(wù)然后調(diào)用。

服務(wù)生產(chǎn)者

   服務(wù)生產(chǎn)者其實(shí)首先是Eureka的客戶(hù)端,生產(chǎn)者將自己注冊(cè)到前面啟動(dòng)的服務(wù)器當(dāng)中,引如果是idea的導(dǎo)航,選擇CloudDiscovery的EurekaDiscovery,如果是 Maven則引入包依賴(lài)是:

<dependency>

????<groupId>org.springframework.cloud</groupId>

????<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

</dependency>

這樣,spring-cloud-starter-netflix-eureka-client這個(gè)jar包就放入我們系統(tǒng)的classpath,為了能夠正常使用這個(gè)jar包,還需要配置,只需要在application.properties中配置eureka.client.service-url.defaultZone屬性即可自動(dòng)注冊(cè)Eureka Server:

eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

當(dāng)我們的服務(wù)在Eureka Server注冊(cè)時(shí),它會(huì)持續(xù)發(fā)送一定時(shí)間間隔的心跳。如果Eureka服務(wù)器沒(méi)有從任何服務(wù)的實(shí)例接收到心跳,它將認(rèn)為這個(gè)服務(wù)實(shí)例已經(jīng)關(guān)閉并從自己的池中剔除它。

以上是服務(wù)生產(chǎn)者注冊(cè)服務(wù)的過(guò)程,比較簡(jiǎn)單,為了使我們的服務(wù)生產(chǎn)者能的演示代碼夠運(yùn)行起來(lái),我們還需要新建一個(gè)服務(wù)生產(chǎn)者代碼:

@RestController
public class ProducerService {

    @GetMapping("/pengproducer")
    public String sayHello(){
        return "hello world";
    }
}

這段代碼是將服務(wù)暴露成RESTful接口,@RestController是聲明Rest接口,/pengproducer是REST的訪(fǎng)問(wèn)url,通過(guò)get方式能夠獲得字符串:hello world

因?yàn)镽EST屬于WEB的一種接口,因此需要在pom.xml中引入Web包:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

然后在application.properties中加入有關(guān)REST接口的配置:

spring.application.name=PengProducerService
server.port=2111

指定我們的生產(chǎn)者服務(wù)的名稱(chēng)是PengProducerService,REST端口開(kāi)在2111。

現(xiàn)在可以在idea中啟動(dòng)我們的應(yīng)用了,這樣我們啟動(dòng)這個(gè)項(xiàng)目,就可以在http://127.0.0.1:2111/ 訪(fǎng)問(wèn)這個(gè)REST服務(wù)。同時(shí),因?yàn)槲覀冎耙呀?jīng)啟動(dòng)了注冊(cè)服務(wù)器,訪(fǎng)問(wèn)http://localhost:1111/你會(huì)發(fā)現(xiàn)PengProducerService出現(xiàn)在服務(wù)列表中:

SpringCloud服務(wù)發(fā)現(xiàn)注冊(cè)Eureka +Ribbon + Feign

上面啟動(dòng)應(yīng)用服務(wù)是在idea編輯器中,我們還可以通過(guò)命令行啟動(dòng)我們的服務(wù)生產(chǎn)者:

java -jar -Dserver.port=2112 producer-0.0.1-SNAPSHOT.jar

這個(gè)是在端口2112開(kāi)啟我們的服務(wù)端點(diǎn)了。現(xiàn)在再問(wèn)http://localhost:1111/,你會(huì)看到可用節(jié)點(diǎn)Availability Zones下面已經(jīng)從(1)變?yōu)?2),現(xiàn)在我們的服務(wù)生產(chǎn)者已經(jīng)有兩個(gè)實(shí)例在運(yùn)行,當(dāng)服務(wù)的消費(fèi)者訪(fǎng)問(wèn)這個(gè)兩個(gè)實(shí)例時(shí),它可以根據(jù)負(fù)載平衡策略比如輪詢(xún)?cè)L問(wèn)其中一個(gè)服務(wù)生產(chǎn)者實(shí)例。

總結(jié)一下,為了讓服務(wù)生產(chǎn)者注冊(cè)到Euraka服務(wù)器中,只需要兩個(gè)步驟:

  • 1. 引入spring-cloud-starter-netflix-eureka-client包
  • 2. 配置Eurake服務(wù)器的地址

請(qǐng)注意,spring-cloud-starter-netflix-eureka-client包是Spring Cloud升級(jí)后最新的包名,原來(lái)是spring-cloud-starter-eureka,里面沒(méi)有netflix,這是過(guò)去版本,Spring Boot 1.5以后都是加入了netflix的,見(jiàn)Spring Cloud Edgware Release Notes

另外,這里不需要在SpringBoot主代碼中再加入@enablediscoveryclient 或 @enableeurekaclient,只要eureka的client包在maven中配置,也就會(huì)出現(xiàn)在系統(tǒng)的classpath中,這樣就會(huì)默認(rèn)自動(dòng)注冊(cè)到eureka服務(wù)器中了。

這部分×××:百度網(wǎng)盤(pán)。

下面我們準(zhǔn)備訪(fǎng)問(wèn)這個(gè)服務(wù)生產(chǎn)者PengProducerService的消費(fèi)者服務(wù):

結(jié)合Ribbon的服務(wù)消費(fèi)者

   上個(gè)章節(jié)我們已經(jīng)啟動(dòng)了兩個(gè)服務(wù)生產(chǎn)者實(shí)例,如何通過(guò)負(fù)載平衡從兩個(gè)中選擇一個(gè)訪(fǎng)問(wèn)呢?這時(shí)就需要Ribbon,為了使用Ribbon,我們需要使用@LoadBalanced元注解,那么這個(gè)注解放在哪里呢?一般有兩個(gè)DiscoveryClient 和 RestTemplate,這兩個(gè)的區(qū)別是:

1. DiscoveryClient可以獲得服務(wù)提供者(生產(chǎn)者)的多個(gè)實(shí)例集合,能讓你手工決定選擇哪個(gè)實(shí)例,這里負(fù)載平衡的策略比如round robin輪詢(xún)就不會(huì)派上,實(shí)際就沒(méi)有使用Ribbon:

List<ServiceInstance> instances=discoveryClient.getInstances("PengProducerService");
ServiceInstance serviceInstance=instances.get(0);

2.RestTemplate則是使用Ribbon的負(fù)載平衡策略,使用@LoadBalanced注釋resttemplate并使用zuul代理服務(wù)器作為邊緣服務(wù)器。那么對(duì)zuul邊緣服務(wù)器的任何請(qǐng)求將默認(rèn)使用Ribbon進(jìn)行負(fù)載平衡,而resttemplate將以循環(huán)方式路由請(qǐng)求。這部分代碼如下:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.client.RestTemplate;

@Controller
public class ConsumerService {

    @Autowired
    private RestTemplate restTemplate;

    public String callProducer() {
        ResponseEntity<String> result =
                this.restTemplate.getForEntity(
                        "http://PengProducerService/pengproducer",
                        String.class,
                        "");
        if (result.getStatusCode() == HttpStatus.OK) {
            System.out.printf(result.getBody() + " called in callProducer");
            return result.getBody();
        } else {
            System.out.printf(" is it empty");
            return " empty ";
        }
    }
}

RestTemplate是自動(dòng)注射進(jìn)這個(gè)控制器,在這控制器,我們調(diào)用了服務(wù)生產(chǎn)者h(yuǎn)ttp://PengProducerService/pengproducer,然后獲得其結(jié)構(gòu)。

這個(gè)控制器的調(diào)用我們可以在SpringBoot啟動(dòng)函數(shù)里調(diào)用:

@SpringBootApplication
public class ConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        ApplicationContext ctx  =  SpringApplication.run(ConsumerApplication
                .class, args);
        ConsumerService consumerService = ctx.getBean(ConsumerService.class);
        System.out.printf("final result RestTemplate=" + consumerService
                .callProducer() + " \n");
    }
}

注意到@LoadBalanced是標(biāo)注在RestTemplate上,而RestTemplate是被注入到ConsumerService中的,這樣通過(guò)調(diào)用RestTemplate對(duì)象實(shí)際就是獲得負(fù)載平衡后的服務(wù)實(shí)例。這個(gè)可以通過(guò)我們的服務(wù)提供者里面輸出hashcode來(lái)分辨出來(lái),啟動(dòng)兩個(gè)服務(wù)提供者實(shí)例,每次運(yùn)行ConsumerService,應(yīng)該是依次打印出不同的hashcode:

hello world1246528978 called in callProducerfinal result RestTemplate=hello world1246528978

再次運(yùn)行結(jié)果:

hello world1179769159 called in callProducerfinal result RestTemplate=hello world1179769159

hellow world后面的哈希值不同,可見(jiàn)是來(lái)自不同的服務(wù)提供者實(shí)例。

如果系統(tǒng)基于https進(jìn)行負(fù)載平衡,那么只需要兩個(gè)步驟:

1.application.properties中激活ribbon的https:

ribbon.IsSecure=true

2.代碼中RestTemplate初始化時(shí)傳入ClientHttpRequestFactory對(duì)象:

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    CloseableHttpClient httpClient = HttpClientUtil.getHttpClient();
    HttpComponentsClientHttpRequestFactory clientrequestFactory = new HttpComponentsClientHttpRequestFactory();
    clientrequestFactory.setHttpClient(httpClient);
    RestTemplate restTemplate = new RestTemplate(clientrequestFactory);
    return restTemplate;
}

這部分×××:百度網(wǎng)盤(pán)

結(jié)合Feign的服務(wù)生產(chǎn)者和消費(fèi)者

   上篇是使用Ribbon實(shí)現(xiàn)對(duì)多個(gè)服務(wù)生產(chǎn)者實(shí)例使用負(fù)載平衡的方式進(jìn)行消費(fèi),在調(diào)用服務(wù)生產(chǎn)者時(shí),返回的是字符串類(lèi)型,如果返回是各種自己定義的對(duì)象,這些對(duì)象傳遞到消費(fèi)端是通過(guò)JSON方式,那么我們的消費(fèi)者需要使用Feign來(lái)訪(fǎng)問(wèn)各種Json對(duì)象。

   需要注意的是:Feign = Eureka +Ribbon + RestTemplate,也就是說(shuō),使用Feign訪(fǎng)問(wèn)服務(wù)生產(chǎn)者,無(wú)需前面章節(jié)那么關(guān)于負(fù)載平衡的代碼了,前面我們使用RestTemplate進(jìn)行負(fù)載平衡訪(fǎng)問(wèn),代碼還是挺復(fù)雜  

   現(xiàn)在我們開(kāi)始Feign的實(shí)現(xiàn):首先我們?cè)诜?wù)的生產(chǎn)者那邊進(jìn)行修改,讓我們生產(chǎn)者項(xiàng)目變得接近實(shí)戰(zhàn)中項(xiàng)目,增加領(lǐng)域?qū)印⒎?wù)層和持久層。

   假設(shè)新增Article領(lǐng)域模型對(duì)象,我們就需要倉(cāng)儲(chǔ)保存,這里我們使用Spring默認(rèn)約定,使用JPA訪(fǎng)問(wèn)h3數(shù)據(jù)庫(kù),將Article通過(guò)JPA保存到h3數(shù)據(jù)庫(kù)中:

要啟用JPA和h3數(shù)據(jù)庫(kù),首先只要配置pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h3database</groupId>
    <artifactId>h3</artifactId>
    <scope>runtime</scope>
</dependency>

Article領(lǐng)域模型對(duì)象作為需要持久的實(shí)體對(duì)象:配置實(shí)體@Entity和@Id主鍵即可:

@Entity
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String title;
    private String body;
    private Date startDate;

然后我們建立一個(gè)空的Article倉(cāng)儲(chǔ)接口即可:

@Repository
public interface ArticleRep extends JpaRepository<Article,Long> {
}

這樣,關(guān)于Article的CRUD實(shí)現(xiàn)就已經(jīng)有了,不需要自己再編寫(xiě)任何SQL語(yǔ)句。這樣我們編寫(xiě)一個(gè)Service就可以提供Article對(duì)象的CRUD方法,這里只編寫(xiě)插入和查詢(xún)批量?jī)蓚€(gè)方法:

@Service
public class ArticleService {

    @Autowired
    ArticleRep articleRep;

    public List<Article> getAllArticles(){
        return articleRep.findAll();
    }

    public void insertArticle(Article article){
        articleRep.save(article);
    }
}

我們?cè)赗EST接口中暴露這兩種方法:

  • 1. get /articles是查詢(xún)所有對(duì)象
  • 2. post /article是新增

    @RestController
    public class ProducerService {
    
    @Autowired
    ArticleService articleService;
    
    @GetMapping("/articles")
    public List<Article> getAllArticles(){
        return articleService.getAllArticles();
    }
    
    @GetMapping("/article")
    public void publishArticle(@RequestBody Article article){
        articleService.insertArticle(article);
    }

上面服務(wù)的生產(chǎn)者提供了兩個(gè)REST url,我們?cè)谙M(fèi)者這邊使用/articles以獲得所有文章:

@FeignClient(name="PengProducerService")
public interface ConsumerService {

    @GetMapping("/articles")
    List<Article> getAllArticles();
}

這是我們消費(fèi)者的服務(wù),調(diào)用生產(chǎn)者 /articles,這是一個(gè)接口,無(wú)需實(shí)現(xiàn),注意需要標(biāo)注FeignClient,其中寫(xiě)入name或value微服務(wù)生產(chǎn)者的application.properties配置:

spring.application.name=PengProducerService

當(dāng)然,這里會(huì)直接耦合PengProducerService這個(gè)名稱(chēng),我們以后可以通過(guò)配置服務(wù)器更改,這是后話(huà)。

然后需要在應(yīng)用Application代碼加入@EnableFeignClients:

@SpringBootApplication
@EnableFeignClients
public class FeignconsumerApplication {

    public static void main(String[] args)  {
        ApplicationContext context = SpringApplication.run(FeignconsumerApplication
                        .class, args);
        ConsumerService consumerService = context.getBean(ConsumerService
                .class);
        System.out.printf("#############all articles ok" + consumerService
                .getAllArticles());
    }

在FeignconsumerApplication我們調(diào)用了前面接口ConsumerService,而ConsumerService則通過(guò)負(fù)載平衡調(diào)用另外一個(gè)生產(chǎn)者微服務(wù),如果我們給那個(gè)生產(chǎn)者服務(wù)加入一些Articles數(shù)據(jù),則這里就能返回這些數(shù)據(jù):

#############all articles ok[com.example.feignconsumer.domain.Article@62b475e2, com.example.feignconsumer.domain.Article@e9474f]

說(shuō)明調(diào)用成功。

在調(diào)試過(guò)程中,曾經(jīng)出現(xiàn)錯(cuò)誤:

Load balancer does not have available server for client:PengProducerService

經(jīng)常排查是由于生產(chǎn)者項(xiàng)目中pom.xml導(dǎo)入的是spring-cloud-starter-netflix-eureka-client,改為pring-cloud-starter-netflix-eureka-server就可以了,這是SpringBoot 2.0發(fā)現(xiàn)的一個(gè)問(wèn)題。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

本章的代碼下載:百度網(wǎng)盤(pán)

總結(jié)

   通過(guò)這個(gè)項(xiàng)目學(xué)習(xí),我們?nèi)缤Q絲剝繭層層搞清楚了Spring Cloud的微服務(wù)之間同步調(diào)用方式,發(fā)現(xiàn)基于REST/JSON的調(diào)用代碼最少,也是最方便,F(xiàn)eign封裝了Ribbon負(fù)載平衡和Eureka服務(wù)器訪(fǎng)問(wèn)以及REST格式處理。
順便給大家推薦一個(gè)Java技術(shù)交流群:908676731,里面會(huì)分享一些資深架構(gòu)師錄制的視頻資料:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識(shí)體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI