<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2021-05-29T13:24:34+00:00</updated><id>/feed.xml</id><title type="html">Humyna’s Blog</title><subtitle>Make my life a story worth telling.</subtitle><entry><title type="html">开源MybatisGenerator脚手架项目</title><link href="/blog/2019/05/22/%E5%BC%80%E6%BA%90MybatisGenerator%E8%84%9A%E6%89%8B%E6%9E%B6%E9%A1%B9%E7%9B%AE.html" rel="alternate" type="text/html" title="开源MybatisGenerator脚手架项目" /><published>2019-05-22T00:00:00+00:00</published><updated>2019-05-22T00:00:00+00:00</updated><id>/blog/2019/05/22/%E5%BC%80%E6%BA%90MybatisGenerator%E8%84%9A%E6%89%8B%E6%9E%B6%E9%A1%B9%E7%9B%AE</id><content type="html" xml:base="/blog/2019/05/22/%E5%BC%80%E6%BA%90MybatisGenerator%E8%84%9A%E6%89%8B%E6%9E%B6%E9%A1%B9%E7%9B%AE.html">&lt;h3 id=&quot;背景&quot;&gt;背景&lt;/h3&gt;
&lt;p&gt;最近由于公司业务发展，需要分别给相关业务基于springboot搭建新工程。
为了提高团队开发效率，选择Mybatis Generator自动生成dao层相关代码和配置文件(model、mapper、xml)，在整合过程中也遇到一些问题，不过最终都一一解决了。&lt;/p&gt;

&lt;h3 id=&quot;开源项目&quot;&gt;开源项目&lt;/h3&gt;
&lt;p&gt;为了以后新建项目少走弯路，我整理了两个脚手架项目mybatis-generator-mysql和mybatis-generator-oracle,分别对应mysql和oracle的代码自动生成。&lt;/p&gt;

&lt;p&gt;项目已开源，&lt;a href=&quot;https://github.com/humyna/java-scaffolding&quot;&gt;项目地址&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;使用方法&quot;&gt;使用方法&lt;/h3&gt;
&lt;p&gt;参考&lt;a href=&quot;https://github.com/humyna/java-scaffolding/blob/master/README.md&quot;&gt;README&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><category term="blog" /><category term="开源" /><category term="mybatis generator" /><category term="oracle" /><category term="mysql" /><summary type="html">背景 最近由于公司业务发展，需要分别给相关业务基于springboot搭建新工程。 为了提高团队开发效率，选择Mybatis Generator自动生成dao层相关代码和配置文件(model、mapper、xml)，在整合过程中也遇到一些问题，不过最终都一一解决了。</summary></entry><entry><title type="html">彻底解决dubbo与dubbox默认序列化不兼容的问题和多注册中心配置</title><link href="/2019/04/28/%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3dubbo%E4%B8%8Edubbox%E9%BB%98%E8%AE%A4%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8D%E5%85%BC%E5%AE%B9%E7%9A%84%E9%97%AE%E9%A2%98%E5%92%8C%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE.html" rel="alternate" type="text/html" title="彻底解决dubbo与dubbox默认序列化不兼容的问题和多注册中心配置" /><published>2019-04-28T00:00:00+00:00</published><updated>2019-04-28T00:00:00+00:00</updated><id>/2019/04/28/%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3dubbo%E4%B8%8Edubbox%E9%BB%98%E8%AE%A4%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8D%E5%85%BC%E5%AE%B9%E7%9A%84%E9%97%AE%E9%A2%98%E5%92%8C%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE</id><content type="html" xml:base="/2019/04/28/%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3dubbo%E4%B8%8Edubbox%E9%BB%98%E8%AE%A4%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8D%E5%85%BC%E5%AE%B9%E7%9A%84%E9%97%AE%E9%A2%98%E5%92%8C%E5%A4%9A%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE.html">&lt;blockquote&gt;
  &lt;p&gt;本文主要解决了spring-boot项目引入dubbo官方提供starter暴露服务被dubbox客户端调用出现序列化不兼容的问题。&lt;/p&gt;

  &lt;p&gt;项目已开源，&lt;a href=&quot;https://github.com/humyna/spring-boot-starter-dubbox&quot;&gt;项目地址&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;近期公司业务发展需要需要基于spring-boot搭建一个新项目。
项目为了兼容公司旧技术栈需要对外暴露Dubbo服务，故引入了Dubbo的原生starter。
在测试过程中发现暴露服务被现有调用时出现序列化问题。
经查是Dubbox修改了Dubbo底层通讯序列化实现，导致无法进行反序列化。

先尝试使用@ImportResource引入Dubbox配置，不过还是因为项目启动时无法替换占位符而放弃。
原因是xml配置文件中使用了AnnotationBean，该bean类型为BeanFactoryPostProcessor，它会提前初始化dubbo的一些其他配置，又xml优先级比其他注解高导致在此AnnotationBean在初始化的时候PropertyPlaceholderConfigurer还未生效，最终导致AnnotationBean初始化dubbox配置失败，占位符未被替换。

我们知道原生Dubbo使用xml配置&amp;lt;dubbo:annotation /&amp;gt;来开启注解配置，并提供com.alibaba.dubbo.config.annotation.Service注解进行服务注册，提供com.alibaba.dubbo.config.annotation.Reference注解进行服务注入。既然Dubbo支持使用JavaConfig方式进行服务的注册暴露及调用，那么可以从这入手来解决序列化兼容问题。实现思路参考：https://my.oschina.net/roccn/blog/847635

具体实现可以参考项目源码。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;spring-boot-starter-dubbox&quot;&gt;spring-boot-starter-dubbox&lt;/h1&gt;
&lt;h2 id=&quot;使用方法&quot;&gt;使用方法&lt;/h2&gt;

&lt;h4 id=&quot;一发布jar至maven伺服&quot;&gt;一、发布jar至maven伺服&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;下载工程代码&lt;/li&gt;
  &lt;li&gt;修改pom.xml中添加你的私服配置&lt;/li&gt;
  &lt;li&gt;然后执行mvn deploy，发布至仓库&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;二springboot工程暴露和调用dubbo服务&quot;&gt;二、springboot工程暴露和调用dubbo服务&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;工程pom.xml引入&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;info.zoio.spring&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;spring-boot-starter-dubbox&amp;lt;/artifactId&amp;gt;
	&amp;lt;version&amp;gt;1.0.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;配置&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;yml配置&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;spring:
  dubbo:
    application:
        name: app-demo
    registry:
      address: zookeeper://127.0.0.1:2181 #修改成your zk配置
    consumer:
      check: false
    provider:
      timeout: 90000
      token: false
      retries: 0
      actives: 30
      accepts: 1000
    protocol:
      id: dubbo
      name: dubbo
      payload: 83886080
      port: 0
      dispatcher: all
      threadpool: fixed
      threads: 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;如果没有配置spring.dubbo.registry,系统会默认注册到127.0.0.1:2181上。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;服务调用
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@org.springframework.stereotype.Component
@org.springframework.context.annotation.Profile({&quot;dev&quot;,&quot;test&quot;,&quot;online&quot;})
public class DubboReference {
 @com.alibaba.dubbo.config.annotation.Reference(group = &quot;demo&quot;, version = &quot;1.0.0&quot;, timeout = 60000, retries = 0,registry={&quot;0&quot;})//registry对应多注册中心配置的id
 public DemoFacade demoFacade;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;其中dev、test、online对应&lt;/p&gt;

  &lt;p&gt;application-dev.yml&lt;/p&gt;

  &lt;p&gt;application-test.yml&lt;/p&gt;

