Dubbo入门学习笔记

Dubbo是什么

Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色。
简单说呢,Dubbo用起来就和EJB、WebService差不多,调用一个远程的服务(或者JavaBean)的时候在本地有一个接口,就像调用本地的方法一样去调用,它底层帮你实现好你的方法参数传输和远程服务运行结果传回之后的返回,就是RPC的一种封装啦~
当然,这个只是Dubbo的最基本的功能,它的特点是:

  1. 它主要是使用高效的网络框架和序列化框架,让分布式服务之间调用效率更高。
  2. 采用注册中心管理众多的服务接口地址,当你想调用服务的时候只需要跟注册中心询问即可,不用像使用WebService一样每个服务都得记录好接口调用方式。
  3. 监控中心:实现对服务方和调用方之间运行状态的监控,还能控制服务的优先级、权限、权重、上下线等,让整个庞大的分布式服务系统的维护和治理比较方便。
  4. 高可用:有个服务宕机了?注册中心就会从服务列表去掉该节点。还是调用到了?客户端会向注册中心请求另一台可用的服务节点重新调用。注册中心宕机?注册中心也能实现高可用(ZooKeeper)。
  5. 负载均衡:采用软负载均衡算法实现对多个相同服务的节点的请求负载均衡。
  6. 等等。。。很多高大上的,看官方文档吧,我也是文档中抄过来的~

一些参考站点或博客

*** 注意 ***

所有东西以官方用户指南为准:http://dubbo.io/User+Guide-zh.htm
其他第三方文章博客什么的可以参考辅助理解,但是由于阿里是国内公司,官方用户指南,特别是中文版都特别详细,真的没有很多必要去找一堆第三方的教程来看啦~

Dubbo环境准备

Dubbo需要四大基本组件:Registry、Monitor、Provider、Consumer。


dubbo-architecture.jpg-version=1&modificationDate=1330892870000.jpg
  1. 安装注册中心(Registry),我用ZooKeeper,具体参考我的ZooKeeper教程博客,安装好ZooKeeper之后注册中心就有了,先放着,等会用。

  2. 安装简单监控中心:simple-monitor。网上找dubbo-monitor-simple-2.8.4-assembly.tar.gz

  • 解压,找到conf文件夹下的dubbo.properties文件,下面简单介绍各个配置参数的意义:

容器,就是说这个简单监控中心是在jetty和spring环境下运行的,依赖于注册中心,日志系统是log4j

dubbo.container=log4j,spring,registry,jetty

监控系统对整个Dubbo服务系统来说也是一个服务,这里指定了这个监控服务的名称

dubbo.application.name=simple-monitor

服务的所有者,这是Dubbo的服务的功能,可以指定服务的负责人

dubbo.application.owner=coselding

下面四个是指定注册中心地址的,分别为广播、zookeeper、redis、dubbo(自带)方式的注册中心,前面说了,我用的是Zookeeper(前面配好了),因此去掉zookeeper前面的#,配置zookeeper的ip和端口号。之后简单监控中心就能通过注册中心获取当前可用的服务列表及其状态,在页面向你汇报Dubbo中的服务运行情况。

dubbo.registry.address=multicast://224.5.6.7:1234

dubbo.registry.address=zookeeper://{ip}:{port}

dubbo.registry.address=redis://127.0.0.1:6379

dubbo.registry.address=dubbo://127.0.0.1:9090

dubbo协议端口号,保持默认即可

dubbo.protocol.port=7070

jetty工作端口号,平时不是习惯8080吗?当然,这里为了不影响之后运行的tomcat,就不要占用8080端口啦。

dubbo.jetty.port=8082

一个工作目录,在这个目录会保存一些监控中心的数据,比如调用曲线图等,这里指定一个存在的空目录即可

dubbo.jetty.directory=${user.home}/monitor

监控中心报表存放的目录,同上,一般默认即可

dubbo.charts.directory=${dubbo.jetty.directory}/charts

