Spring Cloud

Spring Cloud Alibaba & H

1. 建表SQL

1.1 支付模塊

1.1.1 payment

create table payment(
    id varchar(64) not null,
    serial varchar(200) default '',
    primary key(id)
);

1.1.2


2. Mybatis-Plus-Generator

2.1 使用要求

  • 盡量不自己寫SQL
  • 使用plus提供的IService接口,通過門面IFacade接口調(diào)用
  • 業(yè)務(wù)邏輯在 FacadeImpl 實(shí)現(xiàn)類中寫,Iservice實(shí)現(xiàn)類剿吻,Mapper接口不屑任何東西
  • 數(shù)據(jù)庫實(shí)體對象是 XxxxxEntity
  • 輸入對象為 BO
  • 返回對象為VO

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

CAP理論

什么是CAP 拾弃?

  1. C : Consistency (強(qiáng)一致性)
  2. A : Availability (可用性)
  3. P : Partition tolerance (分區(qū)容錯(cuò)性)

Eureka , Zookeeper , Consul 比較

  • Eureka : AP
  • Consul : CP
  • Zookeeper : CP

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

  1. Eureka Server

提供服務(wù)注冊,各個(gè)服務(wù)啟動后會在Server中進(jìn)行注冊羡疗,服務(wù)注冊表將會存儲所有可用服務(wù)節(jié)點(diǎn)的信息染服,可以在界面中看到。

  1. Eureka Client

    通過注冊中心進(jìn)行訪問叨恨,客戶端內(nèi)置一個(gè)使用輪詢(round-robin)算法的負(fù)載均衡器柳刮。在應(yīng)用啟動后會向Server發(fā)送默認(rèn)周期為30秒的心跳包,如果Server在多個(gè)心跳周期內(nèi)沒有收到某個(gè)節(jié)點(diǎn)的心跳i特碳,Server會將該節(jié)點(diǎn)從服務(wù)注冊表中移除(一般默認(rèn)90秒诚亚,3個(gè)心跳周期)。

  2. Eureka 集群

    • 修改 C/Windows/System32/drivers/etc/hosts 配置

      127.0.0.1  eureka7001.com
      127.0.0.1  eureka7002.com
      
    • 改寫yml配置午乓,eureka server互相注冊

      • Eureka Server 7001

        server.port=7001
        spring.application.name=cloud-eureka-server7001
        
        eureka.instance.hostname=eureka7001.com
        # false 表示不想注冊中心注冊自己
        eureka.client.register-with-eureka=false
        # 表示我自己就是服務(wù)端站宗,不需要取檢索服務(wù)
        eureka.client.fetch-registry=false
        eureka.client.service-url.defaultZone=http://eureka7002.com:7002/eureka/
        
      • Eureka Server 7002

        server.port=7002
        spring.application.name=cloud-eureka-server7002
        
        eureka.instance.hostname=eureka7002.com
        # false 表示不想注冊中心注冊自己
        eureka.client.register-with-eureka=false
        # 表示我自己就是服務(wù)端,不需要取檢索服務(wù)
        eureka.client.fetch-registry=false
        eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka/
        
    • cloud-provider-payment 服務(wù)注冊到 Eureka Server 集群上

      eureka.client.register-with-eureka=true
      eureka.client.fetch-registry=true
      eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      
    • 支付模塊10010 和 10011 兩個(gè)服務(wù)注冊發(fā)現(xiàn)益愈,調(diào)用梢灭,服務(wù)名同一個(gè)

      • cloud-provider-payment10010

        server.port=10010
        spring.application.name=cloud-payment-service
        
      • cloud-provider-payment10011

        server.port=10011
        spring.application.name=cloud-payment-service
        
      • cloud-consumer-order 微服務(wù)單機(jī)版調(diào)用 cloud-provider-payment10010 服務(wù)

        public static final String PAYMENT_URL="http://localhost:10010";
        
            @Resource
            private RestTemplate restTemplate;
        
            @GetMapping("/consumer/payment/create/{serial}")
            public CommonResult<PaymentVO> create(@PathVariable("serial") String serial){
                return restTemplate.getForObject(PAYMENT_URL+"/payment/post/"+serial,CommonResult.class);
            }
        
      • 多個(gè)微服務(wù)調(diào)用模式:不能寫死服務(wù)請求地址夷家,可以通過服務(wù)名指定(多個(gè)微服務(wù)同一個(gè)服務(wù)名)

        @LoadBalanced 配置 RestTemplate 負(fù)載均衡:讓應(yīng)用知道如何調(diào)用多個(gè)微服務(wù)

        @Configuration
        public class ConsumerOrderConfig {
            @Bean
            @LoadBalanced
            public RestTemplate getRestTemplate(){
                return new RestTemplate();
            }
        }
        
        public static final String PAYMENT_URL="http://CLOUD-PAYMENT-SERVICE";
        
    • 微服務(wù)界面展示的服務(wù)名,IP信息配置修改

      # eureka 管理界面 STATUS 欄展示的名稱
      eureka.instance.instance-id=paymentService10010
      # 顯示IP地址(鼠標(biāo)浮動上去展示IP地址)
      eureka.instance.prefer-ip-address=true
      
  3. Eureka 服務(wù)發(fā)現(xiàn) Discovery

    • 添加組件

      /**
      * 注入服務(wù)發(fā)現(xiàn)組件
      * import org.springframework.cloud.client.discovery.DiscoveryClient;
      */
      @Resource
      private DiscoveryClient discoveryClient;
      
    • 模擬 服務(wù)發(fā)現(xiàn)敏释,打印獲取到的服務(wù)信息

      @GetMapping("/payment/discovery")
          public Object discovery(){
              // 獲取所有的微服務(wù)名
              List<String> services = discoveryClient.getServices();
              for (String service : services) {
                  log.info("service=",service);
              }
              // 獲取指定微服務(wù)名下的實(shí)例库快,如CLOUD-PAYMENT-SERVICE服務(wù)名下有兩個(gè)實(shí)例微服務(wù)
              // cloud-provider-payment10010,cloud-provider-payment10011
              List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
              for (ServiceInstance instance : instances) {
                  log.info("instance=",instance);
                  log.info(instance.getInstanceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
              }
              return discoveryClient;
          }
      
    • 在主啟動類上添加注解 @EnableDiscoveryClient , 開啟服務(wù)發(fā)現(xiàn)機(jī)制钥顽。

  4. Eureka 自我保護(hù)理論

    • 自我保護(hù)模式下义屏,服務(wù)一旦注冊進(jìn)服務(wù)注冊表后,掛掉的服務(wù)不會立刻清掉蜂大。屬于AP理論分支闽铐。

    • 如何禁止自我保護(hù)模式:

      1. Eureka-Server7001 的 yml配置

        # 關(guān)閉禁止自我保護(hù)模式
        eureka.server.enable-self-preservation=false
        eureka.server.eviction-interval-timer-in-ms=2000
        
      2. Eureka-Client :cloud-provider-payment10010 服務(wù)配置

        # 修改 Eureka 客戶端向服務(wù)端發(fā)送心跳的時(shí)間間隔(默認(rèn)30秒,改成1秒)
        eureka.instance.lease-renewal-interval-in-seconds=1
        # 收到最后一次心跳服務(wù)端等待的時(shí)間上限(默認(rèn)是90秒奶浦,改成2秒)
        eureka.instance.lease-expiration-duration-in-seconds=2
        
    • Eureka 目前很少使用

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

  1. zookeeper 是什么 兄墅?

    zookeeper是分布式協(xié)調(diào)工具,可以完成注冊中心的功能澳叉。

  2. zookeeper 安裝

    • centos7

    • zookeeper-3.4.9/bin

    • 基本操作

      # 關(guān)閉防火墻
      systemctl stop firewalld
      
  3. 服務(wù)提供者:新建 cloud-provider-payment10012模塊,引入zookeeper客戶端依賴

    <!--   引入zookeeper客戶端  -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            </dependency>
    
    如果提示 jar 包沖突錯(cuò)誤隙咸,排除掉apache自帶的zookeeper
    <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.zookeeper</groupId>
                        <artifactId>zookeeper</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
  4. 修改主啟動類注解

    @EnableDiscoveryClient 該注解用于向consul或zookeeper作為注冊中心時(shí)注冊服務(wù)使用

    @SpringBootApplication
    @MapperScan("com.dc.scloud.mapper")
    @EnableDiscoveryClient
    public class ProviderPaymentApplication10012 {
        public static void main(String[] args) {
            SpringApplication.run(ProviderPaymentApplication10012.class);
        }
    }
    
  5. 修改 properties 文件

    # 安裝 zookeeper工具的虛擬機(jī)IP
    spring.cloud.zookeeper.connect-string=192.168.111.144:2181
    
  6. 修改 controller

    @Slf4j
    @RestController
    public class PaymentController {
    
        @Autowired
        private IPaymentFacade iPaymentFacade;
    
        @Value("${server.port}")
        private String servicePort;
    
        @GetMapping("/payment/zk/list")
        public CommonResult<String> paymentWithZK(){
            String msg="spring cloud payment service ,port="+servicePort+ ", "+UUID.randomUUID().toString();
            CommonResult<String> stringCommonResult = new CommonResult<>(200,msg,msg);
            return stringCommonResult;
        }
    }
    
  7. 服務(wù)消費(fèi)者:cloud-consumerzk-order10008

    • 啟動類配置

      @SpringBootApplication
      @EnableDiscoveryClient
      public class ConsumerZKOrderApplication10008 {
          public static void main(String[] args) {
              SpringApplication.run(ConsumerZKOrderApplication10008.class);
          }
      }
      
    • controller

      @RestController
      @Slf4j
      public class OrderController {
      
          //public static final String PAYMENT_URL="http://localhost:10010";
          public static final String PAYMENT_URL="http://cloud-provider-payment10012";
      
          @Resource
          private RestTemplate restTemplate;
      
          @GetMapping("/consumer/payment/query/{id}")
          public CommonResult<PaymentVO> queryPayment(@PathVariable("id") String id){
             return restTemplate.getForObject(PAYMENT_URL+"/payment/zk/list",CommonResult.class);
          }
      }
      
    • zookeeper 中的服務(wù)名區(qū)分大小寫的

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

  1. 什么是 Consul ?

    consul是一套分布式服務(wù)發(fā)現(xiàn)和配置管理系統(tǒng)成洗,提供了服務(wù)之間的服務(wù)治理五督,配置中心,控制總線等泌枪。

  2. 服務(wù)提供者注冊進(jìn)consul:cloud-providerconsul-payment10016

    • pom.xml

      <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
              </dependency>
      
    • application.properties

      server.port=10016
      spring.application.name=cloud-payment-service10016
      
      # 單機(jī)
      spring.cloud.consul.host=localhost
      spring.cloud.consul.port=8500
      spring.cloud.consul.discovery.service-name=${spring.application.name}
      
    • controller

      @Slf4j
      @RestController
      public class PaymentController {
      
          @Value("${server.port}")
          private String servicePort;
      
          @GetMapping("/payment/consul/list")
          public CommonResult<String> paymentWithZK(){
              String msg="spring cloud payment service ,port="+servicePort+ ", "+UUID.randomUUID().toString();
              CommonResult<String> stringCommonResult = new CommonResult<>(200,msg,msg);
              return stringCommonResult;
          }
      }
      
    • 啟動類

      @SpringBootApplication
      @EnableDiscoveryClient
      public class ProviderConsulPayment10016 {
          public static void main(String[] args) {
              SpringApplication.run(ProviderConsulPayment10016.class);
          }
      }
      
  3. 服務(wù)消費(fèi)者注冊進(jìn)consul:cloud-consumerconsul-order10008

    • pom.xml

      <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
              </dependency>
      
    • properties

      server.port=10008
      spring.application.name=cloud-consumerconsul-order10008
      
      # 單機(jī)
      spring.cloud.consul.host=localhost
      spring.cloud.consul.port=8500
      spring.cloud.consul.discovery.service-name=${spring.application.name}
      
    • controller

      @RestController
      @Slf4j
      public class OrderController {
      
          //public static final String PAYMENT_URL="http://localhost:10010";
          public static final String PAYMENT_URL="http://cloud-payment-service10016";
      
          @Resource
          private RestTemplate restTemplate;
      
          @GetMapping("/consumer/consul/query")
          public CommonResult<PaymentVO> queryPayment(){
             return restTemplate.getForObject(PAYMENT_URL+"/payment/consul/list",CommonResult.class);
          }
      }
      
    • MainApplication.class

      @SpringBootApplication
      @EnableDiscoveryClient
      public class ProviderConsulPayment10016 {
          public static void main(String[] args) {
              SpringApplication.run(ProviderConsulPayment10016.class);
          }
      }
      

4. Ribbon 負(fù)載均衡 cloud-consumer-order

4.1 理論

  • Spring Cloud Ribbon 是基于 Netflix Ribbon 實(shí)現(xiàn)的一套客戶端負(fù)載均衡的工具概荷。提供客戶端的軟件負(fù)載均衡算法和服務(wù)調(diào)用。

  • LB 負(fù)載均衡(Load Balance)是什么碌燕?

    1. 將用戶的請求平攤到多個(gè)服務(wù)上误证,常見的負(fù)載軟件有:Nginx,LVS修壕;硬件F5等愈捅。

    2. Ribbon是本地負(fù)載均衡

      在調(diào)用微服務(wù)接口時(shí),會在注冊中心上獲取注冊信息服務(wù)列表緩存到JVM本地慈鸠,在本地實(shí)現(xiàn)RPC遠(yuǎn)程服務(wù)調(diào)用技術(shù)蓝谨。

    3. Nginx是服務(wù)端負(fù)載均衡

      所有的請求都交給Nginx,由Nginx實(shí)現(xiàn)請求轉(zhuǎn)發(fā)青团。

4.2 ribbon負(fù)載均衡和Rest調(diào)用

  1. spring-cloud-starter-netflix-eureka-client 已經(jīng)集成了 ribbon 依賴

  2. Ribbon 默認(rèn)時(shí) 輪詢機(jī)制

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

4.3 ribbon 替換負(fù)載規(guī)則

  1. Ribbon 規(guī)則替換配置類不能在主啟動類所在包及其子包路徑下譬巫。

  2. 新建包:package com.dc.myrule;

  3. 隨機(jī)算法:自定義配置類

    @Configuration
    public class MyRibbonRule {
        /**
         * 配置隨機(jī)算法進(jìn)行負(fù)載
         * @return
         */
        @Bean
        public IRule myRule(){
            return new RandomRule();
        }
    }
    
  4. @RibbonClient 主啟動類指定對某個(gè)微服務(wù)設(shè)置某種負(fù)載策略

    @SpringBootApplication
    @EnableEurekaClient
    @RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MyRibbonRule.class)
    public class ConsumerOrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerOrderApplication.class);
        }
    }
    