  &lt;p&gt;application-online.yml&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;业务调用&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@javax.annotation.Resource
private DubboReference dubboReference;

dubboReference.demoFacade.methodXXX();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;暴露服务&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@org.springframework.stereotype.Component
@Service(version = &quot;1.0.0&quot;,timeout = 10000,interfaceClass = ExportDemoFacade.class,group=&quot;demo&quot;)
public class ExportDemoFacadeImpl implements ExportDemoFacade {
	//TODO
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;三多注册中心多协议配置说明&quot;&gt;三、多注册中心&amp;amp;多协议配置说明&lt;/h4&gt;
&lt;blockquote&gt;
  &lt;p&gt;以下配置均经过测试，可放心使用！&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
  &lt;li&gt;单注册中心配置示例&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;spring:
  dubbo:
 		registry:
      address: zookeeper://127.0.0.1:2181 #修改成your zk配置
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;多注册中心配置示例&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;spring:
  dubbo:
		registry[0]:
			id: provider #务必填写id
			address: zookeeper://127.0.0.1:2181 #修改成你服务注册zk地址
		registry[1]:
			id: consumer1
			address: zookeeper://127.0.0.1:2181 #修改成服务提供者1zk地址
		registry[2]:
			id: consumer2
			address: zookeeper://127.0.0.1:2181 #修改成服务提供者2zk地址
		registry[3]:
			id: consumer3
			address: zookeeper://127.0.0.1:2181 #修改成服务提供者3zk地址
在启动参数中添加，使用逗号隔开
-Dspring.dubbo.provider.registry.ids=provider
-Dspring.dubbo.consumer.registry.ids=consumer1,consumer2,consumer3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;provider多协议支持同多注册中心配置&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;spring:
  dubbo:
  	protocol[0]:
  		id: dubbo #务必填写

在启动参数中添加，使用逗号隔开
-Dspring.dubbo.provider.protocol.ids=dubbo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name></name></author><summary type="html">本文主要解决了spring-boot项目引入dubbo官方提供starter暴露服务被dubbox客户端调用出现序列化不兼容的问题。 项目已开源，项目地址</summary></entry><entry><title type="html">幂等</title><link href="/blog/2019/01/04/%E5%B9%82%E7%AD%89.html" rel="alternate" type="text/html" title="幂等" /><published>2019-01-04T00:00:00+00:00</published><updated>2019-01-04T00:00:00+00:00</updated><id>/blog/2019/01/04/%E5%B9%82%E7%AD%89</id><content type="html" xml:base="/blog/2019/01/04/%E5%B9%82%E7%AD%89.html">&lt;blockquote&gt;
  &lt;p&gt;这一年公司服务化如火如荼地进行，经常听到两个团队在讨论系统交互方案时谈到幂等。不过我发现，虽然大家都知道“幂等”这个词，但真正理解幂等的没几个。我收集整理了网上的一些资料成此文，应该算是全网讲解幂等最全的一篇文章了吧。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;问题&quot;&gt;问题&lt;/h3&gt;

&lt;p&gt;双十一，零点刚开始，小明就迫不及待地点击提交订单按钮，1秒，2秒，3秒，没反应，小明有点心慌，又快速地点击了两下，提示下单成功。随后小明到我的订单列表中一看，发现有三个相同的订单，小明一脸黑线。&lt;/p&gt;

&lt;h3 id=&quot;什么是幂等&quot;&gt;什么是幂等&lt;/h3&gt;
&lt;blockquote&gt;
  &lt;p&gt;HTTP/1.1中对幂等性的定义是：
Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N &amp;gt; 0 identical requests is the same as for a single request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里不讨论学术上如何定义幂等性，而是重点在于如何在分布式环境中提供对外幂等性的接口。对外提供的接口承诺幂等性，其要表达的含义是：只要调用接口成功，外部对接口的多次调用得到的结果是相同的，即执行多次和一次的效果是一样的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;幂等性是系统的接口对外一种承诺(而不是实现), 承诺只要调用接口成功, 外部多次调用对系统的影响是一致的。声明为幂等的接口会认为外部调用失败是常态, 并且失败之后必然会有重试。 幂等性并不属于特定的协议，它是分布式系统的一种特性。&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;为什么需要幂等&quot;&gt;为什么需要幂等&lt;/h3&gt;

&lt;p&gt;上面小明遇到的问题，就是在防止重复提交的情况上没有做好控制。
业务开发中，经常会遇到重复提交的情况，无论是由于网络问题无法收到请求结果而重新发起请求，或是前端的操作抖动而造成重复提交情况。&lt;/p&gt;

&lt;p&gt;在交易系统，支付系统这种重复提交造成的问题有尤其明显，比如：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;用户在APP上连续点击了多次提交订单，后台应该只产生一个订单；（下多个相同的单，我有病啊）&lt;/li&gt;
  &lt;li&gt;向支付宝发起支付请求，由于网络问题或系统BUG重发，支付宝应该只扣一次钱。（多付N笔钱，我真有钱啊）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;很显然，幂等接口认为，外部调用者会存在多次调用的场景，为了防止重试对数据状态的改变，需要将接口的设计为幂等的。&lt;/p&gt;

&lt;h3 id=&quot;什么情况下需要保证幂等性&quot;&gt;什么情况下需要保证幂等性&lt;/h3&gt;

&lt;p&gt;以SQL为例，有下面三种场景，只有第三种场景需要开发人员使用其他策略保证幂等性：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;SELECT col1 FROM tab1 WHER col2=2，无论执行多少次都不会改变状态，是天然的幂等。&lt;/li&gt;
  &lt;li&gt;UPDATE tab1 SET col1=1 WHERE col2=2，无论执行成功多少次状态都是一致的，因此也是幂等操作。&lt;/li&gt;
  &lt;li&gt;UPDATE tab1 SET col1=col1+1 WHERE col2=2，每次执行的结果都会发生变化，这种不是幂等的。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;保证幂等策略&quot;&gt;保证幂等策略&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;幂等需要通过唯一的业务单号来保证&lt;/strong&gt;。也就是说相同的业务单号，认为是同一笔业务。使用这个唯一的业务单号来确保，后面多次的相同的业务单号的处理逻辑和执行效果是一致的。&lt;/p&gt;

&lt;p&gt;下面以支付为例，在不考虑并发的情况下，实现幂等很简单：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;先查询一下订单是否已经支付过，&lt;/li&gt;
  &lt;li&gt;如果已经支付过，则返回支付成功；如果没有支付，进行支付流程，修改订单状态为‘已支付’。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;上述的保证幂等方案是分成两步的，第2步依赖第1步的查询结果，无法保证原子性的。在高并发下就会出现下面的情况：第二次请求在第一次请求第2步订单状态还没有修改为‘已支付状态’的情况下到来。
既然得出了这个结论，余下的问题也就变得简单：&lt;strong&gt;把查询和变更状态操作加锁，将并行操作改为串行操作。&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;乐观锁&quot;&gt;乐观锁&lt;/h4&gt;

&lt;p&gt;如果只是更新已有的数据，没有必要对业务进行加锁，设计表结构时使用乐观锁，一般通过version来做乐观锁，这样既能保证执行效率，又能保证幂等。例如：&lt;/p&gt;

&lt;p&gt;UPDATE table1 SET col1=1,version=version+1 WHERE version=#version#&lt;/p&gt;

&lt;p&gt;不过，乐观锁存在失效的情况，就是常说的ABA问题，不过如果version版本一直是自增的就不会出现ABA的情况。（从网上找了一张图片很能说明乐观锁，引用过来，出自&lt;a href=&quot;http://www.voidcn.com/blog/liyantianmin/article/p-5038695.html&quot;&gt;Mybatis对乐观锁的支持&lt;/a&gt;）&lt;/p&gt;

&lt;h4 id=&quot;防重表&quot;&gt;防重表&lt;/h4&gt;
&lt;p&gt;使用订单号orderNo做为去重表的唯一索引，每次请求都根据订单号向去重表中插入一条数据。第一次请求查询订单支付状态，当然订单没有支付，进行支付操作，无论成功与否，执行完后更新订单状态为成功或失败，删除去重表中的数据。后续的订单因为表中唯一索引而插入失败，则返回操作失败，直到第一次的请求完成（成功或失败）。可以看出防重表作用是加锁的功能。&lt;/p&gt;

&lt;h4 id=&quot;分布式锁&quot;&gt;分布式锁&lt;/h4&gt;
&lt;p&gt;这里使用的防重表可以使用分布式锁代替，比如Redis。订单发起支付请求，支付系统会去Redis缓存中查询是否存在该订单号的Key，如果不存在，则向Redis增加Key为订单号。查询订单支付已经支付，如果没有则进行支付，支付完成后删除该订单号的Key。通过Redis做到了分布式锁，只有这次订单订单支付请求完成，下次请求才能进来。相比去重表，将放并发做到了缓存中，较为高效。思路相同，同一时间只能完成一次支付请求。&lt;/p&gt;

&lt;h4 id=&quot;token令牌&quot;&gt;token令牌&lt;/h4&gt;
&lt;p&gt;这种方式分成两个阶段：申请token阶段和支付阶段。&lt;/p&gt;

&lt;p&gt;第一阶段，在进入到提交订单页面之前，需要订单系统根据用户信息向支付系统发起一次申请token的请求，支付系统将token保存到Redis缓存中，为第二阶段支付使用。&lt;/p&gt;

&lt;p&gt;第二阶段，订单系统拿着申请到的token发起支付请求，支付系统会检查Redis中是否存在该token，如果存在，表示第一次发起支付请求，删除缓存中token后开始支付逻辑处理；如果缓存中不存在，表示非法请求。&lt;/p&gt;

&lt;p&gt;实际上这里的token是一个信物，支付系统根据token确认，你是你妈的孩子。不足是需要系统间交互两次，流程较上述方法复杂。&lt;/p&gt;

&lt;h4 id=&quot;支付缓冲区&quot;&gt;支付缓冲区&lt;/h4&gt;

&lt;p&gt;把订单的支付请求都快速地接下来，一个快速接单的缓冲管道。后续使用异步任务处理管道中的数据，过滤掉重复的待支付订单。&lt;/p&gt;

&lt;p&gt;优点是同步转异步，高吞吐。不足是不能及时地返回支付结果，需要后续监听支付结果的异步返回。&lt;/p&gt;

&lt;h3 id=&quot;三个角度讨论幂等控制的实现&quot;&gt;三个角度讨论幂等控制的实现&lt;/h3&gt;

&lt;h4 id=&quot;http的幂等性&quot;&gt;HTTP的幂等性&lt;/h4&gt;

&lt;p&gt;幂等表示：请求服务器一次或是多次，返回的结果均是一样的[select]一般是GET请求
非幂等表示：请求服务器不同的次数，返回的结果将是不一样的[update delete] 一般是POST请求&lt;/p&gt;

&lt;p&gt;HTTP协议本身是一种面向资源的应用层协议，但对HTTP协议的使用实际上存在着两种不同的方式：一种是restful，它把HTTP当成应用层协议，另一种是SOA，它并没有完全把HTTP当成应用层协议，而是把HTTP协议作为了传输层协议，然后在HTTP之上建立了自己的应用层协议。&lt;/p&gt;

&lt;p&gt;本文所讨论的HTTP幂等性主要针对RESTful风格的，不过正如上一节所看到的那样，幂等性并不属于特定的协议，它是分布式系统的一种特性；所以，不论是SOA还是RESTful的Web API设计都应该考虑幂等性。
| 重要方法 | 安全 | 幂等 |
|–|–|–|–|
| GET | 是 | 是|
| POST | 否 | 否|
| PUT |  否| 是 |
| DELETE | 否 | 是|&lt;/p&gt;

&lt;h4 id=&quot;数据库幂等&quot;&gt;数据库幂等&lt;/h4&gt;

&lt;p&gt;数据库上的幂等和事务是一体的。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;查询操作
查询一次和查询多次，在数据不变的情况下，查询结果是一样的。select是天然的幂等操作&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;删除操作
删除操作也是幂等的，删除一次和多次删除都是把数据删除。(注意可能返回结果不一样，删除的数据不存在，返回0，删除的数据多条，返回结果多个)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;唯一索引，防止新增脏数据
比如：支付宝的资金账户，支付宝也有用户账户，每个用户只能有一个资金账户，怎么防止给用户创建资金账户多个，那么给资金账户表中的用户ID加唯一索引，所以一个用户新增成功一个资金账户记录&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;悲观锁
获取数据的时候加锁获取。
select * from table_xxx where id=’xxx’ for update;&lt;/p&gt;
    &lt;blockquote&gt;
      &lt;p&gt;注意：id字段一定是主键或者唯一索引，不然是锁表，会死人的！&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;悲观锁使用时一般伴随事务一起使用，数据锁定时间可能会很长，根据实际情况选用。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;乐观锁
乐观锁只是在更新数据那一刻锁表，其他时间不锁表，所以相对于悲观锁，效率更高。&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;客户端幂等控制机制-token&quot;&gt;客户端幂等控制机制-token&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;业务要求
页面的数据只能被点击提交一次&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;发生原因
 由于重复点击或者网络重发，或者nginx重发等情况会导致数据被重复提交&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;解决办法
集群环境：采用token加redis（redis单线程的，处理需要排队）
单JVM环境：采用token加redis或token加jvm内存&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;处理流程
数据提交前要向服务的申请token，token放到redis或jvm内存，token有效时间
提交后后台校验token，同时删除token，生成新的token返回&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;token特点
要申请，一次有效性，可以限流&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;幂等性接口的不足&quot;&gt;幂等性接口的不足&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;增加了额外控制幂等的业务逻辑，复杂化了业务功能；&lt;/li&gt;
  &lt;li&gt;把并行执行的功能改为串行执行，降低了执行效率。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;因此除了业务上的特殊要求外，尽量不提供幂等的接口。&lt;/p&gt;</content><author><name></name></author><category term="blog" /><category term="幂等" /><category term="高并发" /><summary type="html">这一年公司服务化如火如荼地进行，经常听到两个团队在讨论系统交互方案时谈到幂等。不过我发现，虽然大家都知道“幂等”这个词，但真正理解幂等的没几个。我收集整理了网上的一些资料成此文，应该算是全网讲解幂等最全的一篇文章了吧。</summary></entry><entry><title type="html">SPV节点是如何利用布隆(Bloom)过滤器保护隐私的？</title><link href="/blog/2018/12/21/how-to-use-bloom-filte-to-protect-privacy.html" rel="alternate" type="text/html" title="SPV节点是如何利用布隆(Bloom)过滤器保护隐私的？" /><published>2018-12-21T00:00:00+00:00</published><updated>2018-12-21T00:00:00+00:00</updated><id>/blog/2018/12/21/how-to-use-bloom-filte-to-protect-privacy</id><content type="html" xml:base="/blog/2018/12/21/how-to-use-bloom-filte-to-protect-privacy.html">&lt;blockquote&gt;
  &lt;p&gt;这两天利用空闲时间终于把之前遗留的几个关于布隆过滤器的问题搞明白了，那种感觉无法用语言表达出来，有相同经历的人应该会懂。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;区块链网络中并不是所有的节点都有能力储存完整的区块链(几百G)，像数字钱包等手机客户端会因为空间的限制，会通过简易支付验证（SPV）的方式在不必存储完整区块链的情况下工作。&lt;/p&gt;

&lt;p&gt;由于SPV节点(没有特殊说明，本文SPV节点均指数字轻钱包)需要读取特定交易从而选择性地验证交易，这样会产生隐私风险——SPV节点对特定数据的请求可能无意中透露了钱包里的地址信息。例如，监控网络的第三方可以跟踪某个SPV节点上的钱包所请求的全部交易信息，并且利用这些交易信息把比特币地址和钱包的用户关联起来，从而损害了用户的隐私。我们可以使用Bloom过滤器来解决SPV节点的隐私风险问题。&lt;/p&gt;

&lt;h3 id=&quot;0x01bloom过滤器保护隐私的原理&quot;&gt;0x01.Bloom过滤器保护隐私的原理&lt;/h3&gt;

&lt;p&gt;Bloom过滤器是一个允许用户描述特定的关键词组合而不必精确表述的基于概率的过滤方法。它能让用户在有效搜索关键词的同时保护他们的隐私。在SPV节点里，这一方法被用来向对等节点发送交易信息查询请求，同时交易地址不会被暴露。&lt;/p&gt;

&lt;p&gt;举个例子，一位手中没有地图的游客需要询问去特定地方的路线。如果他向陌生人询问“教堂街23号在哪里”， 不经意之间，他就暴露了自己的目的地。Bloom过滤器则会这样问，附近有带‘堂’字的街道吗？”这样的问法包含了比之前略少的关键词。这位游客可以自己选择包含信息的多少，比如“以‘堂街’结尾”或者“‘教’字开头的街道”。如果他问得越少，得到了更多可能的地址，隐私得到了保护，但这些地址里面不乏无关的结果；如果他问得非常具体，他在得到较准确的结果的同时也暴露了自己的隐私。&lt;/p&gt;

&lt;p&gt;Bloom过滤器可以让SPV节点指定交易的搜索模式，该搜索模式可以基于准确性或私密性的考虑被调节。一个非常具体的Bloom过滤器会生成更准确的结果，但也会显示该用户钱包里的使用的地址；反之，如果过滤器只包含简单的关键词，更多相应的交易会被搜索出来，在包含若干无关交易的同时有着更高的私密性。&lt;/p&gt;

&lt;h3 id=&quot;0x02bloom过滤器如何工作&quot;&gt;0x02.Bloom过滤器如何工作？&lt;/h3&gt;

&lt;p&gt;Bloom过滤器的实现是由一个可变长度（N）的二进制数组（N位二进制数构成一个位域）和数量可变（M）的一组哈希函数组成。这些哈希函数的输出值始终在1和N之间，该数值与二进制数组相对应。并且该函数为确定性函数，也就是说任何一个使用相同Bloom过滤器的节点通过该函数都能对特定输入得到同一个的结果。Bloom过滤器的准确性和私密性能通过改变长度（N）和哈希函数的数量（M）来调节。&lt;/p&gt;

&lt;p&gt;我们用一个小型的十六位数组和三个哈希函数来演示Bloom过滤器的应用原理。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;初始化&lt;/p&gt;

