使用Github的Webhook实现代码库与应用的交互

很早之前,博主的的网站评论模块已经更改为了 gitalk,Gitalk是一个基于GitHub Issue和Preact的现代评论组件。更换评论组件后有一个问题:我不想每次去github上查看别人的评论,如果别人评论了文章,然后我能够在网站后台看到,这样就很方便了。此时,github的webhook功能就可以登场了。 1. 简介 Webhook,翻译过来可以称为网络钩子,用来将Github上的一系列事件信息回传到某一回调地址上,从而完成与外部应用的交互,它是github提供的一种与外部交互的入口。Github上提供了很多交互事件,当某一事件被触发后,如果设置了Webhook的回调地址,Github将会通过HTTP POST请求将事件信息发送到回调地址上,回调处理应用通过接收事件信息然后实现自身的业务需求。 目前,每个组织或代码库上最多只能创建20个webhook。 一个典型的业务场景是:代码保存在github上,如果稳定的master分支上提交了代码,就触发持续集成系统如Jenkins进行代码构建、打包、部署等系列操作。 2. 事件 配置webhook时,可以选择订阅的事件。一般情况下,我们只需要订阅关注的事件,github也支持一个匹配所有支持事件的通配符(*),添加通配符事件时,github将使用通配符事件替换您配置的任何现有事件,并为所有支持的事件发送有效负载。如果将来添加了新的可匹配的事件,那么将会自动订阅。您可以随时通过API或UI更改订阅事件列表。默认情况下,webhooks仅订阅push事件。 每个事件对应于您的组织和/或存储库可能发生的某组操作。例如,如果您订阅了问题事件,则每次打开,关闭,标记等issue时您都会收到详细的有效负载。github现支持的事件列表见 #events[附录一]。 2.1. 有效负载 有效负载,简单理解就是消息所携带的内容和信息。 每种事件类型都具有特定的有效负载格式以及相关的事件信息,事件的有效负载来源于具体的事件类型的有效负载,但原始push事件除外,它的具有更详细的webhook有效负载。 除了为每个事件记录的字段之外,webhook有效负载还包括执行事件的用户(发送者)、组织和事件发生的存储库。GitHub app的webhook有效负载还可能包括安装事件。 一个marketplace_purchase事件类型的有效负载的示例如下: { "action":"purchased", "effective_date":"2017-10-25T00:00:00+00:00", "sender":{ "login":"username", "id":3877742, "avatar_url":"https://avatars2.githubusercontent.com/u/3877742?v=4", "gravatar_id":"", "url":"https://api.github.com/users/username", "html_url":"https://github.com/username", "followers_url":"https://api.github.com/users/username/followers", "following_url":"https://api.github.com/users/username/following{/other_user}", "gists_url":"https://api.github.com/users/username/gists{/gist_id}", "starred_url":"https://api.github.com/users/username/starred{/owner}{/repo}", "subscriptions_url":"https://api.github.com/users/username/subscriptions", "organizations_url":"https://api.github.com/users/username/orgs", "repos_url":"https://api.github.com/users/username/repos", "events_url":"https://api.github.com/users/username/events{/privacy}", "received_events_url":"https://api.github.com/users/username/received_events", "type":"User", "site_admin":true, "email":"username@email.com" }, "marketplace_purchase":{ "account":{ "type":"Organization", "id":18404719, "login":"username", "organization_billing_email":"username@email.com" }, "billing_cycle":"monthly", "unit_count":1, "on_free_trial":false, "free_trial_ends_on":null, "next_billing_date":"2017-11-05T00:00:00+00:00", "plan":{ "id":435, "name":"Basic Plan", "description":"Basic Plan", "monthly_price_in_cents":1000, "yearly_price_in_cents":10000, "price_model":"per-unit", "has_free_trial":true, "unit_name":"seat", "bullets":[ "Is Basic", "Because Basic " ] } } } ...

2019-07-02 · 2 min · 408 words · Hank

Spring AMQP消息转换