4.4 輪詢算法原理 RoundRobinRule

  1. 負(fù)載均衡算法:
    • rest接口第幾次請求數(shù) % 服務(wù)器集群總數(shù) = 實(shí)際調(diào)用服務(wù)器位置下標(biāo) ;
    • 每一次服務(wù)重啟后rest接口計(jì)數(shù)從1開始督笆;

5. OpenFeign

5.1 什么是 Feign 芦昔?

  1. Feign 是一個(gè)聲明式WebService客戶端。使用方法是:定義一個(gè)服務(wù)接口娃肿,然后在其上添加注解咕缎。Feign也支持可插拔式的編碼器和解碼器珠十。Spring Cloud 對Feign進(jìn)行了封裝,使其支持了SpringMVC標(biāo)準(zhǔn)注解和HttpMessageConverters凭豪。Feign可以與Eureka和Ribbon組合使用支持負(fù)載均衡焙蹭。

  2. Feign 與 OpenFeign 的比較

    Feign OpenFeign
    OpenFeign是 SpringCloud在Feign基礎(chǔ)上支持了SpringMVC的注解。
    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

5.2 Feign 服務(wù)調(diào)用

  1. 新建 cloud-consumeropenfeign-order10008 微服務(wù)

  2. 注入依賴pom

     <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
    
  3. 開啟 Feign

    @SpringBootApplication
    @EnableFeignClients
    public class ConsumerOpenFeignOrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerOpenFeignOrderApplication.class);
        }
    }
    
  4. 定義業(yè)務(wù)類的接口

    @Component
    @FeignClient("CLOUD-PAYMENT-SERVICE")   // 指定請求調(diào)用哪一個(gè)微服務(wù)
    public interface IPaymentFeignService {
    
        @GetMapping(value = "/payment/list/{id}")  // CLOUD-PAYMENT-SERVICE mapper 地址
        public CommonResult<PaymentVO> getParmentById(@PathVariable("id") String id);
    
    }
    
  5. 定義Controller 層

    @ResponseBody
    @RestController
    @Slf4j
    public class FeignOrderController {
        @Resource
        private IPaymentFeignService iPaymentFeignService;
    
        @GetMapping(value = "/consumer/order/feign/list/{id}")
        public CommonResult<PaymentVO> getPaymentVOById(@PathVariable("id") String id){
            return iPaymentFeignService.getParmentById(id);
        }
    }
    