    &lt;p&gt;Bloom过滤器数组里的每一个数的初始值为零。
&lt;img src=&quot;http://www.btccfo.com/wp-content/uploads/2018/11/bloom1.png&quot; alt=&quot;图1&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;向简易Bloom过滤器添加关键词“A”&lt;/p&gt;

    &lt;p&gt;关键词被加到Bloom过滤器中之前，会依次通过每一个哈希函数运算一次。该输入经第一个哈希函数运算后得到了一个在1和N之间的数，它在该数组（编号依次为1至N）中所对应的位被置为1，从而把哈希函数的输出记录下来。接着再进行下一个哈希函数的运算，把另外一位置为1；以此类推。当全部M个哈希函数都运算过之后，一共有M个位的值从0变成了1，这个关键词也被“记录”在了Bloom过滤器里。
&lt;img src=&quot;http://www.btccfo.com/wp-content/uploads/2018/11/bloom2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;向该简易Bloom过滤器里增加第二个关键词“B”&lt;/p&gt;

    &lt;p&gt;增加第二个关键是就是简单地重复之前的步骤。关键词依次通过各哈希函数运算之后，相应的位变为1，Bloom过滤器 则记录下该关键词。需要注意的是，当Bloom过滤器里的关键词增加时，它对应的某个哈希函数的输出值的位可能已经 是1了，这种情况下，该位不会再次改变。也就是说，随着更多的关键词指向了重复的位，Bloom过滤器随着位1的增加而饱和，准确性也因此降低了。该过滤器之所以是基于概率的数据结构，就是因为关键词的增加会导致准确性的降低。 准确性取决于关键字的数量以及数组大小（N）和哈希函数的多少（M）。更大的数组和更多的哈希函数会记录更多的关键词以提高准确性。而小的数组及有限的哈希函数只能记录有限的关键词从而降低准确性。
&lt;img src=&quot;http://www.btccfo.com/wp-content/uploads/2018/11/bloom3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;验证关键词“X”是否在前述Bloom过滤器中（相应的比特位都被置为1，所以这个关键词很有可能是匹配的）&lt;/p&gt;