上一篇,我们介绍了如果使用Spring AMQP注解来实现消息发送和监听,示例都是使用的默认的消息转换器,即SimpleMessageConverter,它只能处理byte[]、String、java序列化对象(实现了Serializable接口的对象)。 通常,不推荐使用Java序列化,因为它存在与Java对象强耦合、依赖java语言等缺点,Spring AMQP也提供了其他的消息转换方式,在本篇,我们将重点来看看如果将消息序列化为JSON格式。 1. MessageConverter Spring AMQP消息转换定义了顶层接口MessageConverter,它的定义如下: public interface MessageConverter { // 将对象转换为Message对象,支持自定义消息属性 Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException; // 将Message转换为对象 Object fromMessage(Message message) throws MessageConversionException; } 它定义了两个方法:将对象转换为Message,将Message转换为对象。 同时,在AmqpTemplate中定义了便捷的消息转换和发送的方法: void convertAndSend(Object message) throws AmqpException; void convertAndSend(String routingKey, Object message) throws AmqpException; void convertAndSend(String exchange, String routingKey, Object message) throws AmqpException; void convertAndSend(Object message, MessagePostProcessor messagePostProcessor) throws AmqpException; void convertAndSend(String routingKey, Object message, MessagePostProcessor messagePostProcessor) throws AmqpException; void convertAndSend(String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor) throws AmqpException; ...

2019-06-17 · 3 min · 488 words · Hank

Spring AMQP注解的使用

上一篇 "Spring AMQP简介和使用",我们介绍了Spring AMQP的一些基本要素和概念,也通过一些示例代码介绍了消息的发送和接收,但是都是使用的原始编码方式来实现,并不依赖Spring环境。其实,Spring AMQP也支持使用注解的方式来进行异步接收消息,极大的简化了编码。 1. hello world 要使用注解,首先需要在Spring应用环境中,我们看一个最简单的demo: 1、重新新建一个Spring boot工程,添加如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-amqp</artifactId> <version>1.7.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>1.7.6.RELEASE</version> </dependency> 2、新建一个Spring配置类RabbitConfiguration,用来申明Bean: @Configuration public class RabbitConfiguration { public static final String ANONYMOUS_QUEUE_NAME = "spring.amqp.anonymous.queue"; @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory("192.168.0.27", 5672); cachingConnectionFactory.setUsername("admin"); cachingConnectionFactory.setPassword("123456"); return cachingConnectionFactory; } @Bean public AmqpAdmin amqpAdmin() { return new RabbitAdmin(connectionFactory()); } @Bean public RabbitTemplate rabbitTemplate() { return new RabbitTemplate(connectionFactory()); } @Bean public Queue anonymousQueue() { // 匿名队列 return new AnonymousQueue(() -> ANONYMOUS_QUEUE_NAME); } } ...

2019-06-13 · 5 min · 990 words · Hank

Spring AMQP简介和使用

1. 前言 很早之前写过几篇关于RabbitMQ的一些基础文章,在本篇中,我们将来学习Spring AMQP。 Spring AMQP 是 Spring 对 AMQP( http://www.amqp.org) 协议的封装和扩展,它提供了模板来对消息的发送和接收进行高级抽象,还提供了基于消息驱动的POJO的支持(类似JMS,Java消息服务)。 在开始之前,首先需要了解一些 RabbitMQ 和 AMQP 的一些基础概念,并在你的机器上安装 RabbitMQ, 本文使用的spring-amqp的版本为1.7.6。 2. Spring AMQP抽象 Spring AMQP 是 Spring 对 AMQP 协议的封装和扩展,提供了消息发送和接收的模板。Spring AMQP项目将核心Spring概念应用于基于 AMQP 的消息传递解决方案的开发,以便更容易和简单的管理AMQO资源。 Spring AMQP由spring-amqp和spring-rabbit两个模块组成。spring-amqp模块位于org.springframework.amqp.core包,它的目标是提供不依赖于任何特定AMQP代理实现或客户端库的通用抽象;而spring-rabbit是spring-amqp通用抽象的具体实现,目前仅提供了rabbitmq的实现。 Spring AMQP包括一些基本的抽象定义(上边说过,他们位于org.springframework.amqp.coreb包中,而非AMQP协议本身定义): 2.1. Message 在0-9-1版本的 AMQP 规范中没有定义 Message 类或接口,当执行诸如 basicPublish() 操作时,内容作为字节数组参数传递,而其他属性作为单独的参数传递。Spring AMQP将 Message 类定义为更通用的AMQP域模型表示的一部分。Message该类的目的是将主体和属性封装在单个实例中,以便API可以更简单。以下示例显示了Message类定义: public class Message { private final MessageProperties messageProperties; private final byte[] body; public Message(byte[] body, MessageProperties messageProperties) { this.body = body; this.messageProperties = messageProperties; } public byte[] getBody() { return this.body; } public MessageProperties getMessageProperties() { return this.messageProperties; } } ...

2019-06-05 · 8 min · 1703 words · Hank

Spring boot集成quartz并实现任务调度

最近在实现任务调度服务,使用 quartz调度框架,由于使用的spring boot版本为1.5.10,该版本并没有提供quartz的starter,在集成的时候还是遇到了很多问题。本文并不会详细介绍quartz框架,请自行查阅相关资料。如果对Spring boot不太熟悉,可以看前边几篇关于spring boot的文章。 1. quartz简介 1.1. 核心概念 scheduler:任务调度器,相当于指挥中心,负责管理和调度job,由SchedulerFactory创建和关闭,创建后需要通过starter()方法来启动调度。 trigger:触发器,定义job的触发时机,通过TriggerBuilder来创建,有SimpleTrigger和CronTrigger,前者可以定义在某一个时刻触发或者周期性触发,后者使用cron表达式来定义触发时间。 Job:具体的调度业务逻辑实现,也就是任务,具体任务需要实现org.quartz.Job接口。 JobDetail:org.quartz.JobDetail用来描述Job实例的属性,并且可以通过JobDataMap传递给Job实例数据,通过JobBuilder来创建。 1.2. quartz配置 quartz框架默认会通过quartz.properties文件来加载配置信息,有几个重要的配置项: org.quartz.scheduler.*:调度器相关配置 org.quartz.threadPool.*:线程池相关配置 org.quartz.jobStore.*:quartz数据存储相关配置,在quartz中,有三种存储类型: RAMJobStore:基于内存存储job JobStoreCMT:基于数据库的数据存储,并且受运行的java容器的事务控制 JobStoreTX:基于数据库的数据存储,不受事务控制 org.quartz.dataSource.*:配置数据库存储quartz数据时的数据源信息 org.quartz.plugin.*:quartz插件相关配置 quartz简单介绍这么多,更多信息请看 这里。 2. Spring boot与quartz集成 前边说过,我使用的spring boot版本为1.5.10,没有quartz的starter,所以需要自己编码实现。同时,我还需要对quartz做持久化处理,需要在mysql库中导入quartz的建表脚本。集成步骤如下: 1、引入依赖 引入quartz的依赖包,quartz-jobs根据实际需要决定是否引入。 <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.3</version> </dependency> 2、创建表 在下载的quartz包中有建表脚本,我的为quartz-2.2.3,建表脚本在/docs/dbTables下边,导入tables_mysql_innodb.sql即可。 3、配置文件 在resources目下新建一个quartz.properties配置文件,我的配置如下: #============================================================================ # Configure Main Scheduler Properties #============================================================================ org.quartz.scheduler.instanceName:event-scheduler org.quartz.scheduler.instanceId:AUTO org.quartz.scheduler.skipUpdateCheck:true #============================================================================ # Configure ThreadPool #============================================================================ org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount:5 org.quartz.threadPool.threadPriority:5 #============================================================================ # Configure JobStore #============================================================================ org.quartz.jobStore.misfireThreshold:60000 org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.useProperties:false org.quartz.jobStore.tablePrefix:QRTZ_ org.quartz.jobStore.isClustered:false #============================================================================ # Configure Datasources #============================================================================ org.quartz.dataSource.ds.maxConnections:7 #============================================================================ # Configure Plugins #============================================================================ org.quartz.plugin.triggHistory.class: org.quartz.plugins.history.LoggingTriggerHistoryPlugin ...

2019-01-22 · 4 min · 647 words · Hank

Spring boot工程Feign请求时Get请求无法传递body的问题

前一篇,我们解决了Spring的RestTemplate在发送Get请求不能传递body,相比RestTemplate,我们更多的还是采用更方便的申明式服务调用框架Feign。本篇不会介绍Feign,而是解决Feign发送Get请求时仍然不能传递body的问题。 1. 准备 在这里,我使用的Spring boot的版本为1.5.10.RELEASE,Spring Cloud版本为Edgware.SR3。 准备三个服务:注册中心、服务生产者、服务消费,这里我们继续使用在 Spring Cloud服务注册中心Eureka定义的三个服务:服务注册中心01-eureka-server、服务提供者01-service-demo,同时新建一个服务消费者01-feign-consumer,使用feign来请求服务而不是ribbon,记得在启动类加上@EnableFeignClients启用Feign。 1、在服务提供者01-service-demo添加一个接口,代码如下: @GetMapping("/hello/user") public String sayHello(@RequestBody User user) { return "hello, " + user.getName() + ", give you a gift " + user.getGift(); } 这样,Get请求需要接收body数据。 2、在服务消费者01-feign-consumer编写如下代码: (1)在SayHelloClient中添加如下声明式服务请求: @FeignClient("hello-service") public interface SayHelloClient { @GetMapping(value = "/hello/user") String sayHello(@RequestBody User user); } (2)在SayHelloController中调用SayHelloClient: @RestController public class SayHelloController { private static Logger log = LoggerFactory.getLogger(SayHelloController.class); @Autowired private ApiProductClient apiProductClient; @GetMapping("/user") public String userHello() { User user = new User(); user.setName("lily"); user.setGift("birthday card"); return sayHelloClient.sayHello(user); } } ...

2019-01-22 · 1 min · 125 words · Hank

使用Spring的RestTemplate发送GET请求,并支持传递Request body参数

最近在使用Spring Boot实现微服务,都是使用RESTful风格的Api接口,服务间使用RestTemplate来进行HTTP通信,遇到这样一个需求:开发一个查询请求Api,参数使用JSON格式的字符串来提交。 1. 请求格式 希望的请求格式如下: GET /pointCard/ HTTP/1.1 Host: localhost:8100 Content-Type: application/json;charset=UTF-8 Content-Length: 114 {"iColumns":7,"iDisplayLength":10,"iDisplayStart":0,"iSortingCols":0,"sColumns":"","sEcho":1,"subjectId":"11227"} 在RESTful下,这样的设计是合理的,GET请求表示从服务器获取资源,但需要将查询参数以JSON格式来提交。但是,这违背了传统的GET请求的规范,我们都知道,GET请求只能将请求参数拼接URI后边,而不能单独传递request body参数,除非你改用POST。 2. 代码实现 我们先来编一个上述请求的API,然后进行测试。 1、编写一个API: @GetMapping(value = "/") public Response getById(@RequestBody @Valid PointCardQuery query) throws Exception { Assert.notNull(query,"查询条件不能为空!"); …… return Response.success(pointCardPurePager, "积分卡获取成功!"); } 上边的代码片段处于一个Restcontroller,要求使用GET方法,并且使用了@RequestBody注解来获取request body参数。 2、我们使用RestTemplate来测试一下: @Test public void testGetWithBody() { RestTemplate restTemplate = new RestTemplate(); String p = "{\"iColumns\":7,\"iDisplayLength\":10,\"iDisplayStart\":0,\"iSortingCols\":0,\"sColumns\":\"\",\"sEcho\":1,\"subjectId\":\"11227\"}"; String url = "http://localhost:8100/pointCard/"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); HttpEntity<String> httpEntity = new HttpEntity<>(p, headers); ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class); String body = responseEntity.getBody(); System.out.println(body); System.out.println(responseEntity.getStatusCode()); System.out.println(responseEntity.getStatusCodeValue()); System.out.println(responseEntity); } ...

2018-10-31 · 2 min · 341 words · Hank

Spring Boot参数验证(下)——Bean Validation在Web中的应用

在 Spring Boot参数验证(上)--Bean Validation及其Hibernate实现 一篇中,我们介绍了验证标准Bean Validation和其Hibernate实现,在本篇,我们看看它们是如何应用在Spring Boot Web项目中。 1. Spring Validator 其实,Spring很早就有了自己的Bean验证机制,其核心为Validator接口,表示校验器: public interface Validator { // 检测Validator是否支持校验提供的Class boolean supports(Class<?> clazz); // 校验逻辑,校验的结果信息通过errors获取 void validate(@Nullable Object target, Errors errors); } Errors接口,用以表示校验失败的错误信息: public interface Errors { // 获取被校验的根对象 String getObjectName(); // 校验结果是否有错 boolean hasErrors(); // 获取校验错误数量 int getErrorCount(); // 获取所有错误信息,包括全局错误和字段错误 List<ObjectError> getAllErrors(); // 获取所有字段错误 List<FieldError> getFieldErrors(); …… } 当Bean Validation被标准化过后,从Spring3.X开始,已经完全支持JSR 303(1.0)规范,通过Spring的LocalValidatorFactoryBean实现,它对Spring的Validator接口和javax.validation.Validator接口进行了适配。 1.1. 全局Validator 全局Validator通过上述的LocalValidatorFactoryBean类来提供,只要使用@EnableWebMvc即可(Xml配置开启<mvc:annotation-driven>),也可以进行自定义: @Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public Validator getValidator(); { // return "global" validator } } ...

2018-10-12 · 3 min · 624 words · Hank

Spring boot全局异常处理和自定义异常页面

Spring boot提供了默认的异常处理机制,但是难以满足业务需要,一般需要编码来实现自己的业务处理机制。在本篇,将介绍如何自定义异常页面,和进行全局的异常处理。 1. 全局异常处理 如果系统业务处理发生异常,我们希望能够将异常信息以友好的形式返回给调用方,而不是仅在后台记录日志,尤其是在开发RESTFul的API时,需要将业务异常信息进行明确定义并返回给API调用方,这显得尤为重要。 现在,我们来定义一个全局的业务异常类BusinessException,如果业务处理失败抛出该类或者其子类,然后编写一个全局异常处理器,将异常转换为有好的信息并返回。 1、定义异常类 public class BusinessException extends Exception { public BusinessException() { super(); } public BusinessException(String msg) { super(msg); } public BusinessException(Throwable cause) { super(cause); } public BusinessException(String message, Throwable cause) { super(message, cause); } public BusinessException(MsgDefinition msgDefinition) { super(msgDefinition.msgOf()); this.msgDef = msgDefinition; } public MsgDefinition msgDef() { return msgDef == null ? MsgDefinition.UNKOWN_ERROR : msgDef; } } 其中的MsgDefinition为具体的错误信息,包含错误码code和错误提示信息msg,子类继承该类进行扩展: public class MsgDefinition { public static final MsgDefinition SUCCESS = new MsgDefinition("0000", "请求成功"); public static final MsgDefinition EMPTY_ARGUMENTS = new MsgDefinition("4001", "请求参数为空"); public static final MsgDefinition ILLEGAL_ARGUMENTS = new MsgDefinition("4002", "请求采参数非法"); public static final MsgDefinition FILE_SIZE_OVER_LIMIT = new MsgDefinition("4301", "文件大小超过限制"); public static final MsgDefinition FILE_NUMBER_OVER_LIMIT = new MsgDefinition("4302", "文件数量超过限制"); public static final MsgDefinition FILE_FORMAT_UNSUPPORTED = new MsgDefinition("4310", "文件格式不支持"); public static final MsgDefinition UNKOWN_ERROR = new MsgDefinition("9999", "系统未知异常"); private String code; private String msg; public MsgDefinition(String code, String msg) { this.code = code; this.msg = msg; } public String codeOf() { return this.code; } public String msgOf() { return this.msg; } @Override public String toString() { return JsonUtil.toJson(this); } } ...

2018-10-10 · 2 min · 383 words · Hank

Spring Boot参数验证(上)——Bean Validation及其Hibernate实现

通常,web项目都需要对请求的参数进行校验,一般是前端JavaScript需要校验,然后后台还需要再进行校验,而校验逻辑大多数是重复的。如果在后台通过硬编码的方式,对Controller参数进行逐个校验,将会非常耗时和低效的,尤其是在使用Spring Boot和Spring Cloud的微服务体系中,大多数服务均会对外提供RESTFul的api,如果有统一的验证机制和框架,将大大提高生产力。幸运的是,Java已经提供了标准的验证API,称为Bean Validation。 1. 简介 1.1. Bean Validation Bean Validation,是JCP(Java Community Process)定义的标准化的JavaBean校验API,基于注解,并且具有良好的易用性和扩展性,1.0版本定义为 /attachment/20181009/28f52319fd84452ba22b490ff911fd04.pdf[JSR 303],而现在发布了2.0版本,定义为 JSR 380,了解详细信息可以看 这里。 Bean Validation并不局限于应用程序的某一层或者哪种编程模型, 它可以被用在任何一层, 也可以是像Swing这样的富客户端程序中. 目标:简化Bean校验,将以往重复的校验逻辑进行抽象和标准化,形成统一API规范; 版本变化:JSR 303在2009发布了1.0Final版,而最新的是在2017年发布的Bean Validation 2.0,被定义为 JSR 380。 需要注意的是,Bean Validation只是一个规范和标准,并没有提供实现,而接下来介绍的hibernate validator就是它的一种实现。 1.2. Hibernate Validator 是JSR 380的一种标准实现,同时还对其进行了扩展,如增加了部分验证约束。目前,最新的稳定版本为 6.0.13.Final。 2. 入门示例 接下来,我们使用Spring Boot来编写一个简单的示例工程。如果对Spring Boot不熟悉的可以看这几篇文章: "Spring Boot之基础入门" "SpringBoot-工程结构、配置文件以及打包" "Spring Boot JPA使用详解" 1、添加maven依赖 前边说过,JSR 303定义了JavaBean的验证标准,而Hibernate Validator是它的一种实现,所以,这两个的jar包我们都需要添加到工程。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> 其实,spring-boot-starter-web启动器已经依赖了hibernate-validator,而后者很明显会依赖validation-api。 ...

2018-10-10 · 8 min · 1686 words · Hank

Spring Cloud服务注册中心Eureka高可用

在 上一篇,我们通过编码,分别编写了服务注册中心、服务提供者、服务消费者,但是服务注册中心仍然是单节点,如果服务注册中心不可用,则会导致整个体系的所有服务都不可用。所以,有必要保证服务注册中心的高可用,本章将来讨论整个问题。 1. Eureka集群 对于Eureka而言,其本身是支持多节点集群的。其原理大致如下:服务注册中心的多个实例彼此互相注册,形成注册中心的集群,同时对外提供服务,即使其中一个挂掉,其他实例仍然可以对外提供服务,从而保证了高可用。 Figure 1. Eureka双节点结构示意图 以双节点为例,如上图所示,两个服务注册中心(Eureka Server)彼此相互注册形成集群,服务提供者(Service Provider)会自动向两者都进行服务注册,即是说,两个服务注册中心有用相同的服务实例列表,服务消费者(Service Consumer)会采用负载均衡算法从服务注册中心集群中选择出一个可用的服务注册中心,来获取服务注册实例,并完成请求。 上一篇已经说过,默认情况下,每一个Eureka服务端同样也是一个Eureka客户端,因此至少需要配置一个其他的Eureka服务端URL来让自己完成注册。如果不提供这个服务端URL,服务端本身可以正常运行和提供服务,但是控制台会打印许多无法成功注册的日志信息。在上一篇在编码单节点服务注册中心时,我们做了如下配置,让其不作为客户端让其他服务注册中心注册: eureka.client.register-with-eureka=false eureka.client.fetch-registry=false 这两项配置默认均为true,即是说eureka默认是开启集群功能的。 2. 编码实战 接下来,我们参照上一篇的服务注册中心、服务提供者、服务消费者代码,来组建一个双节点的服务注册中心,并通过服务消费者获取服务实例,完成远程请求。 2.1. 服务注册中心 1、新建一个名为02-eureka-server-nodes的Spring Boot工程,作为服务注册中心 2、引入如下依赖和插件: <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> 引入spring-boot-maven-plugin的目的是为了打可执行jar包,由于要组建双节点集群,需要启动两次,所以不能直接在idea里边运行工程了,需要打包成可执行jar,通过命令行来启动。 3、启动类添加@EnableEurekaServer注解,表明该工程为Eureka服务端 4、配置多环境,新建三个配置文件:application.properties、application-node1.properties、application-node2.properties,分别进行如下配置: application.properties: spring.application.name=eureka-server # 开发用,关闭服务端自我保护,防止关闭的实例不能被剔除 eureka.server.enable-self-preservation=false application-node1.properties: server.port=8080 eureka.instance.hostname=eureka-node1 # 服务注册中心访问地址 eureka.client.service-url.defaultZone=http://eureka-node2:8081/eureka application-node2.properties: server.port=8081 eureka.instance.hostname=eureka-node2 # 服务注册中心访问地址 eureka.client.service-url.defaultZone=http://eureka-node1:8080/eureka 需要注意的是,eureka-node1需要向eureka-node2来注册服务,而eureka-node2需要向eureka-node1来注册服务,彼此交叉注册,注意配置地址的不同。 5、由于在本机测试,并且这里通过hostname来访问服务,所以需要让hostname可以访问,修改hosts文件进行本机映射: 127.0.0.1 eureka-node1 127.0.0.1 eureka-node2 各操作系统修改hosts文件的方式不同,请自行查阅相关资料。 6、到这里编码工作就已完成,现在,使用maven命令进行打包: ``mvn clean package`` ...

2018-08-16 · 1 min · 202 words · Hank

Spring Cloud服务注册中心Eureka

上一篇,我们学习了Spring Cloud配置集成化组件Config,在本篇,我们来学习Spring Cloud微服务架构的一个核心组件----Eureka。 1. 服务治理 服务治理,即将体系中各个微服务统一纳入服务注册中心,由注册中心维护各个微服务的名称、地址、端口、状态等信息,并提供服务自动化注册和发现机制。 为什么需要服务治理? 对于传统应用而言,应用数量少,各应用间的API调用我们可以通过在配置文件中写入IP地址和端口的方式来进行维护。那么对于现在的微服务架构而言,应用的服务数量上百甚至上千,如果还使用传统的配置方式来维护各个服务信息及其其调用管理,那么无疑是困难重重,不仅容易出错,而且成本直线上升。 <p style="text-align: center;">没有服务治理时,如何管理各个服务? <p style="text-align: left;">在微服务体系中,服务实例的网络位置都是动态分配的,而且实例的地址可能会经常性的改变,有了服务治理,不再需要人为的维护各个服务信息,一切交给服务注册中心来自动管理和维护,大大提高了效率。 几个核心概念 服务注册表:维护了服务实例的网络地址、名称等信息的数据库 服务提供者:提供RESTApi以供其他服务调用的被调用方 服务消费者:调用服务端提供的RESTApi接口,发起服务请求的请求方 服务注册:将服务注册表中提供服务信息进行注册服务的行为 服务发现:从服务注册表中查询服务实例信息的行为 接下来,我们看看服务发现的两种模式。 服务发现模式 服务发现主要有两种模式:服务端发现模式和客户端发现模式。 1、客户端发现模式 即由客户端来获取服务实例列表和网络位置,并且进行负载均衡。客户端查询服务注册表(一个维护了服务实例和其网络位置等信息的数据库),获取服务实例列表,并且采用负载均衡算法,从列表中选择一个服务实例作为请求目标。 Figure 1. 客户端发现模式示意图 Eureka采用的就是客户端发现模式。 2、服务端发现模式 即由服务端来发现服务实例并进行负载均衡:客户端通过负载均衡器向某个服务提出请求,负载均衡器查询服务注册表,并将请求转发到可用的服务实例。 Figure 2. 服务端发现模式示意图 AWS Elastic Load Balancer(ELB)采用的是服务端发现模式。 本篇讲解的Spring Cloud Eureka采用的是客户端发现模式,接下来我们看看eureka的基本概念。 2. Eureka简介 Eureka,字面意思即"发现"之意,是Netflix下开源的服务治理(注册、发现等)中间件。Spring Cloud Eureka在其基础上进行了二次封装,添加了自动化配置等功能,使其成为Spring Cloud微服务体系中核心的服务治理方案之一。 Eureka包括三个角色,如下图所示: Figure 3. Eureka基础架构(图片来源网络) Eureka Server:服务注册中心,维护了服务注册表,提供服务注册、服务发现、刷新和取消注册等功能 Service Provider:服务提供者,对外提供服务,一般为RESTApi接口 Service Consumer:服务消费者,调用服务提供者接口,实现具体业务逻辑 由上图可见,Service Provider将注册中心注册服务,Service Consumer从注册中心查询服务注册表,获取服务实例信息,并通过负载均衡算法获取到一个服务提供者实例,并发起调用请求。 Eureka Server并没有后端存储机制,注册表中的所有服务实例必须发送心跳监测以保证正确连接(内存中实现),服务实例(客户端)会将服务端注册表拉去到本地并缓存在内存中,而不是每次都向服务端发送请求来获取服务实例注册信息。 前边介绍了Eureka和服务治理相关的理论,接下来,我们来编程实现服务注册中心。 3. 单节点服务注册中心 3.1. 编写服务注册中心 1、新建名为01-eureka-server的Spring Boot工程,作为服务注册中心 2、引入如下依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> ...

2018-08-15 · 2 min · 247 words · Hank

Spring Cloud Config之配置集成化

在 Spring Cloud简介中,我们了解了Spring Cloud的基本概念、特点、各大组件和其作用,还介绍了Spring Cloud的版本定义和发布规则,对Spring Cloud应该有了一定的了解。在本篇,我们来看看实现配置集成化的重要组件----Spring Cloud Config。 1. 简介 Spring Cloud Config是一款配置集中管理的组件,它提供了分布式系统的客户端和服务端外部化配置支持,由git(也支持svn、本地文件系统等)存储库支持的集中式外部配置管理,实现了配置的外部化存储。 Spring Cloud Config有几个核心的概念: 配置仓库:集中存储配置文件的地方, 推荐git仓库,也可以使用svn仓库或者本地文件系统; Server:配置管理服务端,提供配置文件管理服务,通过server可以按照一定规则读取配置文件,它直接访问配置仓库; Client:配置客户端,即需要从Server读取配置的应用,它并不直接访问配置仓库,而是通过Server读取配置信息; git仓库本身使用频率高,可以进行版本跟踪等特点,所以spring推荐使用git仓库,本文也将重点介绍基于git仓库的配置中心。 2. 配置服务端 服务端支持的配置仓库有git、svn和native本地文件,spring.profiles.active配置项用于启用配置文件支持的配置仓库,默认为git。在使用git配置仓库有几个重要概念: application: 访问配置服务的名称,默认为application,可以通过spring.config.name配置 profile:激活的profile配置文件,多个以逗号分隔 label:通常为git的分支,默认为master,也可以为commit id、tag名称;如果分支或tag名称包含斜线“/”,那么在HTTP URL中需要更换为"(_)" 下边先简单介绍使用本地文件和本地git仓库搭建配置服务端。 2.1. 使用本地文件 1、新建config-server-native工程,引入依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> 2、启动类加上@EnableConfigServer注解,表明该工程为配置服务端 3、新建本地配置仓库文件夹: ``mkdir /Users/sun/Desktop/belonk/config-native`` 在config-native文件夹中建三个文件:application-dev.properties,application-uat.properties,application-pro.properties,分别配置部分内容以区别不同环境(profile)。 4、application.properties添加如下配置: spring.profiles.active=native # 采用推荐的8888端口 server.port=8888 spring.config.name=config # 配置本地文件目录地址 spring.cloud.config.server.native.searchLocations=/Users/sun/Desktop/belonk/config-native spring.cloud.config.server.native.addLabelLocations=true # 配置本地git仓库地址 #spring.cloud.config.server.git.uri=file:///Users/sun/Desktop/belonk/config-center/ spring.profiles.active:默认为git,改为native启用本地文件系统配置仓库 spring.config.name:访问URL的application名称 spring.cloud.config.server.native.searchLocations:配置本地搜索配置文件的位置,默认包括[classpath:/, classpath:/config, file:./, file:./config] 5、启动应用,浏览器访问http://localhost:8888/config/pro,dev,可以看到显示了正确pro和dev的配置信息,需要注意的是这里的config来自spring.config.name配置。 由于本地文件系统没有版本控制,不便于管理和追述,建议仅仅用于测试,不建议生产上使用。 2.2. 使用本地git仓库 git仓库在本地文件系统,并未push到远端,可用于快速测试。我们在config-server-native工程上稍作修改,改为本地git仓库。 ...

2018-08-14 · 2 min · 380 words · Hank

Visio替代者-亿图9.0破解版下载

1. 简介 亿图,一款windows平台的微软Office风格的绘图软件,不仅简单易用,而且拥有强大的功能,支持的图形图表种类非常丰富,所绘制的图形也十分美观,你可以用来轻松绘制各种图形, 如流程图、框图、思维导图、工程设计图、平面图、时装设计等等。 官网: https://www.edrawsoft.com/cn/ 2. Visio和亿图 Visio是微软收购的一款功能强大的绘图软件,但是其门槛较高,没有一定的专业知识和使用经验很难画出漂亮的图形;而亿图提供了直接从visio导入图形文件的功能,可以说对visio进行了一定程度的兼容,而且使用门槛低,使用亿图提供的强大的丰富多样的模板就可以绘制漂亮的图形,简单方便。 简单而言,Visio适合专业人员使用,是一款专业的绘图工具,功能也非常强大,熟练掌握过后能够绘制出非常专业的图形;而亿图专门为非专业人士设计,提供强大多样的模板,普通使用者也可以快速上手,制作符合需要的漂亮图形,简单、方便、高效! 我们以绘制电路图为例,来看看亿图为什么可以替代visio。 (一)、使用Visio画电路图有哪些不足之处? 滑动变阻器不能调节滑片位置,只能在正中间。即使不连接某一侧接线柱,也会显示出来。 Visio可使用的模板和例子有限。 Visio支持的绘图类型少,内置符号也不是很多,绘图时可选择性不高,画出来的图表美感不足。 Visio的云共享功能需要依靠Microsoft Sharepoint才能使用。 Visio不支持钢笔工具的功能。 Visio只能在Windows上使用,不支持Mac和Linux系统。 (二)、亿图软件为什么可以替代Visio? 亿图自带基本电气模板,有常用的电路元件,可直接拖动到图中。 亿图软件具有多种图形建模功能,除了适用软件内置的符号、图形,还可通过修改已有图形形状或用绘图工具绘制建立新图形,甚至可以从外界输入图形放入模板中调用。 拖拽式绘图,只需在亿图符号库中选中所需图形、符号,然后轻轻按住鼠标,拖放到绘图区即可。 亿图内置丰富的符号,适用范围广。无论是思维导图、流程图、组织结构图、项目管理类图,还是工艺图、地图、科技类图,适用亿图都可轻松绘制。 亿图软件自带云共享功能,其中包含个人云和团队云,随时随地保存,随时随地分享! 亿图软件具有PS下相类似的钢笔工具的功能,可以自己设计图形、符号。 操作界面与微软的 Office 系列软件相似,使用简单、方便,无需复杂学习。 具有再次编辑的功能,即使用亿图软件导出为word、ppt、Visio等格式的文件,可以进行再次编辑。 作为一款纯矢量的绘图软件,使用亿图绘制的图表,无论放大多少倍依旧十分清晰。 亿图与Visio完美衔接,可以任意导入和导出为Visio格式的问题件,而且导出后还可以在Visio上继续编辑。 亿图支持跨平台使用,可以在Windows、Mac以及Linux上同时使用。 3. 下载和破解 1、从百度云盘下载: 链接 https://pan.baidu.com/s/1eFC9Lo418qqaSW0p_9hAYw 2、解压压缩包,安装edrawmax-cn-9.0.exe到指定目录 3、将crack文件夹里的2个dll拷贝到亿图安装目录下替换源文件 4、破解完成! 4. 软件部分截图 Figure 1. 新建绘图 Figure 2. 逻辑结构图 Figure 3. 流程图 5. 申明 本站提供的破解软件仅供个人学习用,切勿用于商业用途! 本站不对使用破解软件造成的任何损失承担责任! 经济允许的话,请支持正版软件!

2018-07-31 · 1 min · 57 words · Hank

Spring Cloud简介

1. 简介 微服务的实施,涉及的内容非常多,包括服务治理、配置中心、服务网关、服务熔断、分布式事务、集群、消息总线等等,各个部分开源的产品也非常多: 服务治理:阿里的Dubbo以及当当维护的DubboX、Netflix的Eureka、Apache的Consul等; 配置中心:Spring Cloud的Config、百度的Disconf、Netflix的Archaius、淘宝的Diamond等; 服务跟踪:Spring Cloud的Sleuth、Twitter的Zipkin等 服务网关:Netflix的Zuul等 …… 开源组件如此众多,在实施微服务时,势必带来技术选型方面的困扰,不仅要了解各个技术的作用、易用性、优缺点,还需要知道其他组件对其支持程度,以及组件之间的依赖、协作关系,因此,很多公司在一开始就心生畏惧甚至直接放弃。 为了解决上述问题,Spring Cloud应运而生。它不单独解决微服务某一方面的问题,而是提出了微服务一整套解决方案。Spring Cloud整合了很多开源的微服务产品,针对微服务某一方面的问题,它提供了默认的实现,从而极大的简化了微服务实施的技术难度,使得技术人员不必再关注技术选型方面的问题,大大节约了成本。 简言之,Spring Cloud是一套构建于Spring boot之上的微服务框架,它整合了大部分微服务实施所需的组件,并提供了默认实现,使得复杂的微服务实施变得简单化。官网地址: http://projects.spring.io/spring-cloud/ 2. 特点 Spring Cloud专注于为典型用例提供良好的开箱即用体验,并为其他用户提供可扩展性机制。 分布式/版本化配置 服务注册和发现 路由 服务间调用 负载均衡 断路器 分布式消息 3. 组件 Spring Cloud整合了微服务架构所需的大部分开源组件,包括: Spring Cloud Config 即配置管理工具,由git(也支持svn、本地文件系统等)存储库支持的集中式外部配置管理,实现了配置的外部化存储。 Spring Cloud Netflix 核心组件,整合了Netflix OSS开源的组件,包括许多子组件: Eureka:服务治理组件,包括服务注册、发现,对Netflix的Eureka进行整合; Hystrix:容错管理组件,断路器,对Netflix的Hystrix进行了整合; Ribbon:客户端负载均衡,也是Netflix的开源组件,Spring Cloud进行了整合; Feign:基于Ribbon和Hystrix的声明式服务调用,整合Netflix的Feign; Zuul:网关组件,智能路由、访问过滤,整合Netflix的Zuul; Archaius:外部化配置组件,整合netflix的Archaius; Spring Cloud Bus 轻量级消息总线,用于集群间通信,传播集群间状态改变,例如动态更新配置。 Cloud Foundry 将您的应用程序与Pivotal Cloud Foundry集成。提供服务发现实现,还可以轻松实现受SSO和OAuth2保护的资源。 Spring Cloud Open Service Broker 提供构建实现Open Service Broker API的服务代理的起点。 Spring Cloud Cluster 基于Zookeeper,Redis,Hazelcast,Consul的选举和通用状态模式的抽象实现。 Spring Cloud Consul Hashicorp Consul的服务发现和配置管理。 Spring Cloud Security 为Zuul代理中的负载平衡OAuth2 rest客户端和身份验证头中继提供支持。 Spring Cloud Sleuth Spring Cloud应用程序的分布式跟踪,兼容Zipkin,HTrace和基于日志(例如ELK)的跟踪。 Spring Cloud Data Flow 组合微服务应用程序的本地云编排服务,提供了易用的DSL、拖放式GUI和REST-API共同简化了基于数据管道的微服务整体编排。 Spring Cloud Stream 轻量级事件驱动的微服务框架,可快速构建可连接到外部系统的应用程序。采用声明式模型,在Spring Boot应用程序之间使用Apache Kafka或RabbitMQ发送和接收消息。 Spring Cloud Stream App Starters 基于Spring Boot的Spring应用程序集成,可提供与外部系统的集成。 Spring Cloud Task 一种短命的微服务框架,用于快速构建执行有限数据处理的应用程序。用于向Spring Boot应用程序添加功能和非功能功能的简单声明。 Spring Cloud Task App Starters Spring Cloud Task App Starters是Spring Boot应用程序,可能是任何进程,包括不能永久运行的Spring Batch作业,它们在有限的数据处理期后结束/停止。 Spring Cloud Zookeeper 使用Apache Zookeeper进行服务发现和配置管理。 适用于Amazon Web Services的Spring Cloud 与托管的Amazon Web Services轻松集成。它提供了一种使用众所周知的Spring习语和API(如消息传递或缓存API)与AWS提供的服务进行交互的便捷方式。开发人员可以围绕托管服务构建应用程序,而无需关心基础结构或维护。 Spring Cloud Connectors 使各种平台中的PaaS应用程序可以轻松连接到数据库和消息代理(该项目以前称为“Spring Cloud”)等后端服务。 Spring Cloud CLI Spring Boot CLI插件,用于在Groovy中快速创建Spring Cloud组件应用程序 Spring Cloud Gateway 一款基于Project Reactor的智能可编程路由器。 Spring Cloud OpenFeign 通过自动配置和Spring环境以及其他Spring编程模型习惯用法提供Spring Boot应用程序的集成。 Spring Cloud Function 通过函数促进业务逻辑的实现。它支持无服务器提供商之间的统一编程模型,以及独立运行(本地或PaaS)的能力。 ...

2018-07-20 · 2 min · 275 words · Hank

Spring Boot JPA使用详解

在上一篇SpringBoot-工程结构、配置文件以及打包中,我们介绍了Spring Boot项目的工程结构、基本配置、使用IDEA开发的配置,以及可执行jar包的结构和打包方式,对Spring Boot的项目有了整体的认识。在本篇,我们将介绍JPA的使用。 1. 简介 百度百科对JPA的解释是这样的: JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它又不限于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持久化标准规范。Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现。 简单来说,Hibernate这种ORM的持久化框架的出现极大的简化了数据库持久层的操作,随着使用人数的增多,ORM的概念越来越深入人心。因此,JAVA专家组结合Hibernate,提出了JAVA领域的ORM规范,即JPA。 JPQL 其实同Hibernate的HQL类似,是JPA标准的面向对象的查询语言。百度百科的解释如下: JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它又不限于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持久化标准规范。Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现。 JPA与Hibernate的关系 JPA是参考Hibernate提出的Java持久化规范,而Hibernate全面兼容JPA,是JPA的一种标准实现。 JPA与Spring Data JPA Spring Boot对JPA的支持其实使用的是Spring Data JPA,它是Spring对JPA的二次封装,默认实现使用的是Hibernate,支持常用的功能,如CRUD、分页、条件查询等,同时也提供了强大的扩展能力。 2. HelloWorld 僚机了JPA的概念过后,接下来,我们使用Spring Boot工程来实现一个最简单的CRUD操作,看看使用JPA我们需要做哪些事情。 引入依赖 直接引入如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> ...

2018-06-27 · 7 min · 1461 words · Hank

使用TpLink的DDNS让外网访问本地服务

1. 背景 很多时候,我们需要将本地服务暴露给外网访问,或者简单地说能够通过互联网访问本地电脑。比如,公司内网有一台服务器,上边的多个服务都需要在公司和在家里都能访问到,如代码库、数据库等;又比如,在开发微信公众号此类需要外网回调本地环境的应用,如果能够将本地服务暴露给外网访问,那么调试起来将非常方便。 目前,市面上有许多工具软件,能够实现上述目的,但是,一方面稳定性欠佳,另一方面几乎都需要付费,不能作为长期使用。 所以,本文介绍的是,如何使用TpLINK路由器提供的DDNS [1] 功能,改造家里的电信网络,将家里的电脑暴露给外网。 2. 准备 原网络服务商为电信,需要有电信光猫、一台TpLINK路由器(支持DDNS [1])、网线。由于我家里的网络原来都是已经连接好的,电信光猫下接了路由器,通过电信光猫和路由器都可以上网,所以本文是基于此来进行网络配置修改的,全部使用的是笔记本的无限网络访问路由器和光猫,并没有用网线直连光猫和路由器。 3. 整体思路 将电信光猫上网方式由原来的“路由”改为“桥接”,让其仅充当交换机的作用;然后配置TpLink路由器,所有的上网都通过TpLink而不是光猫;然后利用TpLink的DDNS和虚拟服务器功能,将本地端口映射到外网,通过DDNS域名就可以访问本地服务了。 4. 实现步骤 在我的网络环境中,以电信光猫为中心,下边连接了多个路由器,包括一台TpLink路由器,路由器和光猫都可以上网,具体怎么实现上述结构的请查阅相关资料。路由器地址:192.168.0.1,电信光猫地址:192.168.1.1。 4.1. 破解电信光猫 首先,电信光猫默认的useradmin账号登进去的权限很有限,不能修改网络设置。所以,我们需要破解之,为修改上网方式做准备。 使用useradmin和密码登录光猫(用户名和密码在光猫背面,一般为useradmin),然后访问http://192.168.1.1/backupsettings.conf,此时会下载光猫配置文件; 用记事本等文本工具打开,搜索TeleComAccount,然后你会看到如下的配置: <X_CT-COM_TeleComAccount> <Password>8MAB1223</Password> </X_CT-COM_TeleComAccount> Password就是光猫管理用户telecomadmin的密码,记录下来备用。 然后,再搜所Username,找到如下配置: <Username>CD68458918</Username> <Password>aAg0xDgADTgA</Password> 其中,Username就是宽带拨号上网的账号,Password就是其密码,不过是Base64的密文,在线找一个BASE64解码网站,就可以得到原始密码,记录下来备用。 然后,把http://devacs.edatahome.com:9090/ACS-server/ACS找到(只有一处),改成http://devacs.edatahome.com.disable:9090/ACS-server/ACS,使得无法连接远程管理服务器,然后找到 <TotalTerminalNumber>5</TotalTerminalNumber> 把5改成一个比较大的数字用以解除光猫接入设备数量限制。 最后,通过前边的telecomadmin和找到的密码,登录光猫,访问http://192.168.1.1/updatesettings.html,选择刚才修改的配置,让光猫启用最新配置(过程大概需要2分钟)。 ok,光猫破解完成。 4.2. 修改电信光猫桥接上网 光猫完成破解后,通过telecomadmin登录,找到状态菜单-用户侧信息,可以查看到当前WAN连接信息,如下图所示: 找到TpLink路由器连接的对应WAN连接,我这里是上边红框部分,截图是我已经修改成了桥接模式,所以地址获取方式一栏写的是BRIDGE。 然后,点击网络-网络设置,连接名称选择上边确定的TpLink连接的WAN连接,然后将连接模式改为桥接,如下图所示: 其他不用做任何修改。 保存过后,现在光猫是不能上网的,接下来,需要配置路由器上网。 4.3. TpLink宽带拨号上网 访问http://192.168.0.1,登录TpLink,点击左侧上网设置菜单,修改上网方式为宽带拨号上网(原来是默认的自动获得IP地址),如下图: 宽带账号和密码分别填入前边 #netaccount[破解光猫时]找到的宽带账号和密码,然后点击连接按钮。 现在,路由器可以正常上网了。但是由于更改了网络配置,现在不能通过192.168.1.1访问光猫了,要WIFI访问光猫,把路由器的上网方式还原为自动获取IP地址即可。 4.4. TpLink #ddns[DDNS]配置 点击路由器下边的应用管理,找到DDNS,点击进入,如图所示: 看到的界面如下: 服务提供商选择TP-LINK,在我的域名下会显示当前登录的DDNS域名,如果没有,点击创建新域名,然后登录即可,具体DDNS使用可以看 这里。 4.5. TpLink虚拟服务器端口映射 DDNS有了,但是还不能访问内网服务,还需要在路由器上做端口映射,将内网端口暴露到外网。 同样在应用管理,找到虚拟服务器,进入后可以看到如下图所示界面: 外部端口:外网能够访问的端口 内部端口:内部服务的端口 添加完成后,现在通过[DDNS域名]:[外部端口]就可以访问内网服务了,我这里是 http://belonk.tpddns.cn:8088。 大功告成!! 5. 结束语 TpLink目前是自带了DDNS,可以免费申请DDNS域名,免费而且稳定。在使用的时候,需要将TpLink路由器作为上网源,光猫只是充当交换器而不提供路由功能。 1. DDNS(Dynamic Domain Name Server)是动态域名服务的缩写。DDNS是将用户的动态IP地址映射到一个固定的域名解析服务上,用户每次连接网络的时候客户端程序就会通过信息传递把该主机的动态IP地址传送给位于服务商主机上的服务器程序,服务器程序负责提供DNS服务并实现动态域名解析。 ...

2018-06-09 · 1 min · 72 words · Hank

SpringBoot-工程结构、配置文件以及打包

前一篇,我们简单介绍了SpringBoot的概念,然后逐步构建了一个HelloWorld的Web应用。现在,我们来整体认识一下SpringBoot,包括:工程结构、配置文件、可执行Jar包等。 1. 工程结构 本质上而言,SpringBoot是一个遵循Maven标准的Maven工程,但是如果开发的是Web项目,与标准Maven存在一定的差别:默认情况下,SpringBoot打包为jar(简单方便,使用自带容器,不需要部署到tomcat等java容器,当然也可以打包为可执行war),此时,不能使用Maven的webapp目录来存放web资源(js、css、图片、页面等),SpringBoot有一套自己的规范,整个工程结构如下: \---src +---main | +---java // java后台代码 | \---resources // 资源文件目录 | application.properties // 配置文件 | +---static // 存放静态文件,如js、css、图片等 | \---templates // 存放模板文件 \---test \---java // 测试代码 默认情况下,Spring boot规定将静态资源存放到resources/static目录,而页面存放到resources/templates目录,也可以通过配置文件修改默认存放目录。 2. 代码结构 Spring Boot推荐的后台代码结构如下: +---src | +---main | | +---java | | | \---com | | | \---belonk | | | | WebappViewStarter.java // 包含main方法的启用类 | | | +---config | | | | CustomParamConfig.java | | | | RandomParamConfig.java | | | +---domain | | | +---service | | | \---web | | | HomeController.java ...

2018-06-07 · 3 min · 505 words · Hank

Intellij IDEA技巧-正则查询替换和纵向编辑

IntelliJ IDEA是一款非常强大的Java IDE,当然现在也支持多种语言,由JetBrains公司开发。IntelliJ IDEA在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、各类版本工具(git、svn、github等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。 使用IDEA的感觉就是,一开始可能不习惯(与Eclipse的设计思想完全不同),但是越用就会发现越强大!这里介绍IDEA的正则查询替换和纵向编辑两个非常强大的功能。 1. 正则查找替换 正则查找替换即:支持使用正则表达式来匹配或替换文本,并且可以通过<code>$n</code>来访问正则的分组。 举个例子:我想要将Mapper取值表达式后边的jdbcType=XXX去掉,怎么做?没有正则,那么我们只能使用多次替换。使用正则,那么我们只需将#{xxx,jdbctye=XXX}部分查询出来,并且按分组替换即可,如下图所示: 在IDEA按快捷键CTRL+R开启查找替换功能,输入正则{(w*),(w*=w*)},此时IDEA会自动高亮匹配的内容。然后再替换框中输入{},即替换后的内容保留匹配正则的第一个分组,此时,IDEA会有一个灰色的pop框来告诉你,该内容会被替换成什么内容(见图中的#{rank})。然后,直接点击replace all替换全部替换即可,也可以使用exclude和replace一个个排除和替换。 正则替换功能适用于需要在某些有规律的内容上进行编辑,只要正则能够匹配到这部分内容。 2. 纵向编辑 纵向编辑是我个人非常喜欢的功能之一,其实很多编辑器(例如VIM、SUBLIME)都支持。在IDEA中,要开启纵向编辑,先按住ALT键不松开,然后按住鼠标竖向移动选择即可。 同样举个简单的例子:将设我从某些地方拷贝了一串属性,现在想要将其定义到具体的Class属性中,如图所示: 假设字段类型都是String,我们需要做以下几步: 1、将首字母变为小写 2、在属性前边添加private String 3、在属性后边添加分号 我们看看使用纵向编辑怎么做: 如上图所示,先纵向选择过后,按HOME键,将光标跳转每一行首,然后SHIFT+→选择每一行首字母,再CTRL+SHIFT+U将选中部分转为大小写,然后在每一行前边添加内容,完成后按End键将光标跳转到每一行尾,输入分号,结束。整个过程中,光标始终处于纵向编辑状态。 这里用到了几个小技巧:SHIFT+左右箭头,可以选择一个字符;CTRL+SHIFT+左右箭头,可以选择一个单词;HOME和END分别跳转到行首和行尾。 接下来,我们看一个实际的综合使用这两个功能的例子。 3. 综合实例 3.1. 需求 我从API文档拷贝了请求API的内容片段,现在,我需要为这些字段建立Class,内容片段如下: "ParkOrder_ID": 131391,//订单ID "ParkOrder_OrderNo": "20170209150056708-B11110",//订单号码 "Parking_ID": 1,//停车场ID "Parking_Enable": 1,//停车场状态 "Parking_Key": "e0b3ca87caf3a415bb3b3f52ca8aa795",//停车场编码 "Parking_Name": "城市明珠停车场1",//停车场名字 "Parking_FreeTime": 2,//首次免费分钟 "Parking_FreeTimeout": 2,//超时免费分钟 "ParkOrder_CarNo": "粤B11110",//车牌号码 "ParkOrder_CarType": "3651",//停车缴费类型 "CarType_No": "3651",//车辆类型 "CarType_Name": "临时车",//车辆类型名字 "ParkOrder_EnterTime": "2017-02-09 15:00:56",//入场时间 "ParkOrder_EnterGateName": "入口车道1",//入口车道名字 "ParkOrder_EnterOperatorName": "管理员",//入场操作员 "ParkOrder_EnterImgPath": "/OrderImg/e0b3ca87caf3a415bb3b3f52ca8aa795/20170209/20170209150056708-B11110-0.jpg",//图片URL "ParkOrder_OutTime": null,//出场时间 "ParkOrder_OutGateName": null,//出口车道名字 "ParkOrder_OutOperatorName": null,//出口操作员名字 "ParkOrder_OutImgPath": null,//出口图片URL "ParkOrder_TotalAmount": null,//总金额 "ParkOrderStatus_No": "200",//停车状态200为 车辆入场 201为车辆出场 202为自动关闭 "ParkOrderStatus_Name": "已入场",//状态中文名字 "ParkOrder_Lock": 0,//车辆是否锁定 "WXUser_ID": null,//微信ID "WXUser_Openid": null,//微信openid "WXUser_Nickname": null,//微信nikename "WXUser_Headimg": null,//头像url "PayOrder_Status": "未支付",//支付状态 "PayOrder_PayedMoney": 0.0//支付金额 ...

2018-05-31 · 2 min · 394 words · Hank

微服务架构的理论基础 - 康威定律

可能出乎很多人意料之外的一个事实是,微服务很多核心理念其实在半个世纪前的一篇文章中就被阐述过了,而且这篇文章中的很多论点在软件开发飞速发展的这半个世纪中竟然一再被验证,这就是康威定律。 1. 概述 微服务是最近非常火热的新概念,大家都在追,也都觉得很对,但是似乎没有很充足的理论基础说明这是正确的,给人的感觉是 不明觉厉 。前段时间看了Mike Amundsen《远距离条件下的康威定律----分布式世界中实现团队构建》(是Design RESTful API的作者)在InfoQ上的一个分享,觉得很有帮助,结合自己的一些思考,整理了该演讲的内容。 可能出乎很多人意料之外的一个事实是,微服务很多核心理念其实在半个世纪前的一篇文章中就被阐述过了,而且这篇文章中的很多论点在软件开发飞速发展的这半个世纪中竟然一再被验证,这就是康威定律(Conway’s Law). 在康威的这篇文章中,最有名的一句话就是: Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. — Melvin Conway(1967) 中文直译大概的意思就是:设计系统的组织,其产生的设计等同于组织之内、组织之间的沟通结构**。看看下面的图片,再想想Apple的产品、微软的产品设计,就能形象生动的理解这句话。 用通俗的说法就是:组织形式等同系统设计。 这里的系统按原作者的意思并不局限于软件系统。据说这篇文章最初投的哈佛商业评论,结果程序员屌丝的文章不入商业人士的法眼,无情被拒,康威就投到了一个编程相关的杂志,所以被误解为是针对软件开发的。最初这篇文章显然不敢自称定律(law),只是描述了作者自己的发现和总结。后来,在Brooks Law著名的人月神话中,引用这个论点,并将其“吹捧”成了现在我们熟知“康威定律”。 2. 康威定律 Mike从他的角度归纳这篇论文中的其他一些核心观点,如下: 第一定律 Communication dictates design 组织沟通方式会通过系统设计表达出来 第二定律 There is never enough time to do something right, but there is always enough time to do it over 时间再多一件事情也不可能做的完美,但总有时间做完一件事情 第三定律 There is a homomorphism from the linear graph of a system to the linear graph of its design organization 线型系统和线型组织架构间有潜在的异质同态特性 ...

2018-05-29 · 2 min · 252 words · Hank

什么是微服务架构

在过去几年中,“微服务架构”这一术语如雨后春笋般涌现出来,它描述了一种将软件应用程序设计为一组可独立部署的服务的特定方式。虽然这种架构风格没有明确的定义,但在组织、业务能力上有一些共同的特征:自动化部署,端点智能化,语言和数据的去中心化控制。 “微服务” - 软件架构拥挤大街上的有一个新术语。虽然我们自然的倾向是轻蔑的一瞥将它一带而过,然而我们发现这一术语描述了一种越来越吸引人的软件系统风格。我们已看到,在过去的几年中有许多项目使用了这种风格,并且到目前为止结果都还不错,以致于这已变成了我们同事在构建企业级应用程序时默认使用的架构风格。然而,遗憾的是并没有太多的信息来概述什么是微服务风格以及怎样用这种风格。 简单来说,微服务架构风格 [1] 是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。 与单体风格作对比有助于开始解释微服务风格:单体应用程序被构建为单一单元。企业级应用程序通常由三部分组成:客户端侧用户接口(由运行于开发机上的浏览器里的HTML页面和Javascript组成),数据库(由插入到通用关系型数据库管理系统中的许多数据表格组成),服务端应用程序。服务端应用程序处理HTTP请求,执行领域逻辑,从数据库中检索、更新数据,选择、填充将要发送到浏览器的HTTP视图。服务端应用程序是一个单一的逻辑可执行单体 [2] 。系统的任何改变都将牵涉到重新构建和部署服务端的一个新版本。 这样的单体服务器是构建这样一个系统最自然的方式。处理请求的所有逻辑都运行在一个单一进程中,允许你使用编程语言的基本特性将应用程序划分类、函数和命名空间。你认真的在开发机上运行测试应用程序,并使用部署管道来保证变更已被正确地测试并部署到生产环境中。该单体的水平扩展可以通过在负载均衡器后面运行多个实例来实现。 单体应用程序可以是成功的,但人们日益对他们感到挫败,尤其是随着更多的应用程序被部署在云上。变更周期被捆绑在一起 —— 即使只变更应用程序的一部分,也需要重新构建并部署整个单体。长此以往,通常将很难保持一个良好的模块架构,这使得很难变更只发生在需要变更的模块内。程序扩展要求进行整个应用程序的扩展而不是需要更多资源的应用程序部分的扩展。 Figure 1. 单体和微服务 这些挫败导向了微服务架构风格:构建应用程序为服务套件。除了服务是可独立部署、可独立扩展的之外,每个服务都提供一个固定的模块边界。甚至允许不同的服务用不同的的语言开发,由不同的团队管理。 我们不会声称微服务风格是新颖的、创新的,其本质至少可以回溯到Unix的设计哲学。但我们的确认为没有足够的人仔细考虑微服务架构,并且如果使用它很多软件实现将会更好。 1. 微服务架构的特征 我们无法给出微服务架构风格的一个正式定义,但我们可以尝试去描述我们看到的符合该架构的一些共性。就概述共性的任何定义来说,并非所有的微服务架构风格都有这些共性,但我们期望大多数微服务架构风格展现出大多数特性。虽然本文作者一直是这个相当松散的社区的活跃用户,我们的目的是试图描述我们工作中和我们知道的一些团队的相似努力中的所见所闻。特别是我们不会制定一些可遵守的定义。 1.1. 通过服务组件化 只要我们一直从事软件行业,一个愿望就是通过把组件插在一起构建系统,如同我们看到的现实世界中事物的构造方式一样。在最近的二十年中,我们看到作为大多数语言平台一部分的公共库的大量汇编工作取得了很大的进展。 当谈到组件时,我们遭遇困难的定义:组件是什么。我们的定义是:组件是一个可独立替换和独立升级的软件单元。 微服务架构将使用库,但组件化软件的主要方式是分解成服务。我们把库定义为链接到程序并使用内存函数调用来调用的组件,而服务是一种进程外的组件,它通过web服务请求或rpc(远程过程调用)机制通信(这和很多面向对象程序中的服务对象的概念是不同的 [3] 。) 使用服务作为组件而不是使用库的一个主要原因是服务是可独立部署的。如果你有一个应用程序 [4] 是由单一进程里的多个库组成,任何一个组件的更改都导致必须重新部署整个应用程序。但如果应用程序可分解成多个服务,那么单个服务的变更只需要重新部署该服务即可。当然这也不是绝对的,一些变更将会改变服务接口导致一些协作,但一个好的微服务架构的目的是通过内聚服务边界和按合约演进机制来最小化这些协作。 使用服务作为组件的另一个结果是一个更加明确的组件接口。大多数语言没有一个好的机制来定义一个明确的 发布接口。通常只有文档和规则来预防客户端打破组件的封装,这导致组件间过于紧耦合。服务通过明确的远程调用机制可以很容易的避免这些。 像这样使用服务确实有一些缺点,远程调用比进程内调用更昂贵,因此远程API被设计成粗粒度,这往往更不便于使用。如果你需要更改组件间的责任分配,当你跨进程边界时,这样的行为动作更难达成。 直观的估计,我们观察到服务与运行时进程一一映射,但这仅仅是直观的估计而已。一个服务可能由多进程组成,这些进程总是被一起开发和部署,比如只被这个服务使用的应用进程和数据库。 1.2. 围绕业务能力组织 当想要把大型应用程序拆分成部件时,通常管理层聚焦在技术层面,导致UI团队、服务侧逻辑团队、数据库团队的划分。当团队按这些技术线路划分时,即使是简单的更改也会导致跨团队的时间和预算审批。一个聪明的团队将围绕这些优化,两害取其轻 - 只把业务逻辑强制放在它们会访问的应用程序中。换句话说,逻辑无处不在。这是Conway法则 [5] 在起作用的一个例子。 任何设计系统(广泛定义的)的组织将产生一种设计,他的结构就是该组织的通信结构。 — Melvyn Conway 1967 Figure 2. Conway法则在起作用 微服务采用不同的分割方法,划分成围绕业务能力组织的服务。这些服务采取该业务领域软件的宽栈实现,包括用户接口、持久化存储和任何外部协作。因此,团队都是跨职能的,包括开发需要的全方位技能:用户体验、数据库、项目管理。 Figure 3. 团队边界增强的服务边界 www.comparethemarket.com是按这种方式组织的一个公司。跨职能团队负责创建和运营产品,产品被划分成若干个体服务,这些服务通过消息总线通信。 大型单体应用程序也总是可以围绕业务能力来模块化,虽然这不是常见的情况。当然,我们将敦促创建单体应用程序的大型团队将团队本身按业务线拆分。我们看到这种情况的主要问题是他们趋向于围绕太多的上下文进行组织。如果单体横跨了多个模块边界,对团队个体成员来说,很难把它们装进他们的短期记忆里。另外,我们看到模块化的路线需要大量的规则来强制实施。服务组件所要求的更加明确的分离,使得它更容易保持团队边界清晰。 侧边栏:微服务有多大? 虽然,“微服务”已成为这种架构风格的代称,这个名字确实会导致不幸的聚焦于服务的大小,并为“微”由什么组成争论不休。在与微服务实践者的对话中,我们发现有各种大小的服务。最大的服务报道遵循亚马逊两匹萨团队(也就是,整个团队吃两个披萨就吃饱了)的理念,这意味着团队不超过12个人。在更小的规模大小上,我们看到这样的安排,6人团队将支持6个服务。 这导致这样一个问题,在服务每12个人和服务每1个人的大小范围内,是否有足够打的不同使他们不能被集中在同一微服务标签下。目前,我们认为最好把它们组合在一起。但随着深入探索这种风格,我们一定有可能改变我们的看法。 1.3. 是产品不是项目 我们看到大多数应用程序开发工作使用一个项目模式:目标是交付将要完成的一些软件。完成后的软件被交接给维护组织,然后它的构建团队就解散了。 微服务支持者倾向于避免这种模式,而是认为一个团队应该负责产品的整个生命周期。对此一个共同的启示是亚马逊的理念 “you build, you run it” ,开发团队负责软件的整个产品周期。这使开发者经常接触他们的软件在生产环境如何工作,并增加与他们的用户联系,因为他们必须承担至少部分的支持工作。 产品思想与业务能力紧紧联系在一起。要持续关注软件如何帮助用户提升业务能力,而不是把软件看成是将要完成的一组功能。 没有理由说为什么同样的方法不能用在单体应用程序上,但服务的粒度更小,使得它更容易在服务开发者和用户之间建立个人关系。 ...

2018-05-29 · 2 min · 343 words · Hank

Spring Boot之基础入门

最近在了解微服务和Spring Cloud相关的东西,Spring Cloud的微服务体系基于Spring boot,所以了解Spring Boot是前提和基础。本文将介绍Spring Boot基础内容,包括简介、环境搭建、示例代码等,所使用JDK版本为JDK8,构建工具为Maven3.5。 1. 简介 Spring Boot是Spring的开源项目,其目的是让开发者可以简单、快速地使用Spring框架创建生产级的应用,其目的不是为了替换Spring,而是简化Spring的使用。Spring Boot可以看做是对Spring框架的二次封装,不过,在封装的同时也还提供了许多高级功能。 Spring Boot特性如下: 创建独立的Spring应用程序 直接嵌入Tomcat、Jetty或Undertow(无需部署WAR文件) 为项目构建提供了许多的“starter”,适用于整合不同的框架,从而简化您的构建工具(Maven)配置 尽可能自动配置Spring 提供可生产的特性,如度量指标、健康检查和集中配置 绝对没有代码生成,也不需要XML配置 2. 系统需求 Spring Boot需要JDK8或以上版本,嵌入的tomcat8.5所支持的Servlet版本为3.1,如下表所示: 依赖 版本 JDK 8或以上版本 Maven 3.2或以上版本 Servlet 3.1+ 3. 构建应用 现在,我们来一步步构建一个Hello world应用。 3.1. 构建 Spring Boot项目是基于Maven构建,完全遵循Maven的项目构建规则和目录规范。所以,你可以直接使用开发工具或者maven命令行来创建Spring Boot项目。需要注意的饿是,如果是Web项目,而且打包的格式为jar,那么与标准Maven项目不同的是不能使用src/main/webapp目录。 也可以访问 https://start.spring.io/来快速构建应用,选择对应的构建工具、语言和版本信息,填写开发信息并加入依赖下载,然后导入开发工具即可: 3.2. 配置Pom.xml 应用创建好了,我们来配置pom.xml,引入Spring boot相关的依赖。 1、继承parent节点 Spring为我们提供了顶层的parent节点,用来定义Spring Boot使用的版本,配置如下: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> <relativePath/> </parent> ...

2018-05-22 · 2 min · 327 words · Hank

XMind8 pro版本下载和破解

1. 简介 XMind是一款比较易用的思维导图软件,开源且功能强大,有免费版本,能够满足基本使用。如果需要使用更高级的功能,可以付费购买收费版本。 官网: https://www.xmind.net/ 2. 下载和破解 1、从官网下载Xmind8 pro,或者可以从百度云盘下载: 链接 https://pan.baidu.com/s/1OkSXoqcZA4unFbR1Crzl4w 密码 wat9 2、安装软件到指定目录 3、下载破解文件: 链接 https://pan.baidu.com/s/1KJ0hkB_rl4qv1MS3a8L1Aw 密码 bgv4 4、开始破解,步骤如下: (1)将破解文件XMindCrack.jar复制到安装根目录, 如: D:\Program Files\XMind 8 Update 32,以文本格式打开安装目录中XMind.ini (2)在 XMind.ini 最后追加: -javaagent:D:/Program Files/XMind 8 Update 3/XMindCrack.jar (3)打开C:\Windows\System32\drivers\etc路径,在hosts最后添加 127.0.0.1 www.xmind.net点击保存 (4)断网情况下,打开XMind8→帮助→序列号→输入序列号,邮箱可以随便填写,序列号在附件内,点击注册即可完成破解! (5)打开 XMind, 点击帮助----序列号,然后输入以下序列号 ,邮箱任意填写: XAka34A2rVRYJ4XBIU35UZMUEEF64CMMIYZCK2FZZUQNODEKUHGJLFMSLIQMQUCUBXRENLK6NZL37JXP4PZXQFILMQ2RG5R7G4QNDO3PSOEUBOCDRYSSXZGRARV6MGA33TN2AMUBHEL4FXMWYTTJDEINJXUAV4BAYKBDCZQWVF3LWYXSDCXY546U3NBGOI3ZPAP2SO3CSQFNB7VVIY123456789012345 5、破解完成,可以使用任意的Pro版本的功能了,授权信息如下: 此时XMind是无法联网使用的,如果需要使用诸如在线图标的功能,需要恢复联网,即取消hosts的修改内容,建议关闭软件自动更新。 3. 对比分析 之前博主曾介绍过另一款 思维导图软件MindManager,个人觉得也非常强大易用,只是没有中文版本(较早的版本有中文版,喜欢的可以查阅相关资料),这两者的文字对比分析可以看 这里,这里不再详述。 有大神对三款思维导图软件做了详细分析对比,见下图: 图片来源: https://www.zhihu.com/question/22094277 4. 申明 本站提供的破解软件仅供个人学习用,切勿用于商业用途! 本站不对使用破解软件造成的任何损失承担责任! 经济允许的话,请支持正版软件!

2018-05-11 · 1 min · 59 words · Hank

使用visualvm监控Java程序性能三——浏览堆dump文件

堆dump,即堆转储,其实是一个当前JVM堆内存所有对象在某个时间点上的快照。堆dump用来将当前应用程序的堆内存中的数据信息保存为文件,并可以快速浏览文件中的内容,查询对象分配信息。 在VisualVM中,堆dump的入口很多,可以再应用程序右键,选择堆dump;也可以打开主窗口的“监视”tab页,点击右上角有堆dump按钮;还可以打开抽样器tab页,进行内存抽样后,点击工具栏上的堆dump按钮。 1. 打开堆dump 执行完堆dump后,主窗口会打开一个堆dump的页面,同时应用程序节点会增加一个headdump+时间子节点,可以直接打开该节点来浏览堆dump文件信息,如图所示: 堆dump文件存储后缀为.hprof,除了能够看到本地堆dump文件,也可以通过菜单栏的文件-装入打开他人分享的dump文件: 2. 浏览堆dump文件信息 堆dump tab页包含了几个子标签页面:概要、类、实例、OQL控制台,我们重点说下前三个。 2.1. 概要 显示了转储堆dump时的信息,包括基本信息、环境信息、系统属性和线程信息,例如你可以看到堆dump时间、文件存放位置、大小等。 2.2. 类 类视图显示类的列表,以及该类引用的实例的数量和百分占比、类所有实例的大小和百分占比。您可以通过在实例视图中右键单击名称和选择“在实例图中显示”或者直接双击类来查看特定类的实例列表: 通过类视图,可以整体上明确哪些类实例数多,占用资源高。 可以通过最下方的“类目过滤器”来搜索类,点击漏斗形状的图标可以选择筛选类型,例如包含/不包含类名称、正则匹配、搜索子类等。例如,搜索类名包含EsSyncTask的所有类,如下图所示: 2.3. 实例数 当在类视图选择查询具体某一个类的实例时(右键或者双击),此时就打开了该类的实例视图。当您从实例窗格中选择一个实例时,VisualVM将显示该类的字段,并在各自的窗格中引用该类。 除了显示了具体的类和概要信息(实例数量、大小、总大小等)外,实例视图还有几个面板: 实例数:显示当前类的实例列表,点击具体的某一个实例可以在字段和引用面板查看实例的字段信息和引用了的对象 字段:显示当前选择的实例的具体字段,包括字段名称、类型、字段值等 引用:递归显示当前所有引用了该类的类实例,该面板会一层层递归显示引用,包括引用实例的再引用。通过引用面板,可以跟踪对象的引用情况,以便找到具体的类,从而优化代码。 通过右键点击实例或者在引用面板右键,可以查看最近垃圾回收根节点。 接下来,我们来编一个实例程序,看看堆dump的情况,代码如下: public class HeaddumpTest { public static void main(String[] args) { Refrence ref = new Refrence(); int id = 0; while (true) { ref.add(new MyClass(id++, "myclass" + id)); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Refrence { private List<MyClass> myClasses = new ArrayList<>(); public void add(MyClass myClass) { myClasses.add(myClass); } public List<MyClass> getMyClasses() { return myClasses; } public void setMyClasses(List<MyClass> myClasses) { this.myClasses = myClasses; } } class MyClass { private int id; private String name; MyClass(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ...

2018-04-24 · 1 min · 168 words · Hank

使用visualvm监控Java程序性能二——主窗口功能详解

无论是本地还是远程节点,双击或者右键打开应用程序,会直接在主窗口打开程序监控页。主窗口包括几个子页面:概述、监视、线程、抽样器,如果是本地程序且支持分析器还会显示Profiler页,根据安装插件的不同,还可能会显示一些插件的页面,例如MBeas等。 Figure 1. 主窗口界面 1. 概述页 概述页显示了应用程序和运行时环境的基本信息,如下图所示: Figure 2. 概述页界面 概述信息包括基本参数、保存的数据、详细信息几个部分。 1.1. 基本参数 基本参数描述了应用程序的一些参数信息,包括如下信息: PID:应用程序的进程ID 主机:应用程序运行的系统地址 主类:运行了main方法的类 参数:应用启动时所传递的参数信息,例如传递给main方法的参数列表 JVM:当前的JVM信息 Java:当前使用的JDK信息 Java Home:JDK的位置 JVM标志:启动JDK时JVM使用的的标志 出现OOME时生产堆dump:当前出现OOME时生产堆dump功能的开启/禁用状态 除了基本信息外,还包括两个可以自主选择显示或隐藏的功能(右上角):保存的数据和详细信息。 1.2. 保存的数据 显示VisualVM存储的当前应用程序的信息,例如线程dump的数量、堆dump和快照的数量等。 1.3. 详细信息 包括两个标签:JVM参数和系统属性。 JVM参数:配置的JVM启动的参数信息,例如堆大小; 系统属性:JVM运行的系统属性,例如用户目录、文件编码; 2. 监视页 监视tab页展示了监听了当前应用程序的整体情况,包括几个指标:CPU、内存、类、线程,它们都以直观的图形方式展现。 Figure 3. 监视页界面 2.1. CPU 该图展示了CPU的使用百分比走势,包括执行垃圾回收活动的时间等。可以从该图查看应用程序是否耗费CPU,是否频繁的进行垃圾回收,以便优化代码或者调整JVM内存设置。 2.2. 内存 反映了内存的占用情况,包括内存大小、最大值和已经使用的大小。内存情况又包括两部分:堆和Metaspace。 堆:该页展示了堆内存的大小和堆内存使用走势情况。 Metaspace:即元空间,JDK8移除了永久代(PermGen),而类的元数据信息被存储在了Metaspace,具体请看 这里。该标签反映了元空间内存的使用情况。 2.3. 类 类视图显示了已经加载的类数量和共享类的数量走势情况。 2.4. 线程 线程视图显示了应用程序在JVM中生存和守护线程的数量走势情况。如果您想在特定的时间点捕获和查看应用程序线程的精确数据,可以打开线程标签页,使用VisualVM进行线程转储(稍后详述)。 除了这四个图表,监视页还有两个重要的功能:执行垃圾回收和堆dump。 执行垃圾回收:立即触发垃圾回收 堆dump:执行堆dump,并在新的标签页打开,以查看对dump的详细信息(同右键应用程序–堆dump)。 通常,需要长期监控应用程序的情况,幸好这不会带来太大的开销。 我们看一个OOM的例子,看看VisualVM的监视页的监控情况。代码如下: public static void main(String[] args) { List<Object> objects = new ArrayList<>(); new Thread(() -> { while (true) { objects.add(new Object()); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } ...

2018-04-21 · 2 min · 402 words · Hank

使用VisualVM监控Java程序性能一——简介

Java应用程序难免遇到性能问题,例如常见的OutOfMemmoryError,又或者是出现内存泄漏的错误,又或是程序运行一段时间就卡死了,CPU或者内存占用率高,甚至造成系统崩溃,等等。出现性能问题时,往往难以分析和跟踪。不过,可以借助于一些性能分析工具,来监测程序性能,从而找出影响性能的问题所在,以便进行优化。本文的VisualVM就是一款比较强大的性能分析工具。 先来看看性能分析的几种方式: 性能分析的主要方式 性能分析常用的有以下几种方式 * 监视:监视是一种用来查看应用程序运行时行为的一般方法。通常会有多个视图(View)分别实时地显示 CPU 使用情况、内存使用情况、线程状态以及其他一些有用的信息,以便用户能很快地发现问题的关键所在。 * 转储(dump):性能分析工具从内存中获得当前状态数据并存储到文件用于静态的性能分析。Java 程序是通过在启动 Java 程序时添加适当的条件参数来触发转储操作的。它包括以下三种: 核心dump:JVM 生成的本地系统的转储。一般的,系统转储数据量大,需要平台相关的工具去分析,如 Windows 上的 windbg 和 Linux 上的 gdb。 Jvm dump:JVM 内部生成的格式化后的数据,包括线程信息,类的加载信息以及堆的统计数据。通常也用于检测死锁。 堆dump:JVM 将所有对象的堆内容存储到文件。 * 快照:应用程序启动后,性能分析工具开始收集各种运行时数据,其中一些数据直接显示在监视视图中,而另外大部分数据被保存在内部,直到用户要求获取快照,基于这些保存的数据的统计信息才被显示出来。快照包含了应用程序在一段时间内的执行信息,通常有 CPU 快照和内存快照两种类型。 CPU 快照:主要包含了应用程序中函数的调用关系及运行时间,这些信息通常可以在 CPU 快照视图中进行查看。 内存快照:主要包含了内存的分配和使用情况、载入的所有类、存在的对象信息及对象间的引用关系等。这些信息通常可以在内存快照视图中进行查看。 * 性能分析:性能分析是通过收集程序运行时的执行数据来帮助开发人员定位程序需要被优化的部分,从而提高程序的运行速度或是内存使用效率,主要有以下三个方面: CPU 性能分析:CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。通常有 CPU 监视和 CPU 快照两种方式来显示 CPU 性能分析结果。 内存性能分析:内存性能分析的主要目的是通过统计内存使用情况检测可能存在的内存泄露问题及确定优化内存使用的方向。通常有内存监视和内存快照两种方式来显示内存性能分析结果。 线程性能分析:线程性能分析主要用于在多线程应用程序中确定内存的问题所在。一般包括线程的状态变化情况,死锁情况和某个线程在线程生命期内状态的分布情况等 — 使用 VisualVM 进行性能分析及调优 https://www.ibm.com/developerworks/cn/java/j-lo-visualvm 1. VisualVM简介 VisualVM是集成JDK命令行工具和轻量级分析功能的可视化分析工具,设计用于开发和生产时间的使用。它提供了一个可视化界面,用于查看基于Java技术、运行于JVM上的应用程序(Java应用程序)的详细信息。 VisualVM组织Java开发工具包(JDK)工具检索的JVM软件的数据,并以一种使您能够快速查看多个Java应用程序的数据的方式提供信息。您可以查看在远程主机上运行的本地应用程序和应用程序的数据。您还可以捕获有关JVM软件实例的数据,并将数据保存到您的本地系统中,以供后期查看或与其他用户共享。 VisualVM完全免费,它通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时获得实时数据,从而进行动态的性能分析。同时,它能自动选择更快更轻量级的技术尽量减少性能分析对应用程序造成的影响,提高性能分析的精度。 2. VisualVM安装 从JDK6开始,VisualVM已经成为JDK自带工具,名称叫做jvisualvm,位于%JAVA_HOME%/bin/jvisualvm.exe,执行即可启动。 需要注意的是,从Oracle JDK 9开始,Java VisualVM迁移到GraalVM,这是在Oracle实验室开发的一种创新的、高性能的多语言虚拟机。详情请参阅 Graal VisualVM页面。 ...

2018-04-20 · 1 min · 114 words · Hank

RabbitMQ服务器管理(二)——权限管理

RabbitMQ有用一套专门的权限控制系统,用来控制不同用户对不同虚拟主机的访问控制。基本思路同大多系统一样:先创建用户,然后为用户授予权限。 大多数系统并没有严格区分认证和鉴权,但是在RabbitMQ中,对这两个概念做了明确的区分: 认证:识别用户身份,即:识别当前用户,认证其身份信息 鉴权:明确了用户身份,那么授权就是要检查该用户是否拥有相应的权限,即:检查授予用户的权限 1. 虚拟主机和guest用户 在 RabbitMQ基础(七)----虚拟主机vhost一篇,我们已经详细介绍了虚拟主机。我们说过,首次安装好RabbitMQ时,会拥有一个默认的虚拟主机“/”。同时,还有一个默认的用户“guest”,密码也为“guest”,该用户有用默认虚拟主机的全部权限。 如果你得RabbitMQ需要公网访问,出于安全性考虑,官方建议删掉该用户或者修改其密码。默认情况下,RabbitMQ禁止guest用户远程访问,只可以访问本地的mq服务。这个是通过loopback_users配置项决定的,如果需要取消该限制,仅需将该选项配置为none即可: loopback_users = none 如果是3.7之前的版本,还不支持这样key=value的配置格式,那么你需要配置成这样: ``[{rabbit, [{loopback_users, []}]}].`` 该配置想的意思是,取消所有用户的本地访问限制。按照 官方的示例配置文件,如果仅需要取消guest用户本地访问限制,那么进行以下配置: loopback_users.guest = false 否则设置为true即可。 关于MQ的配置文件,我们将在后续博文中详细讨论,官方文档见 这里。 2. 权限工作机制 当客户端与服务端建立连接时,第一级权限控制将会执行:服务器会检查连接的用户是否有用访问其连接的虚拟主机的权限,没有则会拒绝连接,否则连接建立成功。 用户操作虚拟主机的资源(路由器、队列、绑定等)时,RabbitMQ会启用第二级权限控制,验证用户是否具有访问虚拟机的资源的权限。 2.1. 权限定义 具体而言,在RabbitMQ中,对资源的操作定义了三种权限: 配置:创建和删除资源,或者改变它们的行为; 写:发布消息到资源; 读:从资源获取消息; 下表显示了对执行权限检查的所有AMQP命令所需的资源类型的权限: AMQP 0-9-1 Operation configure write read exchange.declare (passive=false) exchange exchange.declare (passive=true) exchange.declare (with AE) exchange exchange (AE) exchange exchange.delete exchange queue.declare (passive=false) queue queue.declare (passive=true) queue.declare (with DLX) queue exchange (DLX) queue queue.delete queue exchange.bind exchange (destination) exchange (source) exchange.unbind exchange (destination) exchange (source) queue.bind queue exchange queue.unbind queue exchange basic.publish exchange basic.get queue basic.consume queue queue.purge queue 说明: ...

2018-04-12 · 3 min · 504 words · Hank

RabbitMQ服务器管理(一)——ubuntu上安装MQ

在本篇,我们将在ubuntu上安装RabbitMQ,其他操作系统类似。 我们知道,RabbitMQ使用Erlang语言开发,所以需要先安装Erlang语言,在实践过程中,linux安装RabbitMQ还比较麻烦,涉及到很多依赖包的安装,官方的安装文档见 这里。 1. 正确的安装步骤 简单而言,正确的安装步骤如下: 1、安装erlang依赖包 wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb sudo dpkg -i erlang-solutions_1.0_all.deb 编辑/etc/apt/sources.list,添加以下地址的任意一个 deb https://packages.erlang-solutions.com/ubuntu trusty contrib deb https://packages.erlang-solutions.com/ubuntu saucy contrib deb https://packages.erlang-solutions.com/ubuntu precise contrib 然后执行: wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb sudo dpkg -i erlang-solutions_1.0_all.deb 2、安装erlang sudo apt-get update sudo apt-get install esl-erlang(或erlang) 3、下载RabbitMQ wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.12/rabbitmq-server_3.7.12-1_all.deb 4、安装RabbitMQ依赖包 apt-get install socat get-get install init-system-helpers get-get install adduser get-get install logrotate 5、安装RabbitMQ dpkg -i rabbitmq-server_3.7.12-1_all.deb 具体我的安装流程以及遇到的问题记录如下: 2. 安装过程 2.1. 下载安装包 ubuntu系统基于debian,所以我们要下载官方给的deb安装包: wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.4/rabbitmq-server_3.7.4-1_all.deb 等待下载完成。 2.2. 尝试安装 下载安装后,尝试直接安装: ...

2018-04-12 · 2 min · 255 words · Hank

RabbitMQ基础(七)——虚拟主机vhost

1. 简介 RabbitMQ是一个多租户系统:连接、交换器、队列、绑定、用户权限、策略和其他的东西都属于虚拟主机(virtual hosts,v_host),他们是整个RabbitMQ的逻辑分组。 虚拟主机类似于 Apache的虚拟主机和 Nginx的server块,最重要的区别是:Apache的虚拟主机通过配置文件定义,然而在RabbitMQ中,RabbitMQ只能通过rabbitmqctl控制台工具或者HTTP API来创建。 1.1. 逻辑和物理分隔 如果没有虚拟主机,当RabbitMQ中的数据越来越庞大,队列越来越多,随之而来的是令人头痛的管理问题,比如队列、交换器命名冲突,它们相互影响等等。虚拟主机能够解决这些问题,而不需要我们部署多个RabbitMQ来负责不同的业务。 虚拟主机提供了资源的逻辑分组和分隔,每一个虚拟主机本质上是mini版的RabbitMQ服务器,他们有用自己的连接、队列、绑定、交换器,更重要的是有用自己的权限机制,这有点类似服务器和运行在服务器上的虚拟机一样。 1.2. 客户端连接 通队列、交换器一样,虚拟主机必须定义名称。当AMQP 0-9-1的客户端连接到RabbitMQ时,需要指定虚拟主机名称,同时还需要提供用户名和密码,只有用户具有相关的权限才能建立连接(关于权限控制的详细信息可以看 这里)。 通常,一个虚拟主机的连接只能操作属于该虚拟主机的交换器、队列和绑定等等内容。但是,当客户端同时连接了多个虚拟主机时,可能产生多个虚拟主机内的交换器、队列相互连接的情况,这种情景典型的例子是虚拟主机处于不同的RabbitMQ集群或者同一个集群中, RabbitMQ Shovel plugin就是一个这种场景应用的实例。 1.3. 虚拟主机和STOMP、MQTT 同AMQP 0-9-1一样,STOMP协议也有虚拟主机的概念,具体参见 这里。 相反,MQTT并不支持虚拟主机,MQTT连接默认使用单一的RabbitMQ主机,有MQTT特定的约定和特性,使客户机能够连接到特定的虚拟主机,而无需修改任何客户端lib库。有关详细信息,请参阅 MQTT指南。 2. 虚拟主机管理 RabbitMQ包含一个默认的虚拟主机:“/”,我们默认操作的都是这个虚拟主机,其用户名和密码默认都是guest,为了安全起见我们应该修改其密码(后续文章将详细介绍权限管理)。 2.1. 查询 命令: rabbitmqctl list_vhosts 该命令会查询当前RabbitMQ服务中所有的虚拟机,由于我没有创建任何虚拟机,所以只能看到默认的: rabbitmqctl list_vhosts 2.2. 创建 命令: rabbitmqctl list_vhosts 创建时必须指定名称: rabbitmqctl list_vhosts 创建完成后,查询: rabbitmqctl list_vhosts 可以看到刚创建的虚拟主机。 2.3. 删除 命令: rabbitmqctl list_vhosts 同样必须指定要删除的vhost的名称: rabbitmqctl list_vhosts 查询: rabbitmqctl list_vhosts ...

2018-04-04 · 1 min · 140 words · Hank

windows全能终端神器MobaXterm9.4破解版下载

1. 简介 MobaXterm是远程计算的终极工具箱。在一个单独的Windows应用程序中,它为程序员、网站管理员、it管理员和几乎所有需要以更简单的方式处理远程工作的用户提供了大量的功能。 Mobaxterm为用户提供了多标签和多终端分屏选项,内置sftp文件传输以及xerver,让用户可以远程运行X窗口程序,ssh连接后会自动将远程目录展示在ssh面版中,方便用户上传下载文件。 MobaXterm提供了所有重要的远程网络工具、协议(SSH、X11、RDP、VNC、FTP、MOSH、…)和Unix命令(bash、ls、cat、sed、grep、awk、rsync、…)到Windows桌面。 MobaXterm小巧简单,仅有一个可执行文件。 官方地址: https://mobaxterm.mobatek.net/ 2. 功能介绍 1、支持各种连接SSH,X11,RDP,VNC,FTP,MOSH…… 2、内建XServer 3、连接SSH终端后支持SFTP传输文件 除了上边的一排工具,还可以拖拽上传哦。 4、友好的颜色设计 你已经看到了,MobaXterm配色看起来非常舒适,关键信息都以不同颜色区分,例如,ERROR的日志自动标记为红色,警告信息标记为黄色,等等。 5、支持多标签和多终端分屏 6、支持远程连接windows MobaXterm直接支持VNC/RDP/Xdmcp等多种远程连接方式。 7、支持多种工具 在Tool侧边栏里,有多种工具,例如文件夹比较工具MobaFoldersDiff,网络扫描器Network scanner,等等。 其他的功能不一一列举了,具体介绍和使用参见官方详细说明。 3. 下载 MobaXTerm有免费版本,但是保存的session有上限。这里提供的是专业破解版本下载,百度云有限速,下载链接如下: 链接 https://pan.baidu.com/s/1ONu6aVnV5vLFSNiUH8mDUA 密码 95gl 下载完成后解压,里边包含一个破解的MobaXterm.exe文件,执行即可。 4. 申明 1、破解软件博主亲测安全可用,不过建议下载后进行病毒查杀,博主不对软件的安全性负任何责任; 2、分享破解软件仅供个人使用,切勿用于商业用途; 3、开发一款好的产品真心不易,有条件的朋友请支持正版软件。

2018-04-02 · 1 min · 36 words · Hank

RabbitMQ基础(六)——实现RPC

第二篇中我们学习了如何在多个worker中使用工作队列来分配耗时的任务。现在,假设我们需要运行一个远程计算机上的一个方法并等待其返回结果,那么我们怎么实现?通常,这个过程被称为RPC(远程过程调用Remote Procedure Call)。 百度百科对RPC的介绍: RPC(Remote Procedure Call)--远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。 有多种 RPC模式和执行。最初由 Sun 公司提出。IETF ONC 宪章重新修订了 Sun 版本,使得 ONC RPC 协议成为 IETF 标准协议。现在使用最普遍的模式和执行是开放式软件基础的分布式计算环境(DCE)。 这里我们不过多讨论RPC相关的内容,有兴趣的可以查阅相关资料。 现在,我们将要使用RabbitMQ来构建一个RPC系统,包括可扩展的RPC客户端和服务端。由于我们并没有耗时的任务,所以们将模拟一些RPC服务。 1. Message properties Message properties即消息属性,AMQP-0-9-1预定义了14项消息属性,但是大部分都很少使用,常用的属性有如下几项: deliveryMode:标记消息是否持久化,值为2则持久化,其他为瞬态消息; contentType:描述编码的mime类型(mime-type),例如常用的JSON编码格式:application/json; replyTo:命名回调队列 correlationId:用于关联RPC的请求和响应 接下来,我们看看如何通过消息属性来设置回调队列和关联ID(correlationId)。 2. 回调队列 通常,在RabbitMQ上实现RPC很简单,客户端发送消息,而服务端响应消息即可。为了接收响应信息,客户端需要在请求中发送回调队列地址给服务端,告诉服务端我使用这个队列来接收消息,你将返回消息发送到这个队列即可。我们可以使用默认的队列(在java客户端中是独占的)。 callbackQueueName = channel.queueDeclare().getQueue(); BasicProperties props = new BasicProperties .Builder() .replyTo(callbackQueueName) .build(); channel.basicPublish("", "rpc_queue", props, message.getBytes()); 3. Correlation Id 上边的方法中,我们为每一个RPC客户端创建了回调队列,这是非常低效的。有没有更好的方式:为每一个客户端创建一个共享的回调队列?这样势必又带来新的问题:共享一个回调队列,请求和响应之间的对应关系并不明确。 CorrelationId属性解决了这个问题。 大致思路是这样:为每个请求设置一个 唯一的标识,然后通过回调队列接收响应消息时,获取这个唯一标识,如果这个标识和之前设定的相同,那么说明响应的确是当前请求的响应,可以获取请求的响应结果了。如果获取到的唯一标示并非我们所设定,说明它并不属于我们的任何请求,那么就可以丢弃这个消息。 为什么我们应该忽略回调队列中的未知消息,而不是进行失败处理?这是由于服务器端可能出现竞态条件。可能发生这样的情况,RPC服务器发送了响应消息后挂掉了,但是请求确认消息(ack)还没有发送,那么,重启后的RPC服务器将再次处理请求。这就是为什么在客户端我们必须优雅地处理重复的响应,保持RPC的幂等性。 消息定义correlationId代码: // 设置消息属性,响应后发送到回调队列中 final String correlationId = UUID.randomUUID().toString(); // 随机生成唯一标识 AMQP.BasicProperties properties = new AMQP.BasicProperties().builder() .correlationId(correlationId) // 每次请求都设定唯一标识,该标识用于将请求和响应进行匹配 .replyTo(callbackQueueName) // 回调队列 .build(); ...

2018-03-28 · 2 min · 355 words · Hank

RabbitMQ基础(五)——topic交换器

在上一篇,我们将日志系统做了改造,按照日志级别进行消息路由。我们还认识了direct类型的交换器,它是直接按照bindingKey与routingKey进行精确匹配,这两者分别在队列绑定和消息发送时进行设置。 Direct交换器每次仅能匹配一个精确的条件(bindingKey),如果要实现按照多个条件进行路由,或者按照条件进行模糊匹配,那么它就无能为力了。例如:前一篇你的日志程序,我们既要按照日志级别进行采集,还要根据打印日志的类来进行过滤,使用direct或者fanout都难以实现。 Topic交换器就是专门来处理这种场景的。在本篇,我们不再使用日志的例子,而是以动物为例,来了解topic交换器。 1. Topic交换器 发送到topic交换器的消息所设定的routingKey必须是一系列的单词列表,他们使用"."分隔。通常,这些单词会根据消息内容进行特殊定义,最大长度为255字节,举例:“stock.usd.nyse”、"syse.vmw"、"quick.range.rabbit"。 BindingKey也必须拥有相同的格式和遵循相同的规则。topic交换器和direct交换器处理逻辑上类似:带有特定routingKey的消息将被分发到绑定了匹配bindingKey的所有队列。但是,bindingKey还有两种特殊的通配符: *:能够模糊匹配一个单词 #:能够模糊匹配零个或多个单词 简单而言,topic交换器能够将消息的routingKey和队列绑定的bindingKey进行模糊匹配。如果不使用上边的两种通配符,那么topic交换器跟direct交换器没什么区别。 通配符举例: *.test.*:仅能匹配中间为test的三个单词的routingKey,例如mq.test.topic。 lazy.#:能够匹配以lazy开头的所有routingKey,单词个数不限,例如:lazy能匹配,lazy.test也能匹配 2. 示例 现在,我们来编写一个能够按照动物信息进行消息分发的程序。我们从速度、颜色和种类三个维度来描述动物信息,这里我们的key也是由这三个词语的具体描述组成,格式为“速度.颜色.种类”整体结构大致如下: 首选创建了一个类型为topic的交换器;然后我们定义了三个key,用于绑定到Q1和Q2两个队列,Q1绑定的key为*.orange.,Q2绑定的key为..rabbit和lazy.;消费者C1希望从Q1获取消息,而C2则希望从Q2获取消息。 整个程序的含义如下: 1、C1对颜色为orange(橙色)的动物感兴趣,希望获取它们的信息; 2、C2除了希望接收物种为rabbit(兔子)的消息外,还希望订阅速度为lazy(缓慢)的所有动物的信息。 很明显,C1、C2获取到的消息肯定存在重复的,它们接收消息的维度不同。 2.1. 生产者 创建交换器 // 创建交换器 channel.exchangeDeclare(exchangeName, "topic"); 模拟数据 String[] msgs = { "quick.orange.rabbit", "lazy.orange.elephant", "lazy.brown.fox", // 能匹配 "lazy.black.male.cat", // 四个单词也可以匹配 "orange", "quick.orange.male.rabbit" // 不能匹配,消息被丢弃 }; 发送消息 for (String msg : msgs) { System.out.println("发送:" + msg); channel.basicPublish(exchangeName, msg, null, msg.getBytes("utf-8")); } 为了简便,我将消息内容直接作为routingKey。实际上,routingKey需要根据消息内容进行特殊定制。 2.2. 消费者 创建随机队列 ...

2018-03-23 · 1 min · 130 words · Hank

RabbitMQ基础(四)——direct交换器与路由

前一篇我们构建了一个简单的日志记录系统,能够广播日志消息到所有已绑定的接收者。但是这样有一定的局限性,我们能够按照一定的条件来进行日志分发呢?例如,按照日志级别来分发日志消息,某个消费者只收到error级别的日志?在本章,我们将实现这个功能。 1. Bindings 上一篇,绑定队列的代码如下: channel.queueBind(queueName, EXCHANGE_NAME, ""); 第三个参数为routingKey,为了便于区分消息发送时的routingKey,我们将队列绑定的routingKey称为bindingKey。 BindingKey允许在队列绑定时设置额外的条件,路由器会按照这个key与消息的routingKey进行条件匹配,成功匹配的消息才会发送到该绑定队列。这个路由器按照bindingKey进行过滤的过程,我们称为消息路由(Routing)。 不同类型的交换器,bindingKey的作用和规则都有所不同。在fanout类型中,bindingKey会被忽略,因为这个交换器类型本身就是为了用于广播消息。在接下来我们要介绍的direct类型的交换器中,设定bindingKey的值有着重要意义。 2. Direct交换器 现在我们对上一篇的日志系统进行扩展,使其可以根据日志的级别进行过滤,假设我们的日志级别有debug、info、warning和error,我们想把error级别的消息单独使用一个消费者来接收,其他的由另外的消费者接收。 上篇中,我们使用的fanout交换器并不能满足上述需求----因为它只会把消息进行简单地广播。 我们将要用到的是 direct类型的交换器,它会按照绑定时给定的bindingKey与消息发布时的routingKey进行 精确匹配。即:当bindingKey与routingKey完成相同时,消息才会被交换器分发给队列。 在开始改造我们的日志程序前,我们先看看多重绑定。 3. 多重绑定 多重绑定,即将相同的bindingKey绑定到多个队列上。RabbitMQ允许这么做,这样与fanout交换器作用类似,可以将消息发送到多个队列。例如: 如图所示,Q1和Q2队列都绑定了black key,那么所有与black匹配的消息都会分发到Q1和Q2中,看起来与消息广播类似。 4. 程序改造 现在,我们开始来改造我们的日志程序,来实现上述的需求。 4.1. 生产者 创建direct类型的交换器: // 创建direct交换器 channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 准备日志数据: static Log[] logs = { new Log("error", "this is an error log."), new Log("error", "this is an error log."), new Log("error", "this is an error log."), new Log("error", "this is an error log."), new Log("warning", "this is a warning log."), new Log("info", "this is an info log."), new Log("info", "this is an info log."), new Log("info", "this is an info log."), new Log("debug", "this is a debug log."), new Log("debug", "this is a debug log.") }; ...

2018-03-23 · 2 min · 225 words · Hank

RabbitMQ基础(三)——fanout交换器和发布/订阅

前一篇,我们创建了工作队列,并将任务发布到队列,每一项任务都会发送给一个worker。接下来,我们将使用发布/订阅模式,将消息分发给多个消费者。 为了说明这一模式,我们将构建一个简单的日志记录系统。它将由两个程序组成----第一个将发出日志消息(生产者),第二个将接收并打印它们(消费者)。运行多个消费者,它们都可以接收消息。 1. 交换器 前边的部分我们都是通过消息队列来发布和接收消息,现在让我们看下RabbitMQ的全消息模型。让我们快速回顾一下 前边的内容: producer:用来发送消息 queue:用来缓存消息 consumer:用来接收消息 交换器,即Exchange,交换器是消息到达的第一站,所有的消息都先发送给交换器,交换器再按照不同的规则进行消息分发。RabbitMQ中的消息传递模型的核心思想是,生产者不会直接向队列发送任何消息。实际上,生产者甚至不知道消息是否会被传递到任何队列。相反,生产者只能发送消息给交换器。 交换器做的事情非常简单:一方面,它接收来自生产者的消息,另一边则将消息推送到队列中。交换器必须知道如何处理它接收到的消息。是否应该分发到特定的队列?是否应该分发到多个队列?或者应该被抛弃?其实,这些规则是由交换类型(exchange type)定义的。 交换类型:根据交换器的功能、用途和适用场景,将交换器进行类型定义,每种类型有各自的功能和适用场景。常见的交换类型有:direct、topic、headers、fanout。 接下来,我们学习下fanout类型的交换器。 fanout:将接收到的消息分发到所有能匹配的队列(广播)。简单而言,即:所有订阅了这些消息的队列,都能够收到消息。 创建名为logs的交换器,其类型为fanout: channel.exchangeDeclare("logs", "fanout"); 发送消息: String message = "中文日志信息"; channel.basicPublish("logs", "", null, message.getBytes("utf-8")); 注意,上边的代码队列名称为空。 2. 临时队列 对于队列,很重要的一点是,我们需要为其命名,因为生产者和消费者必须通过队列名称来定位到具体的队列,从中发送和获取消息(前几篇的"hello"队列和"task_queue"队列名称)。上边发送消息的代码中,我们并没有给队列命名,而是使用了""。 对于我们的日志记录系统,如果要每个消费者都能获取所有的日志消息,那么我们必须完成两点: 连接到RabbitMQ时,我们需要创建一个全新的队列,里边没有任何消息; 当消费者与RabbitMQ断开连接,那么队列应该被自动删除。 对于第1点,我们可以利用随机值来命名队列;对于第2点,我们只能检测断开连接后删除队列。但是,这都不是很好的做法。 其实,我们要做的是创建一个非持久的、独占的、自动删除的队列,这个队列的名称随机。RabbitMQ已经为我们提供了这个功能: String queueName = channel.queueDeclare().getQueue(); 如上边代码所示,我们创建了一个默认的队列,这队列具有上述特性,并且名称是随机生成的,格式为amq.gen-xxxx,例如amq.gen-JzTY20BRgKO-HjmUJj0wLg,我们称之为临时队列。 关键队列的非持久、独占和自动删除: 非持久(non-durable):队列中的消息不会持久化 独占(exclusive):队列为私有队列,只有当前应用程序能够消费队列 自动删除(autodelete ):最后一个消费者取消订阅队列时,队列自动删除 3. 绑定 本文开头,我们创建一个名称为"logs"、类型为"fanout"的交换器。那么,交换器需要分发消息给队列,如果将队列与交换器进行关联呢?我们把交换器和队列之间的关联关系称为绑定(binding)。 绑定的代码如下: channel.queueBind(queueName, "logs", ""); queueName即为前边创建的临时队列,第三个参数为routingKey,这里为空。 4. 完整代码 生产者LogSender: public static final String EXCHANGE_NAME = "logs"; public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); // 创建exchange channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); // 分发消息 String message = "中文日志信息"; channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("utf-8")); channel.close(); connection.close(); } ...

2018-03-22 · 2 min · 216 words · Hank

MindManager2016版本下载,带正版激活码

MindManger是Mindjet公司开发一款思维导图软件,目前已经更新到了2018版本。不过这里介绍的是2016版本,提供激活码。可以直接激活,提供下载的2016版本暂时没有中文版本的,激活码在下载包中。 与之前的版本比较,2016版本增加了多种图表,风格采用win10,您可以访问官网进行详细了解。 官网地址: https://www.mindjet.com/ 下载地址 https://pan.baidu.com/s/1LUqlM5gSMVQESE0suiOhqw 密码 mwtc 软件截图:

2018-03-22 · 1 min · 9 words · Hank

Spring事务管理四:声明式事务

编程式事务虽然可以精确控制事务,但是事务控制代码必须侵入业务逻辑代码中,耦合度高,后期难以维护。一般而言,不需要精确控制事务,所以采用的更多的是Spring的声明式事务。 Spring声明式事务基于AOP实现,有两种事务定义方式:xml配置和注解定义,前者使用tx命名空间,后者使用@Transactional注解。 1. 事务属性 在定义事务之前,需要了解一些事务的参数,正如前边TransactionDefinition类定义的,包括传播机制、隔离级别、是否只读、事务超时等,还包括回滚规则定义等参数。 1.1. 传播机制 传播机制(propagation)定义了客户端与被调用方法之间的事务界限。简单而言,就是一个方法调用其他一个或多个方法来实现业务逻辑时,这些方法间的事务如何进行传播,这就由传播机制来决定。 Spring提供了7中传播机制,如下表所示: Table 1. 事务的7种传播机制 传播行为 说明 REQUIRED 业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务 NOT_SUPPORTED 声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行 REQUIRES_NEW 属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行 MANDATORY 该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出异常。 SUPPORTS 这一事务属性表明,方法可以受事务控制,也可以不。如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行 NEVER 指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行 NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效 注意REQUIRES_NEW和NESTED两者的区别; PROPAGATION_REQUIRES_NEW启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. ...

2018-03-08 · 2 min · 401 words · Hank

Spring事务管理三:编程式事务

前边提到,编写程序式的事务管理可以清楚的定义事务的边界,可以实现细粒度的事务控制,比如你可以通过程序代码来控制你的事务何时开始,何时结束等,它可以实现细粒度的事务控制。 1. TransactionTemplate Spring提供了TransactionTemplate对象来控制事务,它使用了一种回调机制,通过回调来修改事务状态,继承关系如下: TransactionOperations接口定义了基础的事务的执行操作execute(),该接口便于后续扩展,一般不直接使用。 在使用TransactionTemplate之前,需要声明bean: <!--JDBC事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--编程式事务配置--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean> TransactionTemplate使用PlatformTransactionManager的实现来管理事务,这里注入的是JDBC的事务管理器。事务控制代码如下: public void add(User user) throws Exception{ // Spring编码式事务,回调机制 transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { try { userMapper.insertSelective(user); } catch (Exception e) { // 异常,设置为回滚 status.setRollbackOnly(); throw e; } return null; } }); } 调用TransactionTemplate的execute()方法,传递一个TransactionCallback接口的实现,执行其doInTransaction()方法,该方法会回传事务状态对象TransactionStatus,如果有异常,则调用status.setRollbackOnly()将事务状态标记为回滚,否则doInTransaction方法正常返回,事务则会提交。 如果事务控制的方法不需要返回值,那么可以使用TransactionCallback接口的抽象实现类TransactionCallbackWithoutResult: @Override public void add(User user) throws Exception { // Spring编码式事务,回调机制 transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { try { userMapper.insertSelective(user); } catch (Exception e) { // 异常,设置为回滚 status.setRollbackOnly(); throw e; } } }); } ...

2018-03-08 · 1 min · 103 words · Hank

Spring事务管理二:Spring事务管理器

前边简单介绍了事务的概念和其ACID特性,现在让我们看看Spring是如何实现对事务的支持的。 1. Spring事务管理器结构 Spring并不直接管理事务,而是提供多种事务管理器,将管理事务的责任委托给JTA或相应的持久性机制所提供的某个特定平台的事务实现。 Spring对事务管理器的抽象: Spring提供了顶层接口PlatformTransactionManager,并提供了扩展接口ResourceTransactionManager和抽象实现类AbstractPlatformTransactionManager。PlatformTransactionManager下的所有接口和实现类如图所示: 一般而言,每一种事务实现的类图如下: PlatformTransactionManager是Spring事务管理器的核心,接口定义如下: public interface PlatformTransactionManager { // 返回一个已经激活的事务或创建一个新的事务(根据给定的TransactionDefinition类型参数定义的事务属性), // 返回的是TransactionStatus对象代表了当前事务的状态,其中该方法抛出TransactionException(未检查异常) // 表示事务由于某种原因失败。 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交给定的事务,检查其状态。如果事务被标记为rollback-only,则执行回滚。 // 如果事务不是新的事务,则忽略提交周围适当的事务。如果先前的事务被挂起,则在提交新事务后恢复先前的事务。 void commit(TransactionStatus status) throws TransactionException; // 执行给定事务的回滚。如果事务不是一个新的事务,将其周边适当的事务标记为rollback-only。 // 如果先前的事务被挂起,则在回滚新事务后恢复先前的事务。 void rollback(TransactionStatus status) throws TransactionException; } TransactionDefinition接口定义如下: public interface TransactionDefinition { // 返回事务传播行为 int getPropagationBehavior(); // 返回事务隔离级别 int getIsolationLevel(); // 返回事务超时时间 int getTimeout(); // 返回事务是否只读 boolean isReadOnly(); // 返回事务的名称 String getName(); } ...

2018-03-07 · 1 min · 200 words · Hank

Spring事务管理一:Spring事务简介

1. 定义 在软件开发领域, 全有或全无的操作被称为事务(transaction)。 事物允许将几个操作组合成一个要么全部发生要么全部不发生的工作单元。发生和不发生两者只能选择其一,而不可能两者都选择。事务确保了数据和资源免于处在不一致的状态。 让我们继续使用最简单有效的转账例子来说明事务: A给B转100元,这个过程大概需要两步: step1、从A的账户扣除100元; step2、给B的账户增加100元。 这两步必须要么全部执行成功要么都不成功并且还原账户的金钱为初始值,否则转账失败。如果step1成功而step2失败,那么A账户少了100元,但是B却没收到钱;如果step1失败而step2成功,B账户会多出100元。这两种情况都是不允许发生的。 所以,step1和step2必须处于同一事务中,我们就说事务把这两个操作组合为要么都成功要么都失败的工作单元。整个操作成功,那么转账成功,结果是A账户扣了100元而B账户多了100元;如果整个过程失败,那么A和B的账户的金额恢复到转账前的状态,即未发生任何变化,就像转账操作从未发生过一样。 2. ACID特性 Atomic(原子性):确保所有操作要么都发生,要么都不发生。 Consistent(一致性):事务前后,数据均处于正确状态,数据不应该被破坏。 Isolated(隔离性):事务彼此隔离,用户间操作不相混淆。 Durable(持久性):事务一旦完成,结果应该持久化,例如保存到数据库中。 上边转账的例子中,两步操作必须要么成功要么都失败,确保了原子性;而原子性保证账户中的金额不会处于不一致或者说部分完成,保证了一致性;转账过程与其他转账过程互不影响,遵循隔离性;转账成功后,结果必须持久化保存,防止事务结果丢失。 3. Spring对事务管理的支持 Spring提供两者事务管理方式:编程式事务管理和声明式事务管理。 3.1. 编程式事务管理 编写程序式的事务管理可以清楚的定义事务的边界,可以 实现细粒度的事务控制,比如你可以通过程序代码来控制你的事务何时开始,何时结束等,与后面介绍的声明式事务管理相比,它可以实现细粒度的事务控制。 3.2. 声明式事务管理 如果你并不需要细粒度的事务控制,你可以使用声明式事务,在Spring中,你只需要在Spring配置文件中做一些配置,即可将操作纳入到事务管理中, 解除了和代码的耦合, 这是 对应用代码影响最小的选择,从这一点再次验证了Spring关于 AOP的概念。当你不需要事务管理的时候,可以直接从Spring配置文件中移除该设置。 Spring通过回调机制将实际的事务实现从事务性代码中抽象出来。如果应用只使用一种持久化资源,那么可以使用持久化机制本身的事务性支持(JDBC、Hibernate、JPA等);如果应用事务跨多个资源,那么Spring会使用第三方的JTA(JAVA事务API)实现来支持分布式(XA)事务。 使用声明式事务还是编程式事务管理,在很大程度上是 细粒度和易用性之间权衡。一般情况下,不需要精确控制事务,采用声明式事务不仅易用性高,而且降低了事务与其他业务代码的耦合性。 4. 总结 在软件开发领域,全有或全无的操作被称为事务。事务必须具备ACID特性,在Spring中,除了编程来精确控制事务,还可以使用AOP来配置低耦合度的声明式事务控制。

2018-03-07 · 1 min · 38 words · Hank

股票是什么

最近看了下股票相关的东西,刚好看到一则比较有意思的说明股票是什么的故事,转载下来! 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 从前,在一片广阔的大草原上,有很多只母鸡,他们,吃着火锅唱着歌,吃着青草下着蛋。但是,母鸡们又不愿意像这样日复一日的吃草,下蛋。于是,一场革命性的变革即将到来…… 1. Chapter 1 股票的产生 有一天,一只有远见的花母鸡突然想到,如果我能吃到更多,更好的虫子,岂不是可以进化成母鸡中的战斗机,下更多、更好的蛋? 可是,问题来了,这只花母鸡手里没有钱,就只能卖身。花母鸡找到了一只聪明的猴子,说,你给我钱,我之后每下两个蛋,都能分你一个。 猴子当然聪明得很,他心想:花母鸡每年能生100个蛋,一个蛋能卖1块钱,于是我每年就能分50块钱,那我给你1000块钱吧,这样我20年就能回本。 花母鸡说,OK,我这有50张 母鸡券,拿着作为凭证吧。 于是,猴子这50张母鸡券就被称为“ 股份”,而他所做的1000块钱投资,就叫“按20倍 市盈率入股”。啥叫市盈率?就是母鸡券的价格除以每年可以领到的鸡蛋,也可以理解为多久“回本”。这个数字,越低越好。 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 2. Chapter 2 股东的权益 花母鸡拿着猴子的钱,开开心心的吃了很多虫子,理所当然的也生了更多的蛋。 第一年,花母鸡下了100个蛋,分了猴子50个,猴子很开心。这,就叫做“ 分红”。 第二年,花母鸡还是下了100个蛋,但花母鸡把猴子叫过来开了个股东大会,说我用这100个蛋换个大鸡窝,以后每年我都能多生10个蛋,股东你觉得如何呀?猴子一想,我原来每年收益5%,用50个蛋换以后每年5个蛋,每年收益10%,10年回本,合算!这,就叫做 有效率的再投资。 这时候,聪明猴投资母鸡的事儿在动物圈传开了,猩猩这时候跑过来说,我给你1000块钱,要不你把50个母鸡券卖给我? 聪明猴心想,我特么又不是傻B,就跟猩猩说:我这个母鸡品种好,叫 价值鸡,现在每年生110个蛋,以后还能健康生蛋50年,我按照20年回本卖给你,怎么也得1100!猩猩说,好好好,买买买! 于是,动物圈的第一次股权交易发生了,而单张母鸡券的价格,也就从20元,上浮到了22元。这,就是 股价上浮的原因之一。 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 3. Chapter 3 市场形成 这个交易做成以后,在动物圈引发了轰动,各种动物都蠢蠢欲动,尤其是那些安安稳稳下蛋的母鸡们。于是,每个品种的母鸡,都发行了带有自己印记的母鸡券,给小动物们互相买卖。 老虎王二虽然并不知道这些动物在弄啥嘞,那些母鸡券貌似只有“卵”用,但作为大王,底下动物干什么都得管着才行(他们的确啥都不懂),于是他效仿大洋彼岸隔壁森林发达市场的老王颁布了一项规定:母鸡可以在老虎家向所有动物公开拍卖自己的母鸡券,所有的母鸡券都必须在老虎家里交易,这就叫做 股票交易市场。 而母鸡必须在老虎家做体检,登记所有信息,方便动物们深入了解母鸡,另一方面动物们也容易找到自己的交易对手,这样 傻傻的犀牛大象也毫不费力就可以买卖母鸡券了! 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 4. Chapter 4 股价 大家已经可以想到,假设一个母鸡一夜之间变成了胖母鸡,生蛋加倍,那么凭借相应母鸡券就可以领到双倍的鸡蛋,所以母鸡券价格翻倍;反之今夜母鸡得了禽流感,卒,对应母鸡券就一文不值。 那么再许许多多个夜晚,什么都没有发生,还是原来的母鸡,还是生普通的蛋,为什么母鸡券还是涨了跌了?原因很复杂,或许是猴子认为母鸡会变胖,或许是因为猩猩想娶媳妇儿急需用钱,但无论怎样的原因, 母鸡券的价格上涨或下降,母鸡都得不到任何直接的好处或者是坏处,还是原来的母鸡,还是生普通的蛋。 而股价,有时候跟母鸡本身,并没有什么关系。 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 哦对了,开头提到的迈克尔·道格拉斯生活中可不像电影里那样呼风唤雨,他自己炒股赔了据说高达1亿美元。 原文: 作者:邹波 链接:https://www.zhihu.com/question/25215182/answer/54368155[]

2018-02-28 · 1 min · 54 words · Hank

MySQL中批量插入和批量更新

大批量同步数据到MySQL中,涉及到更新和新增数据,如果仅仅循环一次次插入,不仅耗费过多的网络开销,性能低下,而且频繁的获取数据库连接、操作数据库,增加数据库压力,甚至严重影响数据库性能。因此,此时我们需要批量操作,来减轻数据库压力,提升应用性能。 1. 数据准备 首先创建一张测试表,插入一些初始数据: CREATE TABLE `hello` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; INSERT INTO `test`.`hello` (`id`, `name`, `age`) VALUES ('1', 'zhangsan', '30'); INSERT INTO `test`.`hello` (`id`, `name`, `age`) VALUES ('2', 'lisi', '18'); INSERT INTO `test`.`hello` (`id`, `name`, `age`) VALUES ('3', 'wangwu', '22'); INSERT INTO `test`.`hello` (`id`, `name`, `age`) VALUES ('4', 'zhaoliu', '25'); ...

2018-02-27 · 2 min · 240 words · Hank

RabbitMQ基础(二)——工作队列Work Queues

上一篇,我们编写了从一个指定的队列发送和接收消息的程序。在本文中,我们将创建一个工作队列,用于将耗时的任务分配给多个工作人员。 Work Queues,称为工作队列(也称Task Queues,任务队列),其主旨在于避免立即执行资源密集型任务,并且必须等待它完成。相反,我们把任务安排在后来执行。对此,我们将任务封装为消息并将其发送到队列中。在后台运行的工作进程会获取任务并最终执行任务。当你运行许多工作进程时,任务将在他们之间共享。 1. 轮询调度 使用任务队列的优点之一是能够轻松地并行工作。如果我们积累了大量的工作,我们就可以增加更多的任务消费者,这样就可以很容易地扩大规模。默认情况下,RabbitMQ将依次将每个消息发送给下一个使用者。平均每个消费者将得到相同数量的消息,这种分发消息的方式称为轮询。 2. 消息确认 完成一项任务可能需要几秒钟。你可能会想,如果其中一个消费者开始了一项很长的任务,并且只完成了部分任务,会发生什么。使用我们当前的代码,一旦RabbitMQ向客户传递消息,它立即标记为删除。在这种情况下,如果你关闭一个工作进程,我们将失去它正在处理的信息。我们也将丢失所有发送给这个特定工作进程并且还未处理消息。 通常,我们希望一个工作进程挂掉后,将其任务交给其他工作进程完成。为了保证消息不丢失,RabbitMQ支持消息确认机制:当消费者处理完消息后,其反馈给RabbitMQ,表明消息已经被接收和处理,RabbitMQ可以自由删除该消息。 如果一个消费者挂掉(其通道关闭,连接关闭,或者TCP连接丢失),而没有发送ack,RabbitMQ将会知道一条消息没有被完全处理,并且将重新排队。如果同时有其他的消费者在线,那么它将很快把消息重新交给另一个消费者处理,这样就保证不会丢失任何信息。 消息不会超时:当消费者进程挂掉时,RabbitMQ将重新传递消息,即使处理消息需要很长时间。 RabbitMQ默认是开启手动消息确认的,我们可以通过autoAck=true标记明确地关闭,即采用系统自动确认消息。如果需要手动确认消息,那么将autoAck设置为false,一旦我们完成了任务,需要向工作进程发出确认消息,代码如下: // 处理完成,手动接收消息时,需要在处理成功后进行反馈,保证消息不丢失 channel.basicAck(envelope.getDeliveryTag(), false); 示例的部分关键代码如下: NewTask.java /** * 发布5条消息,每条消息的一个“.”表示消息执行时间需要1秒。 * * @param args * @throws IOException * @throws TimeoutException */ public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); String[] msgs = new String[]{ "First Message.", // 1s "Second Message..", // 2s "Third Message...", // 3s "Fourth Message....", // 4s "Fifth Message....." // 5s }; Channel channel = connection.createChannel(); channel.queueDeclare(TASK_QUEUE_NAME, false, false, false, null); for (String msg : msgs) { // 发布消息 channel.basicPublish("", TASK_QUEUE_NAME, null, msg.getBytes("utf-8")); System.out.println("[x] Sent '" + msg + "'"); } channel.close(); connection.close(); } ...

2018-01-24 · 3 min · 431 words · Hank

RabbitMQ基础(一)——基本概念和HelloWorld

1. 基本概念 RabbitMQ是一个消息代理,是一个erlang开发的AMQP(Advanced Message Queue )的开源实现。 RabbitMQ是轻量级的,易于部署在premises和云中。它支持多种消息传递协议。RabbitMQ可以部署在分布式和联合配置中,以满足高级别、高可用性需求。 其主要思想非常简单:它接受并转发消息。你可以把它想象成邮局:当你把邮件寄到邮箱时,你很确定邮差先生最终会把邮件寄给你的收件人。使用这个比喻,RabbitMQ是一个邮筒,一个邮局和一个邮差。 RabbitMQ与邮局的主要区别在于,它不处理纸张,而是接受、存储和转发二进制数据。 官网地址: http://www.rabbitmq.com 2. AMQP AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有 RabbitMQ等。 目标是实现一种在全行业广泛使用的标准消息中间件技术,以便降低企业和系统集成的开销,并且向大众提供工业级的集成服务。通过AMQP,让消息中间件的能力最终被网络本身所具有,并且通过消息中间件的广泛使用发展出一系列有用的应用程序。 Broker: 中间件。接收和分发消息的应用,RabbitMQ Server就是Message Broker。 Virtual host: 虚拟主机。出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue等。 Connection: 连接。publisher/consumer和broker之间的TCP连接。断开连接的操作只会在client端进行,Broker不会断开连接,除非出现网络故障或broker服务出现问题。 Channel: 渠道。如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。 Exchange: 路由。message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)。 Queue: 队列。消息最终被送到这里等待consumer取走。一个message可以被同时拷贝到多个queue中。 Binding: 绑定。exchange和queue之间的虚拟连接,binding中可以包含routing key。Binding信息被保存到exchange中的查询表中,用于message的分发依据。 3. RabbitMQ术语 3.1. Producter 即生产者。Producing就是发送,发送消息的程序是生产者(Producter)。用P表示,如下图: 3.2. Exchange 交换器,RabbitMQ中,其实消息不会直接相队列发送,而是发送给交换器,然后交换器在按照一定的规则转发给不同的队列。交换器做的事情非常简单:一方面,它接收来自生产者的消息,另一边则将消息推送到队列中。交换必须知道如何处理它接收到的消息。是否应该附加到特定的队列?是否应该附加到许多队列?或者应该被抛弃。这些规则由交换类型(exchange type)定义。 3.3. Exchange Type 交换器类型,在创建交换器时指定,用于区分交换器的不同作用,实现不同的功能。RabbitMQ定义了四种交换器类型:direct、topic、headers、fanout,每种类型都有特定的应用场景(见后续文章的详细介绍)。 direct:bindingKey和routingKey进行精确匹配,适用于精确将消息发送给指定队列; topic:bindingKey和routingKey可以进行模糊匹配,通过使用通配符"*"和"#"分别来模糊匹配一个单词和多个单词;适用于将消息按照一定的规则发送到匹配的一个或多个队列; fanout:广播,这种交换器可以将消息广播给所有订阅的交换器; header:不常用,有兴趣的话可以自行了解。 3.4. Queue 即队列,队列是邮箱的名称,它处于RabbitMQ内部。尽管消息流通过RabbitMQ和您的应用程序,但它们只能存储在队列中。队列不受任何限制,它可以根据你的需要存储尽可能多的消息----它本质上是一个无限的缓冲区。许多生产者都可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。队列上有它的名称,如下图表示: 3.5. Consumer 即消费者,Consuming跟receiving的含义类似。Consumer通常为等待接收消息的应用程序 。注意,生产者、消费者和消息代理不需要处于同一台主机上,事实上,在大多数应用场景都是如此。 ...

2018-01-18 · 2 min · 354 words · Hank

Windows server 2012 R2评估版本激活

原来服务器安装了Windows server 2012 R2评估版本,只能试用180天,最近刚好到期了,系统激活前每隔一个小时会自动关机,所以必须进行激活。 要激活分为两步: 1、将评估版升级为标准版 2、使用激活工具激活 1. 将评估版升级为标准版 1.1. 查看系统当前版本 最好以管理员身份运行cmd,输入命令: DISM /online /Get-CurrentEdition 可以查询当前安装的版本是什么。 如果是评估版,例如Standard,把“ServerStandardEval”中的Eval这四个字母去掉,就是你的当前版本。 我的当前版本就是ServerStandard,如下图所示: 1.2. 升级为标准版 同样在cmd中输入如下命令: DISM /online /Set-Edition:ServerStandard /ProductKey:你的标准版key /AcceptEula ServerStandard为查出来的EDITION ID,ProductKey为标准版key,例如我使用如下命令成功升级为标准版: DISM /online /Set-Edition:ServerStandard /ProductKey:M98WF-NY2PP-73243-PC8R6-V6B4Y /AcceptEula 如果提示key无效或不可用,多找一些key试试。 升级完成后,重启系统,现在桌面的标准评估版本已经不再显示,右键点击我的电脑-属性,可以看到现在为standard版本,说明升级成功,但是仍然没有激活。 2. 使用激活工具激活 我尝试使用KMSTools激活工具激活,结果激活失败。 最终,在网上找到了小马激活工具,成功激活。 工具下载地址: https://pan.baidu.com/s/1qYwqI2w。 注意:博主亲测可以激活,但杀毒软件可能会报病毒,博主是直接绕过杀毒软件的,存在一定的风险,请谨慎考虑。 激活成功后,再次重启系统,可以看到已经成功激活了。 申明 本文章仅供个人学习参考使用,如需商业用途,建议大家支持正版系统。

2017-12-05 · 1 min · 45 words · Hank

redis desktop manager桌面工具

链接 http://pan.baidu.com/s/1i53VLmX 密码 vmm6 比较好用的Redis可视化界面工具,支持Windows 10, Mac OS X 10.12, Ubuntu 16, LinuxMint | Fedora | CentOS | OpenSUSE等平台。 github地址: https://github.com/uglide/RedisDesktopManager/ 官网: http://redisdesktop.com 界面截图: 可支持控制台: 支持按key过滤查找功能:

2017-11-23 · 1 min · 26 words · Hank

Astah professional 7.2版本破解文件

分享Astah professional 7.2下载和破解: 官方下载地址: http://astah.net/download 1、免费的community版本 链接: http://pan.baidu.com/s/1kVch3oj 密码:3o49 2、专业版 链接: http://pan.baidu.com/s/1slzBKip 密码:lwjn 专业版破解文件 链接: http://pan.baidu.com/s/1pLBlgDH 密码:u9x7 破解方法:解压下载的文件,将astah-pro.jar拷贝的软件安装根目录覆盖原文件即可。 上边的连接永久有效,博主自己测试可以正常破解使用。

2017-11-22 · 1 min · 19 words · Hank

WEBP格式的图片导致ImageIO.read方法return null

1. 问题背景 最近开发一个图片服务,主要是维护项目图片,支持JPG、BMP、PNG、JPEG等常规格式。开发的时候没问题,但是上到生产的时候,客户在维护图片的时候,发现有的JPG格式的图片能上传,有的不能。 拿到正常上传和不能上传的图片,对比发现,不能上传的图片,在windows上根本无法打开,图片如下(用chrome打开可以看见,右键另存图片,或者 点击这里下载): 但是奇怪的是,该图片可以在chrome浏览器显示,也可以在安卓APP上显示,但是其他浏览器和iOS无法正常显示。 2. 问题分析 首先debug代码,发现图片都会使用ImageIO来读取,以便获取图片宽高从而进行等比例压缩,代码如下: BufferedImage bi = ImageIO.read(inputStream); Map rtnMap = new HashMap(); if (bi == null) { rtnMap.put("status", "1"); //上传不成功 rtnMap.put("msg", "该图片本身不是图片格式"); return rtnMap; } 非正常图片,上边的bi会返回null,而不会抛出任何异常信息。 首先怀疑图片是损坏的,因为windows上根本无法打开,显示如下: 但是该图片可以在chrome上正常显示,这表明图片不会是损坏了,应该是跟平台有关,那么会不会是图片格式根本不是JPG,仅仅后缀显示为JPG呢? 然后我用记事本打开正常的和非正常的图片文件进行比对,想获取一些有用的信息,结果发现: 非正常JPG图片 正常JPG图片 如上所示,正常JPG里边,显示格式为JFIF,而非正常图片显示为WEBPVP8,明显不同。看来,不能正常上传的图片是平台不支持的图片格式,到底WEBPVP8是什么格式? 3. WEBP格式 通过谷歌搜索,查询半天发现,这种格式名为WEBP,由谷歌开发,官方地址(记得翻墙): https://developers.google.com/speed/webp/?hl=zh-CN,也是图片格式的一种, 百度百科原文如下: WebP格式,谷歌(google)开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器带宽资源和数据空间。Facebook Ebay等知名网站已经开始测试并使用WebP格式。 但WebP是一种有损压缩。相较编码JPEG文件,编码同样质量的WebP文件需要占用更多的计算资源。桌面版Chrome可打开WebP格式。 恍然大悟,原来是一种新型图片格式,很明显windows和iOS还不支持,但是谷歌旗下产品可以支持,这就解释了为什么chrome、安卓能够打开。 在JAVA中,还不支持这种格式,可以用如下代码检查当前支持的图片格式: String[] ss = ImageIO.getReaderFormatNames(); System.out.println(Arrays.asList(ss)); 返回结果:[JPG, jpg, bmp, BMP, gif, GIF, WBMP, png, PNG, jpeg, wbmp, JPEG] 在查找问题时,我还尝试使用twelvemonkeys扩展包来扩展所支持的图片类型: <dependency> <group>com.twelvemonkeys.imageio</group> <artifact>imageio-jpeg</artifact> <version>3.3.2</version> </dependency> <dependency> <group>com.twelvemonkeys.imageio</group> <artifact>imageio-tiff</artifact> <version>3.3.2</version> </dependency> <dependency> <group>com.twelvemonkeys.imageio</group> <artifact>imageio-pnm</artifact> <version>3.3.2</version> </dependency> ...

2017-11-03 · 2 min · 375 words · Hank

ubuntu下ssh关闭密码登陆,采用公钥认证登陆

前段时间,公司内网的机器被黑客入侵,最后分析原因,在于我们希望通过外网访问公司的git仓库,然后开放了22端口,而此前git所在机器上被病毒感染过…… 由于git是单独的服务器,所以我们希望只有公钥认证的用户可以授权访问该服务器,否则拒绝访问。因此,我们决定采用以下方案: 禁止用户通过ssh的账号密码登陆,而是用公钥私钥认证登陆。具体做法如下: 1. 修改SSH配置文件sshd_config 注意是sshd_config,修改ssh_config无效 编辑sshd_config文件: $ vi /etc/ssh/sshd_config 禁用密码验证,默认是yes $ vi /etc/ssh/sshd_config 启用密钥验证 $ vi /etc/ssh/sshd_config 指定公钥数据库文件 $ vi /etc/ssh/sshd_config 2. 重启ssh服务 $ vi /etc/ssh/sshd_config 由于服务器有两个用户:root管理员用户和git用户,而所有访问代码的必须通过git用户访问,git本身已经通过公钥私钥登陆,所以对原来访问代码仓库的客户端没有影响。而目前root用户禁止了密码登陆,如果进行授权呢? 很简单,将授权以root用户登录服务器的客户端公钥加入到/root/.ssh/authorized_keys文件中,客户端登陆时,带上客户端的私钥信息,就可以完成认真并成功以root用户登录,我用的mobXterm工具,其他工具类似,链接选择私钥如下: 这样,就完成了只能公钥认证通过的用户才能登陆服务器的功能。 3. 注意事项 3.1. 保留会话窗口 在修改sshd_config文件并重启ssh之前,先保留一个链接到服务器的会话,否则如果ssh配置出现错误,将导致不能链接到服务器。 3.2. ssh配置文件 修改sshd_config而非ssh_config。 3.3. 为何不修改ssh默认的22端口 因为链接git仓库客户端较多,如果修改了22端口,每一个客户端都要重新设置代码仓库的remote地址,处理起来较为麻烦,如果如何处理请自行百度。当然为了更为安全,建议还是修改ssh默认的22端口。

2017-08-14 · 1 min · 41 words · Hank

git从其他git服务器完整迁移到自建git服务器

1). 从原地址克隆一份裸版本库,原本托管于oschina。 git clone --bare git://github.com/username/project.git (1) 1 --bare 创建的克隆版本库都不包含工作区,直接就是版本库的内容,这样的版本库称为裸版本库。 2). 然后到新的 Git 服务器上创建一个新项目,比如commons。 mkdir commons.git cd commons.git git init --bare 上边的命令表示创建名为commons的版本库,并初始化为空版本库(注意--bare参数)。 3). 以镜像推送的方式上传代码到新的git服务器上。 git push --mirror git@<你的serverIp>:commons.git (1) 1 --mirror 克隆出来的裸版本对上游版本库进行了注册,这样可以在裸版本库中使用git fetch命令和上游版本库进行持续同步。 例如: $ git push --mirror git@192.168.0.106:/data/code/commons.git Counting objects: 100, done. Delta compression using up to 4 threads. Compressing objects: 100% (54/54), done. Writing objects: 100% (100/100), 44.88 KiB | 0 bytes/s, done. Total 100 (delta 13), reused 100 (delta 13) To 192.168.0.106:/data/code/commons.git * [new branch] master -> master ...

2017-06-08 · 1 min · 98 words · Hank

Nginx学习——nginx的下载、安装和启动

Nginx作为一款轻量级WEB服务服务器,除了作为http代理和反向代理服务器,还更广泛的运用于负载均衡、高级http服务、邮件代理服务等。接下来,我们开始学习如何下载安装Nginx服务器,包括windows平台和linux平台。 1. Nginx版本 Nginx的官方网址: http://nginx.org Nginx下载地址: http://nginx.org/en/download.html 如上图所示,目前nginx官方分为三个版本: 主线开发版本(Mainline version):即功能较新的处于开发中的版本,可以用于学习,但不太稳定,不适合商用; 稳定版本(Stable version):功能稳定,适合商用; 历史遗留版本(Legacy versions):较早的历史版本。 其他链接 图1中的CHANGES的链接,是对于此版本的更新日志记录; 图1中的nginx-x.x.x的链接,对应的是nginx特定版本的源代码; 图1中的pgp链接,记录的是使用GPG加密软件计算后的签名信息,用于下载文件的验证,防止文件被篡改; 图1中的nginx/Windows-x.x.x对应的是nginx的windows版本的下载链接。 2. Windows 1、下载安装 下载图1中的nginx/Windows-1.12.0,得到window版本的nginx压缩包。 windows版本为绿色版本,解压即用,无须安装。解压下载的压缩包,得到如下的目录结构: 2、启动 双击nginx.exe启动 直接运行图2的nginx.exe即启动了nginx,同时可以在任务管理器中看到有两个nginx.exe的进程。 Windows命令行启动 运行cmd,进入nginx目录,运行命令 $ nginx.exe 这种方式会使命令行一直处于执行中,无法进行后续操作 或者 $ start nginx 推荐的方式。 如果启动未出现异常信息,表明启动成功,任务管理器中会出现两个nginx.exe的进程。 3、停止 杀进程 直接在任务管理器中kill掉nginx进程,不推荐。 命令行 运行cmd,进入nginx目录,输入命令 $ nginx.exe –s quit 推荐,这种方式是平缓停止,完整有序的停止nginx,并保存相关信息 或者 $ nginx.exe –s stop 快速停止nginx,不保存相关信息。 4、重新加载配置文件 运行cmd,进入nginx目录,输入命令 $ nginx –s reload 5、重新打开日志文件 运行cmd,进入nginx目录,输入命令 $ nginx –s reopen 6、Nginx版本查看 $ ./sbin/nginx –v 或者 $ ./sbin/nginx –V ...

2017-05-15 · 2 min · 269 words · Hank