监控中心数据资料目录,同上,一般默认即可

dubbo.statistics.directory=${user.home}/monitor/statistics

监控中心日志文件路径

dubbo.log4j.file=logs/dubbo-monitor-simple.log

监控中心日志记录级别

dubbo.log4j.level=WARN

>* 运行bin目录下的start.sh(unix系下)或start.bat(win下)即可。
>* 浏览器访问:`http://{简单监控中心所在的主机ip}:8082`,端口号是刚才配置文件设置的8082,即可访问查看Dubbo服务集群中的应用和服务的简单情况。

3. 安装**Dubbo管理控制台**:
>* 下载`dubbo-admin-2.8.4.war`,这个是Dubbo的管理控制台的webapp的war包,将其解压。
>* WEB-INF目录下的`dubbo.properties`文件配置Dubbo的信息,如下:
>```bash
# 配置注册中心地址,和简单监控中心一样,通过注册中心才能监控当前所有可用的服务。
dubbo.registry.address=zookeeper://127.0.0.1:2181
# root账户的密码,网页进入控制台界面之前需要输入帐号密码
dubbo.admin.root.password=root
# guest访客账户的密码
dubbo.admin.guest.password=guest
  • 将修改完配置的dubbo-admin的整个目录复制到tomcat的webapps目录下,重启tomcat,说白了dubbo-admin就是tomcat的一个webapp的形式存在。

Dubbo注册中心

上面已经安装完成了zookeeper的注册中心了,这个注册中心主要就是负责dubbo的所有服务地址列表维护,并且可以通过在ZooKeeper节点中设置相应的值来实现对这个服务的权重、优先级、是否可用、路由、权限等的控制。
你可以先记住,之后在Dubbo的管理控制台对服务的一堆治理策略设置和调整,实际上就是修改了注册中心中的服务对应的配置数据(即修改了zookeeper中服务对应的节点的配置数据)。
之后Consumer从注册中心请求到服务的数据时就能根据这些配置数据进行相应的治理配置参数的代码执行生效。

Dubbo样例服务开发

这里我用maven构建项目,在Spring环境中配置Provider和Consumer。
先说明使用的依赖:

        <!-- Spring所需依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>

        <!-- dubbo所需依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.8.4</version>
        </dependency>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.0.GA</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.netty</groupId>
            <artifactId>netty</artifactId>
            <version>LATEST</version>
        </dependency>

        <!-- ZooKeeper所需依赖 -->
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.12</version>
        </dependency>
  1. Provider
  • 声明服务的接口:
public interface IMyDemo {
        String sayHello(String name);
}
  • 对接口进行实现(这里是Provider,需要真的实现,之后在Consumer端调用接口之后实际就是在这里的实现代码执行所需逻辑的):
public class MyDemo implements IMyDemo {
        @Override
        public String sayHello(String name) {
            String hello = "hello " + name;
            System.out.println(hello);
            return hello;
        }
}
  • Spring配置相应的Dubbo服务(provider.xml):
<?xml version="1.0" encoding="UTF-8"?>
<!-- 这里添加了dubbo的命名空间,之后Spring通过dubbo中扩展的配置解析生成对应的dubbo实例放到Spring的IoC容器中 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!-- 指定服务的应用名称,在dubbo中层次级别是先分为多个应用(可以理解为一个项目),在每个应用下有多个服务(可以理解为项目下具体的某个提供服务的服务类) -->
    <!-- 这里就是指定一个应用的名称,指定相同的应用名的服务在dubbo中都会被分配在同一个应用分支下 -->
    <dubbo:application name="hello-world-app"  />
    <!-- 之前配置好的ZooKeeper服务器作为注册中心,这里指定好ZooKeeper的地址,此处的Dubbo才能连接上注册服务器,很好理解 -->
    <dubbo:registry address="zookeeper://119.29.153.56:2181" id="registry" />
    <!-- Dubbo的每个节点之间通信可以支持rmi、Http、Dubbo等一系列的协议,这里指定默认的dubbo协议,以及此处的服务Provider对外暴露的服务端口号 -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!-- 到这里,这个Provider节点就已经能连上注册服务器,并使用指定的协议进行通信了 -->
    <!-- 这里对外暴露刚才编写的接口,并指定IoC容器中接口的实现来为这个接口提供实际的服务 -->
    <dubbo:service interface="com.weidian.dubbo.IMyDemo" ref="myDemo"/>
    <!-- 这里是Spring原始的JavaBean声明方式,并放在IoC容器中 -->
    <bean id="myDemo" class="com.weidian.dubbo.MyDemo"/>
</beans>
  • 本地测试一下这个服务是否可用,这里还没用到Dubbo,只是先测试一下Spring容器是否有问题:
@org.junit.Test
public void testDubbo() throws InterruptedException {
        ApplicationContext providerContext = new ClassPathXmlApplicationContext("provider.xml");
        IMyDemo demo = providerContext.getBean(IMyDemo.class);
        System.out.println(demo.sayHello("world"));
        Thread.sleep(60000);
    }
  • 运行结果:


    provider.png

Provider内部代码输出了一遍,返回到测试代码又输出了一遍,总共两遍hello world

  1. Consumer
  • 编写和Provider服务的对应接口:
public interface IMyDemo {
        String sayHello(String name);
}
  • Spring配置远程的服务为本地的JavaBean(consumer.xml):
<?xml version="1.0" encoding="UTF-8"?>
<!-- 这里添加了dubbo的命名空间,之后Spring通过dubbo中扩展的配置解析生成对应的dubbo实例放到Spring的IoC容器中 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!-- 声明要连接的应用名称,要和Provider声明的指定的应用名一致 -->
    <dubbo:application name="hello-world-app"  />
    <!-- 之前配置好的ZooKeeper服务器作为注册中心,这里指定好ZooKeeper的地址,此处的Dubbo才能连接上注册服务器,很好理解 -->
    <dubbo:registry address="zookeeper://119.29.153.56:2181" id="registry" />
    <!-- Dubbo的每个节点之间通信可以支持rmi、Http、Dubbo等一系列的协议,这里指定默认的dubbo协议,
        以及此处的Consumer对外暴露的服务端口号,因为注册中心有服务节点列表更新是要实时推送到Consumer中的 
        -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!-- 到这里,这个Consumer节点就已经能连上注册服务器,并使用指定的协议进行通信了 -->
    <!-- 接口指定Consumer端的那个服务接口,之后它就会通过这个接口的应用名和全限定名去注册中心
        查找实际的服务Provider地址列表,再通过指定的dubbo协议进行通信,实现RPC,而在Consumer
        本机端对Spring的IoC容器指定id,方便之后对这个远程JavaBean的引用调用 -->
    <dubbo:reference id="demoRemote" interface="com.weidian.dubbo.IMyDemo" protocol="dubbo"/>
</beans>
  • 另外,也能绕过Registry直连Provider,如下:
<dubbo:reference interface="com.weidian.dubbo.IMyDemo" version="1.0" id="myDemo" url="dubbo://127.0.0.1:20880/"></dubbo:reference>
  • 远程过程调用测试(前提是先把Provider的服务先运行起来再来运行这个Consumer(在两个端的测试代码尾部都添加了sleep代码的原因):
@org.junit.Test
public void testGetRemoteService() throws InterruptedException {
        ApplicationContext consumerContext = new ClassPathXmlApplicationContext("consumer.xml");
        IMyDemo demoRemote = consumerContext.getBean(IMyDemo.class);
        System.out.println(demoRemote.sayHello("world"));
        Thread.sleep(30000);
    }
  • 运行结果:
    Provider:


    remote-provider.png

启动Provider输出了两遍,Consumer调用时本地实现又输出了一遍,共三遍。
Consumer:


consumer.png

启动Consumer,远程返回输出了一遍。

Dubbo简单监控中心

简单介绍,其实用处不大,我觉得管理后台的功能已经把这个监控中心的功能覆盖了,仅仅只是拿来测试使用一下。

  • 主界面:


    simple-monitor.png
  • 服务列表界面,显示所有服务以及它的Provider和Consumer情况:


    simple-monitor-Service.png

Dubbo管理后台使用

之前Tomcat中配置好了dubbo-admin的webapp,现在只要打开tomcat,并输入相应的地址即可访问dubbo-admin的界面,如我的是http://127.0.0.1:8080/dubbo-admin/
其实就是一个很常见的管理后台,可以控制每个服务、应用的状态、权重、路由控制、访问控制、负载均衡、各个应用的服务情况和消费情况等,不需要教程,直接上手使用即可,至于其中的一些可能有疑问的概念,下面的内容将一一说明。

服务路由

路由,顾名思义,就是通过配置去设定哪些Consumer节点的请求由哪些节点的Provider节点的服务来进行响应,可以在一定程度上控制负载分布。
知道这个概念,那剩下就是配置的问题了,明白怎么回事其实就很简单啦~

在dubbo-admin主界面——服务治理——路由规则,如下:

router-main.png

点击新增

router-new.png

路由名称:为你定义的这个路由规则声明一个名称,之后可以根据这个名称来找这个路由规则。
优先级:很明显,一个int数值代表这个路由规则的优先级,优先级越高这个规则越先匹配。
服务名:列表选择的,从注册中心中已有的服务列表中选择一个服务,表示这个路由规则要约束的是哪个服务。
方法名:列表选择的,选择要约束的这个服务中的哪个方法。
匹配条件:填写匹配下面的列表就表示匹配了条件才受这个路由规则约束,否则就是不匹配规则才受该规则约束。
消费者IP地址:要约束的Consumer的IP地址列表,逗号隔开。
消费者应用名:要约束的Consumer的应用名列表。
消费者集群:按照提示上写的去找,没找到相应的选项,暂时也没用到,之后再回头编辑。
过滤规则:填写匹配下面的列表就表示匹配了条件才受这个路由规则约束,否则就是不匹配规则才受该规则约束。
提供者IP地址:要约束的Provider的IP地址列表,逗号隔开。
提供者集群:按照提示上写的去找,没找到相应的选项,暂时也没用到,之后再回头编辑。
提供者协议:指定Provider的协议,不是该协议的也不约束。
提供者端口:指定Provider的端口,不是这个端口的也不约束。

测试样例展示:


router-sample.png

这样配置的路由就能够生效,效果是:com.weidian.dubbo.IMyDemo服务的sayHello方法下,地址为192.168.31.164的Consumer的请求由(地址:192.168.31.164;协议:dubbo;端口:20880)的Provider进行响应,如果没有匹配的Provider就会默认返回所有的Provider列表给Consumer,让Consumer自己选。
点击保存,保存新增的路由规则,并在路由规则列表中启用这个规则,如下:

router-list.png

点击预览,在消费者地址填写192.168.31.164,点击预览,如下图所示:

router-success.png

运行Consumer程序,查看消费者状态,可知设定的这个路由规则对该对应地址的消费真生效了,如下图:


router-consumer.png

另外,IP地址支持结尾为匹配所有,如10.0.0.或者10.0.*等。
不匹配的配置规则和匹配的配置规则是一致的。

负载均衡

dubbo提供4种负载均衡方式:
Random:随机,按权重配置随机概率,调用量越大分布越均匀,默认是这种方式
RoundRobin:轮询,按权重设置轮询比例,如果存在比较慢的机器容易在这台机器的请求阻塞较多
LeastActive:最少活跃调用数,不支持权重,只能根据自动识别的活跃数分配,不能灵活调配
ConsistentHash:一致性hash,对相同参数的请求路由到一个服务提供者上,如果有类似灰度发布需求可采用
dubbo的负载均衡机制是在客户端调用时通过内存中的服务方信息及配置的负责均衡策略选择,如果对自己系统没有一个全面认知,建议先采用random方式。

Dubbo过滤器

有需要自己实现dubbo过滤器的,可关注如下步骤:

  1. dubbo初始化过程加载ClassPath下的META-INF/dubbo/internal/META-INF/dubbo/META-INF/services/三个路径(classloader resource)下面的com.alibaba.dubbo.rpc.Filter文件。
    文件内容:
    每行Name=FullClassName,这些类必须是实现Filter接口,如下图:

    filter-prop.png

  2. 自定义Filter类:

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
//激活这个过滤器的注解,标记这个过滤器在消费者端加入过滤器链
@Activate(group = Constants.CONSUMER)
//dubbo过滤器的实现类
public class DubboTestFilter implements Filter {
    /**计时器过滤器,记录这个RPC的整个过程执行时间
     * @param invoker
     * @param invocation
     * @return
     * @throws RpcException
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long start = System.currentTimeMillis();
        //这个是RPC的实现体,Result就是RPC的执行结果,和Servlet的过滤器有些类似
        Result result = invoker.invoke(invocation);
        System.out.println("time = " + (System.currentTimeMillis() - start) + "ms");
        System.out.println(result.getValue());
        return result;
    }
}
  1. consumer.xml配置文件中配置这个过滤器,如下图:


    filter-spring.png
  2. 先来看看配置完成之后的执行效果:


    filter-run.png

对比上面的过滤器代码可知,先输出了RPC过程的运行时间,之后在过滤器中输出了一遍执行结果hello world,之后单元测试本身再把执行结果输出一遍,因此hello world输出了两遍。

  1. Dubbo过于这个过滤器的加载过程:

(1) 先加载那三个路径下的com.alibaba.dubbo.rpc.Filter文件里面的键值对,key为过滤器的名称,value为过滤器的类全限定名(这个类必须实现Dubbo中的Filter接口);
(2) 这样就能找到这个类的class文件了,检查@Activate注解加载这个过滤器设定的一些全局基本属性;
(3) Spring在加载consumer.xml文件的时候,通过

<dubbo:consumer filter="dubboTestFilter" id="dubboTestFilter" retries="0"/>

指定消费者端要加载的过滤器,通过filter属性指定过滤器名称(就是配置文件中的过滤器key),这样刚才加载的过滤器类就加入消费者代码逻辑中的过滤器链了。

  1. 关于@Activate注解: —— 自动激活

group:(provider|consumer)匹配了对应的角色才被加载
value:标明过滤条件,不写则所有条件下都会被加载,写了则只有dubbo URL中包含该参数名且参数值不为空才被加载,这个参数会以dubbo协议的一个参数K-V对传到Provider。

Dubbo Rest风格服务

  1. 当当Dubbox:https://dangdangdotcom.github.io/dubbox/rest.html
  2. Dubbo Rest风格服务开发:https://segmentfault.com/a/1190000005170426

----

还欠缺安全保护和集群性能调优方面的讲解,毕竟才刚入门,待我在公司的项目中使用一段时间有了真正的一些实战感悟再来好好总结~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 0 准备 安装注册中心:Zookeeper、Dubbox自带的dubbo-registry-simple;安装Du...
    七寸知架构阅读 13,979评论 0 88
  • 1.背景 想象下这么个场景: 有个做生活服务的APP,主要提供一些生活化的咨询信息,比如天气、新闻、个人三金账单、...
    点融黑帮阅读 3,871评论 0 33
  • 午醒逛园藤满路。几种心情,只把冰眉蹙。月少半边天狗吐,心缺一角谁能补? 遍地残花弗忍顾。泪眼忽寒,愁絮穿朱户。又是...
    瑰洱阅读 254评论 0 2
  • 用了好几年的苹果原装数据线,终于还是无法忍受它的爱破皮、不耐脏以及价格略贵。图拉斯数据线是我用的第一款第三方数据线...
    黎虹阅读 634评论 0 3