    &lt;p&gt;为测试某一关键词是否被记录在某个Bloom过滤器中，我们将该关键词逐一代入各哈希函数中运算，并将所得的结果与原数组进行对比。如果所有的结果对应的位都变为了1，则表示这个关键词有可能已被该过滤器记录。之所以这一结论并不确定，是因为这些字节1也有可能是其他关键词运算的重叠结果。简单来说，Bloom过滤器正匹配代表着“可能是”。
&lt;img src=&quot;http://www.btccfo.com/wp-content/uploads/2018/11/bloom4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;验证关键词“Y”是否存在于简易Bloom过滤器中&lt;/p&gt;

    &lt;p&gt;如果我们代入关键词计算后的结果某位为0，说明该关键词并没有被记录在过滤器里。负匹配的结果不是可 能，而是一定。也就是说，负匹配代表着“一定不是”。
&lt;img src=&quot;http://www.btccfo.com/wp-content/uploads/2018/11/bloom5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;0x03spv节点如何使用bloom过滤器&quot;&gt;0x03.SPV节点如何使用Bloom过滤器？&lt;/h3&gt;

&lt;p&gt;SPV节点将初始化“过滤器”为“空”;在该状态下，Bloom过滤器将不匹配任何模式。&lt;/p&gt;

&lt;p&gt;然后，SPV节点将列出所有感兴趣的地址，密钥和散列，它将通过从其钱包控制的任何UTXO中提取公钥哈希和脚本哈希和交易ID来实现。 SPV节点然后将其中的每一个添加到Bloom过滤器，以便如果这些模式存在于交易中，则Bloom过滤器将“匹配”，而不会自动显示模式。&lt;/p&gt;

&lt;p&gt;然后，SPV节点将向对等体发送一个过滤器加载消息，其中包含在连接上使用的bloom过滤器。在对等体上，针对每个传入交易检查Bloom过滤器。完整节点根据bloom过滤器检查交易的几个部分，寻找匹配，包括：交易ID、每个交易输出的锁定脚本的数据组件（脚本中的每个键和哈希）、 每个交易输入、每个输入签名数据组件（或见证脚本）。&lt;/p&gt;

&lt;p&gt;通过检查所有这些组件，可以使用Bloom过滤器来匹配公钥哈希，脚本，OP_RETURN值，签名中的公钥或智能合同或复杂脚本的任何未来组件。&lt;/p&gt;

&lt;p&gt;在建立过滤器之后，对等体然后将针对Bloom过滤器测试每个交易的输出。只有与过滤器匹配的交易才会发送到节点。&lt;/p&gt;

&lt;p&gt;响应于来自节点的getdata消息，对等体将发送一个merkleblock消息，该消息仅包含与过滤器匹配的块和每个匹配交易的merkle路径的块头。然后，对等体还将发送包含由过滤器匹配的交易的tx消息。&lt;/p&gt;

&lt;p&gt;由于完整节点向SPV节点发送交易，SPV节点丢弃任何误报，并使用正确匹配的交易来更新其UTXO集和钱包余额。随着它更新自己的UTXO集视图，它还会修改Bloom过滤器，以匹配任何引用其刚刚发现的UTXO的交易。然后，完整节点使用新Bloom过滤器来匹配新交易，并重复整个过程。&lt;/p&gt;

&lt;p&gt;设置bloom过滤器的节点可以通过发送filteradd消息将模式交互式添加到过滤器。要清除bloom过滤器，节点可以发送一个过滤清除消息。因为不可能从布局过滤器中删除模式，所以如果不再需要模式，则节点必须清除并重新发送新的Bloom过滤器。&lt;/p&gt;

&lt;p&gt;(END)&lt;/p&gt;</content><author><name></name></author><category term="blog" /><category term="spv" /><category term="bloom filter" /><summary type="html">这两天利用空闲时间终于把之前遗留的几个关于布隆过滤器的问题搞明白了，那种感觉无法用语言表达出来，有相同经历的人应该会懂。</summary></entry><entry><title type="html">如何彻底删除已经提交至github上的一次提交(commit)</title><link href="/blog/2018/12/19/how-to-delete-github-commit.html" rel="alternate" type="text/html" title="如何彻底删除已经提交至github上的一次提交(commit)" /><published>2018-12-19T00:00:00+00:00</published><updated>2018-12-19T00:00:00+00:00</updated><id>/blog/2018/12/19/how-to-delete-github-commit</id><content type="html" xml:base="/blog/2018/12/19/how-to-delete-github-commit.html">&lt;p&gt;昨天不小心提交个人代码至github时，在提交记录中带上了公司的邮箱信息。原因是github desktop使用本地的git配置信息，在提交前忘了修改。&lt;/p&gt;

&lt;p&gt;公司安全同学为了防止公司代码信息泄露，会定期扫描github，我相信邮箱后缀肯定在关键词列表中。为了避免不必要的麻烦，我决定删除这条记录。&lt;/p&gt;

&lt;p&gt;一般已经push的代码回滚会使用git revert命令，但是这样还会保留原提交记录，不满足我的需求。
而git reset –hard虽然可以回滚源码和提交记录，但是只适用于本地代码仓库。&lt;/p&gt;

&lt;p&gt;所以为了删除这条提交记录唯一的方法只能是： &lt;strong&gt;先删除github上的仓库https://github.com/xxx/test,然后新建一个空项目test，接着把本地代码回滚至上一版本，最后再重新修改git配置提交代码，并push到github上。&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;step1删除github上的仓库test直接登录网站操作即可&quot;&gt;Step1:删除github上的仓库test。直接登录网站操作即可。&lt;/h3&gt;

&lt;h3 id=&quot;step2本地代码回滚至上一版本&quot;&gt;Step2:本地代码回滚至上一版本&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;先备份代码&lt;/li&gt;
  &lt;li&gt;git log确定上一提交的commit id&lt;/li&gt;
  &lt;li&gt;执行命令git reset –hard [commit id]回滚至上一次提交&lt;/li&gt;
  &lt;li&gt;将备份代码覆盖回滚代码文件，并提交(务必注意修改git config中user信息)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;step3提交至github&quot;&gt;Step3：提交至github&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;新建一个Github仓库test&lt;/li&gt;
  &lt;li&gt;通过命令行cd进本地仓库；&lt;/li&gt;
  &lt;li&gt;使用以下语句将本地代码及记录push到远程仓库：&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;git remote add origin1 https://github.com/xxx/test&lt;/p&gt;