5.3 Feign 超時(shí)控制

默認(rèn)Feign客戶端只等待1秒嫂伞,但是服務(wù)端處理時(shí)間超過1秒孔厉,直接發(fā)會報(bào)錯(cuò)。

在 properties 中配置默認(rèn)設(shè)置:

ribbon.ReadTimeout=5000
ribbon.ConnectTimeout=5000

5.4 Feign 日志增強(qiáng)

  1. 自定義配置類

    @Configuration
    public class FeignConfig {
        @Bean
        Logger.Level feignLoggerLevel(){
            return Logger.Level.FULL;
        }
    }
    
  2. 配置 yml / properties

    # 開啟 feign 日志 : 以哪一種日志級別監(jiān)控哪一個(gè)feign接口
    logging.level.com.dc.scloud.service.IPaymentFeignService=debug
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末末早,一起剝皮案震驚了整個(gè)濱河市烟馅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌然磷,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姿搜,死亡現(xiàn)場離奇詭異,居然都是意外死亡捆憎,警方通過查閱死者的電腦和手機(jī)舅柜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躲惰,“玉大人致份,你說我怎么就攤上這事〈〔Γ” “怎么了氮块?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诡宗。 經(jīng)常有香客問我滔蝉,道長,這世上最難降的妖魔是什么塔沃? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任蝠引,我火速辦了婚禮,結(jié)果婚禮上蛀柴,老公的妹妹穿的比我還像新娘螃概。我一直安慰自己,他們只是感情好鸽疾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布吊洼。 她就那樣靜靜地躺著,像睡著了一般肮韧。 火紅的嫁衣襯著肌膚如雪融蹂。 梳的紋絲不亂的頭發(fā)上旺订,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機(jī)與錄音超燃,去河邊找鬼区拳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛意乓,可吹牛的內(nèi)容都是我干的樱调。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼届良,長吁一口氣:“原來是場噩夢啊……” “哼笆凌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起士葫,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤乞而,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后慢显,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爪模,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年荚藻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屋灌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡应狱,死狀恐怖共郭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情疾呻,我是刑警寧澤除嘹,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站罐韩,受9級特大地震影響憾赁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜散吵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一龙考、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧矾睦,春花似錦晦款、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赁温,卻和暖如春坛怪,著一層夾襖步出監(jiān)牢的瞬間淤齐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工袜匿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留更啄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓居灯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親怪嫌。 傳聞我的和親對象是個(gè)殘疾皇子义锥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容