  &lt;p&gt;git push -u origin1 master&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;参考：https://www.cnblogs.com/qq952693358/p/7806152.html&lt;/p&gt;</content><author><name></name></author><category term="blog" /><category term="git" /><category term="Github" /><summary type="html">昨天不小心提交个人代码至github时，在提交记录中带上了公司的邮箱信息。原因是github desktop使用本地的git配置信息，在提交前忘了修改。</summary></entry><entry><title type="html">git 跨库push代码</title><link href="/blog/2015/05/19/push-mutil-repo.html" rel="alternate" type="text/html" title="git 跨库push代码" /><published>2015-05-19T00:00:00+00:00</published><updated>2015-05-19T00:00:00+00:00</updated><id>/blog/2015/05/19/push-mutil-repo</id><content type="html" xml:base="/blog/2015/05/19/push-mutil-repo.html">&lt;h3 id=&quot;场景&quot;&gt;场景&lt;/h3&gt;

&lt;p&gt;项目代码从团队自己搭建的git库(A库)切换到基于gitlab的git库(B库),因此我在本地开发的代码需要提交到新库(B库)。&lt;/p&gt;

&lt;h3 id=&quot;具体操作步骤&quot;&gt;具体操作步骤&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;本地仓库与远程仓库建立连接&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;humyna@god:~/gitpushtest$ git remote add gitlab_repo git@11.11.11.11:gittest/mygit.git&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;列出已经存在的远程分支&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;humyna@god:~/gitpushtest$ git remote -v&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git_repo git@10.10.10.10:gittest/mygit.git (fetch)
git_repo git@10.10.10.10:gittest/mygit.git (push)
gitlab_repo git@11.11.11.11:gittest/mygit.git (fetch)
gitlab_repo git@11.11.11.11:gittest/mygit.git (push)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;检查当前状态&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;humyna@god:~/gitpushtest$ git status&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;位于分支 dev1
您的分支与上游分支 'git_repo/dev1' 一致。

无文件要提交，干净的工作区
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;将本地的提交push到gitlab的dev1分支上(注意：此处由于gitlab_repo上dev1分支的代码是基于本地上一个提交的代码，故不存在merge等操作，否则需要fetch–&amp;gt;merge–&amp;gt;push)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;humyna@god:~/gitpushtest$ git push gitlab_repo dev1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Counting objects: 80, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (30/30), done.
Writing objects: 100% (46/46), 8.24 KiB | 0 bytes/s, done.
Total 46 (delta 17), reused 0 (delta 0)
To git@11.11.11.11:gittest/mygit.git
   		99a59be..6b0cf26  dev1-&amp;gt; dev1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.由于公司项目设计机密，就不发gitlab的提交成功截图了，另地址和显示均作了替换处理&lt;/p&gt;

&lt;p&gt;2.git_repo只是git@10.10.10.10:gittest/mygit.git的别名而已，只要保证多个远程库别名不重复即可&lt;/p&gt;

&lt;p&gt;3.以上思路也可用于同时push代码到多个远程库中，如下就是将代码同时push到git_repo和gitlab_repo的dev1分支&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git push git_repo dev1
git push gitlab_repo dev1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;—-EOF—-&lt;/p&gt;</content><author><name></name></author><category term="blog" /><category term="git" /><category term="push" /><summary type="html">场景 项目代码从团队自己搭建的git库(A库)切换到基于gitlab的git库(B库),因此我在本地开发的代码需要提交到新库(B库)。 具体操作步骤 本地仓库与远程仓库建立连接 humyna@god:~/gitpushtest$ git remote add gitlab_repo git@11.11.11.11:gittest/mygit.git 列出已经存在的远程分支 humyna@god:~/gitpushtest$ git remote -v git_repo git@10.10.10.10:gittest/mygit.git (fetch) git_repo git@10.10.10.10:gittest/mygit.git (push) gitlab_repo git@11.11.11.11:gittest/mygit.git (fetch) gitlab_repo git@11.11.11.11:gittest/mygit.git (push) 检查当前状态 humyna@god:~/gitpushtest$ git status 位于分支 dev1 您的分支与上游分支 'git_repo/dev1' 一致。 无文件要提交，干净的工作区 将本地的提交push到gitlab的dev1分支上(注意：此处由于gitlab_repo上dev1分支的代码是基于本地上一个提交的代码，故不存在merge等操作，否则需要fetch–&amp;gt;merge–&amp;gt;push) humyna@god:~/gitpushtest$ git push gitlab_repo dev1 Counting objects: 80, done. Delta compression using up to 4 threads. Compressing objects: 100% (30/30), done. Writing objects: 100% (46/46), 8.24 KiB | 0 bytes/s, done. Total 46 (delta 17), reused 0 (delta 0) To git@11.11.11.11:gittest/mygit.git 99a59be..6b0cf26 dev1-&amp;gt; dev1 注意 1.由于公司项目设计机密，就不发gitlab的提交成功截图了，另地址和显示均作了替换处理 2.git_repo只是git@10.10.10.10:gittest/mygit.git的别名而已，只要保证多个远程库别名不重复即可 3.以上思路也可用于同时push代码到多个远程库中，如下就是将代码同时push到git_repo和gitlab_repo的dev1分支 git push git_repo dev1 git push gitlab_repo dev1 —-EOF—-</summary></entry><entry><title type="html">memcached入门</title><link href="/blog/2015/01/26/memcached-getting-started.html" rel="alternate" type="text/html" title="memcached入门" /><published>2015-01-26T00:00:00+00:00</published><updated>2015-01-26T00:00:00+00:00</updated><id>/blog/2015/01/26/memcached-getting-started</id><content type="html" xml:base="/blog/2015/01/26/memcached-getting-started.html">&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;

&lt;h3 id=&quot;下载&quot;&gt;下载&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://memcached.org/downloads&quot;&gt;下载地址&lt;/a&gt;，最新版本为1.4.22&lt;/p&gt;

&lt;h3 id=&quot;安装&quot;&gt;安装&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Debian/Ubuntu: apt-get install libevent-dev&lt;/li&gt;
  &lt;li&gt;Redhat/Centos: yum install libevent-devel&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;wget http://memcached.org/latest&lt;/p&gt;

  &lt;p&gt;tar -zxvf memcached-1.x.x.tar.gz&lt;/p&gt;

  &lt;p&gt;cd memcached-1.x.x&lt;/p&gt;

  &lt;p&gt;./configure &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make test &amp;amp;&amp;amp; sudo make install&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;启动&quot;&gt;启动&lt;/h3&gt;
&lt;p&gt;./memcached -d -m 1024 -l localhost -p 11211&lt;/p&gt;

&lt;p&gt;这会以守护程序的形式启动 memcached（-d），为其分配 1GB 内存（-m 1024），并指定监听 localhost，即端口 11211。&lt;/p&gt;

&lt;h3 id=&quot;连接到memcached&quot;&gt;连接到memcached&lt;/h3&gt;
&lt;p&gt;telnet localhost 11211&lt;/p&gt;

&lt;p&gt;如果一切正常，则应该得到一个 telnet 响应，它会指示 Connected to localhost（已经连接到 localhost）。&lt;/p&gt;

&lt;h3 id=&quot;常用命令说明&quot;&gt;常用命令说明&lt;/h3&gt;
&lt;p&gt;启动/结束
memcached -d -m 10 -u root -l localhost -p 11211 -c 256 -P /tmp/memcached.pid&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;-d 选项是启动一个守护进程，&lt;/li&gt;
  &lt;li&gt;-m 是分配给Memcache使用的内存数量，单位是MB，这里是10MB&lt;/li&gt;
  &lt;li&gt;-u 是运行Memcache的用户，这里是root&lt;/li&gt;
  &lt;li&gt;-l 是监听的服务器IP地址，如果有多个地址的话，这里指定了服务器的IP地址localhost&lt;/li&gt;
  &lt;li&gt;-p 是设置Memcache监听的端口，这里设置了12000，最好是1024以上的端口&lt;/li&gt;
  &lt;li&gt;-c 选项是最大运行的并发连接数，默认是1024，这里设置了256，按照服务器的负载量来设定&lt;/li&gt;
  &lt;li&gt;-P 是设置保存Memcache的pid文件&lt;/li&gt;
  &lt;li&gt;kill &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat /tmp/memcached.pid&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;获取运行状态&quot;&gt;获取运行状态&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;echo stats&lt;/td&gt;
          &lt;td&gt;nc localhost 11211&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;watch “echo stats&lt;/td&gt;
          &lt;td&gt;nc localhost 11211” (实时状态)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;客户端命令详解&quot;&gt;客户端命令详解&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;9 个memcached客户端命令可以分为三类：基本、高级、管理&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;基本memcached客户端命令&quot;&gt;基本memcached客户端命令&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;set、add、replace、get、delete&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
  &lt;li&gt;前三个命令是用于操作存储在 memcached 中的键值对的标准修改命令。&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;修改命令语法&quot;&gt;修改命令语法&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;command &amp;lt;key&amp;gt; &amp;lt;flags&amp;gt; &amp;lt;expiration time&amp;gt; &amp;lt;bytes&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;value&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&quot;修改命令参数说明&quot;&gt;修改命令参数说明&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;key 用于查找缓存值&lt;/li&gt;
  &lt;li&gt;flags 可以包括键值对的整型参数，客户机使用它存储关于键值对的额外信息&lt;/li&gt;
  &lt;li&gt;expiration time 在缓存中保存键值对的时间长度（以秒为单位，0 表示永远）&lt;/li&gt;
  &lt;li&gt;bytes 在缓存中存储的字节点&lt;/li&gt;
  &lt;li&gt;value 存储的值（始终位于第二行）&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;set&quot;&gt;set&lt;/h5&gt;

&lt;p&gt;set 命令用于向缓存添加新的键值对。如果键已经存在，则之前的值将被替换。&lt;/p&gt;

&lt;h5 id=&quot;add&quot;&gt;add&lt;/h5&gt;

&lt;p&gt;仅当缓存中不存在键时，add 命令才会向缓存中添加一个键值对。如果缓存中已经存在键，则之前的值将仍然保持相同，并且您将获得响应 NOT_STORED。&lt;/p&gt;

&lt;h5 id=&quot;replace&quot;&gt;replace&lt;/h5&gt;

&lt;p&gt;仅当键已经存在时，replace 命令才会替换缓存中的键。如果缓存中不存在键，那么您将从 memcached 服务器接受到一条 NOT_STORED 响应。&lt;/p&gt;

&lt;p&gt;后两个基本命令get和delete的语法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;command &amp;lt;key&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h5 id=&quot;get&quot;&gt;get&lt;/h5&gt;

&lt;h5 id=&quot;delete&quot;&gt;delete&lt;/h5&gt;

&lt;h3 id=&quot;高级memcached客户端命令&quot;&gt;高级memcached客户端命令&lt;/h3&gt;

&lt;p&gt;可以在memcached中使用的两个高级命令是gets和cas。gets和cas命令需要结合使用。使用这两个命令来确保不会将现有的名称/值对设置为新值（如果该值已经更新过）。&lt;/p&gt;

&lt;h5 id=&quot;gets&quot;&gt;gets&lt;/h5&gt;

&lt;p&gt;gets命令的功能类似于基本的get命令。两个命令之间的差异在于，gets返回的信息稍微多一些：64位的整型值非常像名称/值对的“版本”标识符。&lt;/p&gt;

&lt;p&gt;gets命令将返回一个额外的值，用于标识名称/值对。如果对此名称/值对执行另一个set命令，则gets 返回的额外值将会发生更改，以表明名称/值对已经被更新。&lt;/p&gt;

&lt;h5 id=&quot;cas&quot;&gt;cas&lt;/h5&gt;

&lt;p&gt;cas（check和set）是一个非常便捷的memcached命令，用于设置名称/值对的值（如果该名称/值对在您上次执行gets后没有更新过）。它使用与 set 命令相类似的语法，但包括一个额外的值：gets返回的额外值。&lt;/p&gt;

&lt;p&gt;从本质上说，同时使用 gets和 cas命令可以防止使用自上次读取后经过更新的名称/值对。&lt;/p&gt;

&lt;h3 id=&quot;缓存管理命令&quot;&gt;缓存管理命令&lt;/h3&gt;
&lt;p&gt;最后两个memcached命令用于监控和清理memcached实例。它们是stats和flush_all命令。&lt;/p&gt;

&lt;h5 id=&quot;stats&quot;&gt;stats&lt;/h5&gt;

&lt;p&gt;stats 命令的功能正如其名：转储所连接的 memcached 实例的当前统计数据。&lt;/p&gt;

&lt;h5 id=&quot;flush_all&quot;&gt;flush_all&lt;/h5&gt;

&lt;p&gt;flush_all 是最后一个要介绍的命令。这个最简单的命令仅用于清理缓存中的所有名称/值对。如果您需要将缓存重置到干净的状态，则 flush_all 能提供很大的用处。&lt;/p&gt;

&lt;h2 id=&quot;缓存性能&quot;&gt;缓存性能&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;使用高级 memcached 命令来确定缓存的性能。&lt;/p&gt;

  &lt;p&gt;stats 命令用于调优缓存的使用。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;有两种方法用于确定缓存的效率&quot;&gt;有两种方法用于确定缓存的效率&lt;/h3&gt;

&lt;h4 id=&quot;方法一&quot;&gt;方法一&lt;/h4&gt;

&lt;p&gt;需要注意的两个最重要的统计数据是get_hits和get_misses。这两个值分别指示找到名称/值对的次数（get_hits）和未找到名称/值对的次数（get_misses）。结合这些值，我们可以确定缓存的利用率如何。&lt;/p&gt;

&lt;p&gt;初次启动缓存时，可以看到get_misses会自然地增加，但在经过一定的使用量之后，这些get_misses值应该会逐渐趋于平稳 — 这表示缓存主要用于常见的读取操作。&lt;/p&gt;

&lt;p&gt;如果看到get_misses继续快速增加，而get_hits逐渐趋于平稳，则需要确定一下所缓存的内容是什么。可能缓存了错误的内容。&lt;/p&gt;

&lt;h4 id=&quot;方法二&quot;&gt;方法二&lt;/h4&gt;

&lt;p&gt;查看缓存的命中率（hit ratio）。缓存命中率表示执行get的次数与错过get的次数的百分比。要确定这个百分比，需要运行stats命令，用get_hits的数值除以cmd_gets。&lt;/p&gt;

&lt;p&gt;在理想情况下，可能希望得到更高的百分比—比率越高越好。查看统计数据并不时测量它们可以很好地判定缓存策略的效率。&lt;/p&gt;</content><author><name></name></author><category term="blog" /><category term="memcached" /><summary type="html">Getting Started 下载 下载地址，最新版本为1.4.22 安装 Debian/Ubuntu: apt-get install libevent-dev Redhat/Centos: yum install libevent-devel wget http://memcached.org/latest tar -zxvf memcached-1.x.x.tar.gz cd memcached-1.x.x ./configure &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make test &amp;amp;&amp;amp; sudo make install 启动 ./memcached -d -m 1024 -l localhost -p 11211 这会以守护程序的形式启动 memcached（-d），为其分配 1GB 内存（-m 1024），并指定监听 localhost，即端口 11211。 连接到memcached telnet localhost 11211 如果一切正常，则应该得到一个 telnet 响应，它会指示 Connected to localhost（已经连接到 localhost）。 常用命令说明 启动/结束 memcached -d -m 10 -u root -l localhost -p 11211 -c 256 -P /tmp/memcached.pid -d 选项是启动一个守护进程， -m 是分配给Memcache使用的内存数量，单位是MB，这里是10MB -u 是运行Memcache的用户，这里是root -l 是监听的服务器IP地址，如果有多个地址的话，这里指定了服务器的IP地址localhost -p 是设置Memcache监听的端口，这里设置了12000，最好是1024以上的端口 -c 选项是最大运行的并发连接数，默认是1024，这里设置了256，按照服务器的负载量来设定 -P 是设置保存Memcache的pid文件 kill cat /tmp/memcached.pid 获取运行状态 echo stats nc localhost 11211 watch “echo stats nc localhost 11211” (实时状态) 客户端命令详解 9 个memcached客户端命令可以分为三类：基本、高级、管理 基本memcached客户端命令 set、add、replace、get、delete 前三个命令是用于操作存储在 memcached 中的键值对的标准修改命令。 修改命令语法 command &amp;lt;key&amp;gt; &amp;lt;flags&amp;gt; &amp;lt;expiration time&amp;gt; &amp;lt;bytes&amp;gt; &amp;lt;value&amp;gt; 修改命令参数说明 key 用于查找缓存值 flags 可以包括键值对的整型参数，客户机使用它存储关于键值对的额外信息 expiration time 在缓存中保存键值对的时间长度（以秒为单位，0 表示永远） bytes 在缓存中存储的字节点 value 存储的值（始终位于第二行） set set 命令用于向缓存添加新的键值对。如果键已经存在，则之前的值将被替换。 add 仅当缓存中不存在键时，add 命令才会向缓存中添加一个键值对。如果缓存中已经存在键，则之前的值将仍然保持相同，并且您将获得响应 NOT_STORED。 replace 仅当键已经存在时，replace 命令才会替换缓存中的键。如果缓存中不存在键，那么您将从 memcached 服务器接受到一条 NOT_STORED 响应。 后两个基本命令get和delete的语法 command &amp;lt;key&amp;gt; get delete 高级memcached客户端命令 可以在memcached中使用的两个高级命令是gets和cas。gets和cas命令需要结合使用。使用这两个命令来确保不会将现有的名称/值对设置为新值（如果该值已经更新过）。 gets gets命令的功能类似于基本的get命令。两个命令之间的差异在于，gets返回的信息稍微多一些：64位的整型值非常像名称/值对的“版本”标识符。 gets命令将返回一个额外的值，用于标识名称/值对。如果对此名称/值对执行另一个set命令，则gets 返回的额外值将会发生更改，以表明名称/值对已经被更新。 cas cas（check和set）是一个非常便捷的memcached命令，用于设置名称/值对的值（如果该名称/值对在您上次执行gets后没有更新过）。它使用与 set 命令相类似的语法，但包括一个额外的值：gets返回的额外值。 从本质上说，同时使用 gets和 cas命令可以防止使用自上次读取后经过更新的名称/值对。 缓存管理命令 最后两个memcached命令用于监控和清理memcached实例。它们是stats和flush_all命令。 stats stats 命令的功能正如其名：转储所连接的 memcached 实例的当前统计数据。 flush_all flush_all 是最后一个要介绍的命令。这个最简单的命令仅用于清理缓存中的所有名称/值对。如果您需要将缓存重置到干净的状态，则 flush_all 能提供很大的用处。 缓存性能 使用高级 memcached 命令来确定缓存的性能。 stats 命令用于调优缓存的使用。 有两种方法用于确定缓存的效率 方法一 需要注意的两个最重要的统计数据是get_hits和get_misses。这两个值分别指示找到名称/值对的次数（get_hits）和未找到名称/值对的次数（get_misses）。结合这些值，我们可以确定缓存的利用率如何。 初次启动缓存时，可以看到get_misses会自然地增加，但在经过一定的使用量之后，这些get_misses值应该会逐渐趋于平稳 — 这表示缓存主要用于常见的读取操作。 如果看到get_misses继续快速增加，而get_hits逐渐趋于平稳，则需要确定一下所缓存的内容是什么。可能缓存了错误的内容。 方法二 查看缓存的命中率（hit ratio）。缓存命中率表示执行get的次数与错过get的次数的百分比。要确定这个百分比，需要运行stats命令，用get_hits的数值除以cmd_gets。 在理想情况下，可能希望得到更高的百分比—比率越高越好。查看统计数据并不时测量它们可以很好地判定缓存策略的效率。</summary></entry><entry><title type="html">git cherry-pick使用场景</title><link href="/blog/2015/01/07/git-cherry-pick.html" rel="alternate" type="text/html" title="git cherry-pick使用场景" /><published>2015-01-07T06:55:13+00:00</published><updated>2015-01-07T06:55:13+00:00</updated><id>/blog/2015/01/07/git-cherry-pick</id><content type="html" xml:base="/blog/2015/01/07/git-cherry-pick.html">&lt;ul&gt;
  &lt;li&gt;git cherry-pick用法&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;git cherry-pick commit_id1 commit_id2&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;功能说明&lt;/p&gt;

    &lt;p&gt;“复制”一个或多个已提交节点并在当前分支做一次完全一样的新提交，这个新的提交的哈希值和原来的不同，但标识名一样&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;功能示意图&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://i.imgur.com/YWowgd7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;使用场景&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们在dev1上进行bug修复，并作了一次提交，然后被告知，“抱歉，今天需要紧急上线dev1分支代码，你这个bug没有时间测试，需要将这次提交移到dev2分支上进行测试。“ 这时候就是cherry-pick显神威的地方了。
可以进行如下操作:&lt;/p&gt;

&lt;p&gt;1.切换到dev2分支，注意需要确保dev2分支干净&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;git checkout dev2&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;2.然后执行cherry-pick命令&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;git cherry-pick commit_id&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;3.对于dev1分支的提交，切换到执行revert命令,撤销此次提交&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;git checkout dev1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;git revert &lt;commit&gt;&lt;/commit&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(完)
本文使用MarkdownPad 2编辑完成&lt;/p&gt;</content><author><name></name></author><category term="blog" /><category term="git" /><category term="cherry-pick" /><summary type="html">git cherry-pick用法 git cherry-pick commit_id1 commit_id2 功能说明 “复制”一个或多个已提交节点并在当前分支做一次完全一样的新提交，这个新的提交的哈希值和原来的不同，但标识名一样 功能示意图 使用场景 我们在dev1上进行bug修复，并作了一次提交，然后被告知，“抱歉，今天需要紧急上线dev1分支代码，你这个bug没有时间测试，需要将这次提交移到dev2分支上进行测试。“ 这时候就是cherry-pick显神威的地方了。 可以进行如下操作: 1.切换到dev2分支，注意需要确保dev2分支干净 git checkout dev2 2.然后执行cherry-pick命令 git cherry-pick commit_id 3.对于dev1分支的提交，切换到执行revert命令,撤销此次提交 git checkout dev1 git revert (完) 本文使用MarkdownPad 2编辑完成</summary></entry><entry><title type="html">[最佳实践]Windows7下搭建写博环境</title><link href="/blog/2014/03/16/blogging-based-on-jekyll.html" rel="alternate" type="text/html" title="[最佳实践]Windows7下搭建写博环境" /><published>2014-03-16T00:00:00+00:00</published><updated>2014-03-16T00:00:00+00:00</updated><id>/blog/2014/03/16/blogging-based-on-jekyll</id><content type="html" xml:base="/blog/2014/03/16/blogging-based-on-jekyll.html">&lt;h2 id=&quot;软件下载&quot;&gt;软件下载&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Windows 7 64bit&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rubyforge.org/frs/download.php/76054/rubyinstaller-1.9.3-p194.exe&quot;&gt;RubyInstaller 1.9.3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/oneclick/rubyinstaller/downloads/&quot;&gt;DevKit-DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Jekyll 1.4.2&lt;/li&gt;
  &lt;li&gt;Rdiscount&lt;/li&gt;
  &lt;li&gt;Github客户端&lt;/li&gt;
  &lt;li&gt;Gvim7.3&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;安装配置&quot;&gt;安装配置&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;1.下载RubyInstaller并安装，然后在命令行终端下输入gem update –system来升级gem，然后分别输入ruby -v和gem -v检验安装版本&lt;/li&gt;
  &lt;li&gt;2.下载最新的DevKit(DevKit是windows平台下编译和使用本地C/C++扩展包的工具。它就是用来模拟Linux平台下的make,gcc,sh来进行编译。这个方法目前仅支持通过RubyInstaller安装的Ruby)并双击运行解压到C:\DevKit。然后打开终端cmd，输入下列命令进行安装：
  cd C:\DevKit
  ruby dk.rb init
  ruby dk.rb install&lt;/li&gt;
  &lt;li&gt;3.完成上面的准备就可以安装Jekyll了,因为Jekyll是用Ruby编写的,最好的安装方式是通过RubyGems(gem):
  gem install Jekyll –version “=1.4.2”&lt;/li&gt;
  &lt;li&gt;4.安装Rdiscount，这个用来解析Markdown标记的包，使用如下命令：
  gem install rdiscount&lt;/li&gt;
  &lt;li&gt;5.安装Github on windows，安装完桌面有Github和git shell两个快捷方式&lt;/li&gt;
  &lt;li&gt;6.安装Gvim7.3，并配置markdown插件&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;写作环境搭建&quot;&gt;写作环境搭建&lt;/h2&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1.在github.com注册账号
2.创建名为humyna.github.io的仓库
3.Clone in Desktop
4.打开git shell,使用如下命令clone Jekyll-Bootstrap到本地
git clone https://github.com/plusjade/jekyll-bootstrap.git Jekyll-Bootstrap
5.将Jekyll-Bootstrap文件夹中除.git外的所有文件copy到humyna.github.io文件夹中
6.修改_config.yml中相关的配置，设置博客主题
7.在git shell中，cd到humyna.github.io文件夹下，执行命令jekyll serve --watch(不加--watch则不会检测文件夹内的变化，即修改后需要重新启动jekyll),即可以通过http://localhost:4000访问本地的博客环境
8.执行rake post title=&quot;hello world&quot;即可在_posts文件夹下生成一个md文件，使用vim编辑进行博客创作，保存后可以在浏览器中看到效果。
9.完成写作后，使用github的客户端完成提交，这样就能将完成的博文发布到网上(humyna.github.io)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;注意事项&quot;&gt;注意事项&lt;/h2&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1.Ruby默认安装后在用户环境变量中已添加（path=C:\Ruby193\bin）无需再配置
2.Ruby和DevKit解压路径不能有空格
3.Jekyll应该安装1.4.2版本(使用jekyll -v检查是否安装成功)并配置用户环境变量解决中文编码问题LC_ALL=en_US.UTF-8和Lang=en_US.UTF-8
如果此时使用gem 安装软件会报错：
ERROR:  While executing gem ... (ArgumentError)
invalid byte sequence in UTF-8
需要先删除这两个环境变量，等完装完再加上。
4.Gvim需要设置默认编码为utf-8,不然打开含有中文文件会有乱码
5.可以绑定自己的域名，在根目录下闯将CNAME文件并将域名加进去，同时配置域名映射即可。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name></name></author><category term="blog" /><category term="jekyll" /><summary type="html">软件下载 Windows 7 64bit RubyInstaller 1.9.3 DevKit-DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe Jekyll 1.4.2 Rdiscount Github客户端 Gvim7.3 安装配置 1.下载RubyInstaller并安装，然后在命令行终端下输入gem update –system来升级gem，然后分别输入ruby -v和gem -v检验安装版本 2.下载最新的DevKit(DevKit是windows平台下编译和使用本地C/C++扩展包的工具。它就是用来模拟Linux平台下的make,gcc,sh来进行编译。这个方法目前仅支持通过RubyInstaller安装的Ruby)并双击运行解压到C:\DevKit。然后打开终端cmd，输入下列命令进行安装： cd C:\DevKit ruby dk.rb init ruby dk.rb install 3.完成上面的准备就可以安装Jekyll了,因为Jekyll是用Ruby编写的,最好的安装方式是通过RubyGems(gem): gem install Jekyll –version “=1.4.2” 4.安装Rdiscount，这个用来解析Markdown标记的包，使用如下命令： gem install rdiscount 5.安装Github on windows，安装完桌面有Github和git shell两个快捷方式 6.安装Gvim7.3，并配置markdown插件 写作环境搭建 1.在github.com注册账号 2.创建名为humyna.github.io的仓库 3.Clone in Desktop 4.打开git shell,使用如下命令clone Jekyll-Bootstrap到本地 git clone https://github.com/plusjade/jekyll-bootstrap.git Jekyll-Bootstrap 5.将Jekyll-Bootstrap文件夹中除.git外的所有文件copy到humyna.github.io文件夹中 6.修改_config.yml中相关的配置，设置博客主题 7.在git shell中，cd到humyna.github.io文件夹下，执行命令jekyll serve --watch(不加--watch则不会检测文件夹内的变化，即修改后需要重新启动jekyll),即可以通过http://localhost:4000访问本地的博客环境 8.执行rake post title=&quot;hello world&quot;即可在_posts文件夹下生成一个md文件，使用vim编辑进行博客创作，保存后可以在浏览器中看到效果。 9.完成写作后，使用github的客户端完成提交，这样就能将完成的博文发布到网上(humyna.github.io) 注意事项 1.Ruby默认安装后在用户环境变量中已添加（path=C:\Ruby193\bin）无需再配置 2.Ruby和DevKit解压路径不能有空格 3.Jekyll应该安装1.4.2版本(使用jekyll -v检查是否安装成功)并配置用户环境变量解决中文编码问题LC_ALL=en_US.UTF-8和Lang=en_US.UTF-8 如果此时使用gem 安装软件会报错： ERROR: While executing gem ... (ArgumentError) invalid byte sequence in UTF-8 需要先删除这两个环境变量，等完装完再加上。 4.Gvim需要设置默认编码为utf-8,不然打开含有中文文件会有乱码 5.可以绑定自己的域名，在根目录下闯将CNAME文件并将域名加进去，同时配置域名映射即可。</summary></entry></feed>