好API的6个特质


好API的6个特质

API之于程序员就如同图形界面之于普通用户(end-user)。API中的『P』实际上指的是『程序员』(Programmer),而不是『程序』(Program),强调的是API是给程序员使用的这一事实。

在第13期Qt季刊,Matthias 的关于API设计的文章中提出了观点:API应该极简(minimal)且完备(complete)、语义清晰简单(have clear and simple semantics)、符合直觉(be intuitive)、易于记忆(be easy to memorize)和引导API使用者写出可读代码(lead to readable code)。

1 极简
极简的API是指每个class的public成员尽可能少,public的class也尽可能少。这样的API更易理解、记忆、调试和变更。

2 完备
完备的API是指期望有的功能都包含了。这点会和保持API极简有些冲突。如果一个成员函数放在错误的类中,那么这个函数的潜在用户就会找不到,这也是违反完备性的。

3 语义清晰简单
就像其他的设计一样,我们应该遵守最少意外原则(the principle of least surprise)。好的API应该可以让常见的事完成的更简单,并有可以完成不常见的事的可能性,但是却不会关注于那些不常见的事。解决的是具体问题;当没有需求时不要过度通用化解决方案。

4 符合直觉
就像计算机里的其他事物一样,API应该符合直觉。对于什么是符合直觉的什么不符合,不同经验和背景的人会有不同的看法。API符合直觉的测试方法:经验不很丰富的用户不用阅读API文档就能搞懂API,而且程序员不用了解API就能看明白使用API的代码。

5 易于记忆
为使API易于记忆,API的命名约定应该具有一致性和精确性。使用易于识别的模式和概念,并且避免用缩写。

6 引导API使用者写出可读代码
代码只写一次,却要多次的阅读(还有调试和修改)。写出可读性好的代码有时候要花费更多的时间,但对于产品的整个生命周期来说是节省了时间的。

最后,要记住的是,不同的用户会使用API的不同部分。尽管简单使用单个Qt类的实例应该符合直觉,但如果是要继承一个类,让用户事先看好文档是个合理的要求。

转载自coolshell

OpenKM6工作流功能浅析

前几天同事和我沟通,希望用工作流类似的功能,对现有的任务进行管理。
说实话,我实在不看好openkm的工作流管理功能(jbpm),但周末还是花了一些时间看了一下。
不看还不知到,看了以后才发现,OpenKM6已经有较长一段时间没有进行框架升级了,还都停留在JDK7早期时代,并且用到了GWt技术。
OpenKM6的工作流,用的是jbpm-3.3.1.GA。看了一下jboss上的jbpm项目,已经进化到jbpm6,好歹开始拥抱drools了吧。
额,扯远了。回到正题。

一、首先是开发环境的搭建,要到这里来下载哦
https://sourceforge.net/projects/openkmportabledev/?source=directory
如果你嫌麻烦,建议就直接解压到指定文件夹就可以用了。

二、打开eclipse,编译jbpm项目
是基于mvn的,有一些jar包,在openkm的仓库里,但下载不到,啥意思吗。自己google咯,还好不算多。

三、编译的时候,建议看下openkm的相关教程,超级简单
https://docs.openkm.com/kcenter/view/wfg/sample-workflow-execution.html

四、介绍一下几个常用节点:
start:工作流的起点,一个流程只允许一个起点
end:工作流的终点,可以有多个,但只要有一个到达终点,流程就结束了
fork、join:就是任务分支与状态同步啦
decision:选择
node:自动处理的任务节点
task node:需要人工干预的任务节点,数据的输入是通过表单form完成的
mail node:自动进行邮件处理的几点
transition:就是两个节点之间的连线

五、构建一个自己的工作流
1、新建一个工作流项目
2、会自动新建一个工作流,打开XXX.jpdl.xml文件,就可以进行编辑了
通过编辑器,自己画一个工作流,会自动生成两个文件,加上刚才的文件一共有三个文件:
XXX.jpdl.xml 节点定义及流程描述
.XXX.gpd.xml 元素的位置信息
XXX.jpg 工作流截图
在发布工作流的时候,这三个文件都要带上
JBPM01

六、写Handler
编辑器上,可以直接处理的操作,其实很有限,也没有太多功能,主要还是靠编码实现。
编码的话,主要是实现几类接口:
org.jbpm.graph.def.ActionHandler:
多数的Handler,都用这个即可,包括transition、mail node、task node、node等
org.jbpm.taskmgmt.exe.Assignable:
主要用于任务执行者的分配
org.jbpm.graph.node.DecisionHandler:
主要用于选择节点

Handeler都只需要实现对于的方法即可。最后,通过编辑器进行配置。
JBPM02

六、发布工作流
发布也很简单,就是直接用eclipse上的jbpm菜单就可以发布了,但要把参数配置好才行
JBPM03

七、执行
1、首先要到openkm的后台,将工作流启用
2、然后找到任意一个文件、文件夹,执行工作流即可

八、填坑
遇到的最大的坑,莫过于字符集问题,我这边的方法,只修正了.XXX.gpd.xml的字符集问题,form的字符集问题,并没有修正
找到文件org.jbpm.gd.jpdl_3.4.1.v20120717-1252-H7-GA-SOA.jar,替换掉文件AbstractContentProvider.class即可
AbstractContentProvider.java源码如下:

package org.jbpm.gd.common.editor;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.jbpm.gd.common.model.NamedElement;
import org.jbpm.gd.common.model.SemanticElement;
import org.jbpm.gd.common.notation.AbstractNodeContainer;
import org.jbpm.gd.common.notation.BendPoint;
import org.jbpm.gd.common.notation.Edge;
import org.jbpm.gd.common.notation.Label;
import org.jbpm.gd.common.notation.Node;
import org.jbpm.gd.common.notation.NodeContainer;
import org.jbpm.gd.common.notation.NotationElement;
import org.jbpm.gd.common.notation.NotationElementFactory;
import org.jbpm.gd.common.notation.NotationMapping;
import org.jbpm.gd.common.notation.RootContainer;
import org.jbpm.gd.jpdl.Logger;

public abstract class AbstractContentProvider implements ContentProvider {
	protected abstract SemanticElement getEdgeSemanticElement(Node paramNode,
			Element paramElement, int paramInt);

	protected abstract SemanticElement getNodeSemanticElement(
			NodeContainer paramNodeContainer, Element paramElement, int paramInt);

	protected abstract void addNodes(NodeContainer paramNodeContainer,
			Element paramElement);

	protected abstract void addEdges(Node paramNode, Element paramElement);

	protected abstract SemanticElement findDestination(Edge paramEdge,
			Node paramNode);

	protected String getRootNotationInfoElement() {
		return "<root-container/>";
	}

	protected String createInitialNotationInfo() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
		buffer.append("\n\n");
		buffer.append(getRootNotationInfoElement());
		return buffer.toString();
	}

	public String getNotationInfoFileName(String semanticInfoFileName) {
		return ".gpd." + semanticInfoFileName;
	}

	public String getDiagramImageFileName(String semanticInfoFileName) {
		int index = semanticInfoFileName.indexOf(".xml");
		String result = index > -1 ? semanticInfoFileName.substring(0, index)
				: semanticInfoFileName;
		return result + ".jpg";
	}

	protected void processRootContainer(RootContainer rootContainer,
			Element notationInfo) {
		addDimension(rootContainer, notationInfo);
		addNodes(rootContainer, notationInfo);
		postProcess(rootContainer);
	}

	protected void addNodes(NodeContainer nodeContainer,
			SemanticElement[] semanticElements, Element notationInfo) {
		List notationInfoElements = notationInfo == null ? new ArrayList()
				: notationInfo.elements();
		for (int i = 0; i < semanticElements.length; i++) {
			Element notationInfoElement = null;
			String nodeName = ((NamedElement) semanticElements[i]).getName();
			for (int j = 0; j < notationInfoElements.size(); j++) {
				Element element = (Element) notationInfoElements.get(j);
				String elementName = element.attributeValue("name");
				if (((elementName != null) && (elementName.equals(nodeName)))
						|| ((elementName == null) && (nodeName == null))) {
					notationInfoElement = element;
				}
			}

			addNode(nodeContainer, semanticElements[i], notationInfoElement);
		}
	}

	protected void addEdges(Node node, SemanticElement[] semanticElements,
			Element notationInfo) {
		List notationInfoElements = notationInfo == null ? new ArrayList()
				: notationInfo.elements();
		for (int i = 0; i < semanticElements.length; i++) {
			Element notationInfoElement = null;
			if (notationInfoElements.size() >= i + 1) {
				notationInfoElement = (Element) notationInfoElements.get(i);
			}
			addEdge(node, semanticElements[i], notationInfoElement);
		}
	}

	protected void addNode(NodeContainer nodeContainer,
			SemanticElement semanticElement, Element notationInfoElement) {
		String notationElementId = NotationMapping
				.getNotationElementId(semanticElement.getElementId());
		Node notationElement = (Node) nodeContainer.getFactory().create(
				notationElementId);
		notationElement.setSemanticElement(semanticElement);
		notationElement.register();
		nodeContainer.addNode(notationElement);
		semanticElement.addPropertyChangeListener(notationElement);
		processNode(notationElement, notationInfoElement);
		if ((notationElement instanceof NodeContainer)) {
			addNodes((NodeContainer) notationElement, notationInfoElement);
		}
	}

	protected void addEdge(Node node, SemanticElement semanticElement,
			Element notationInfoElement) {
		NotationElement notationElement = node
				.getRegisteredNotationElementFor(semanticElement);
		if (notationElement == null) {
			String notationElementId = NotationMapping
					.getNotationElementId(semanticElement.getElementId());
			notationElement = node.getFactory().create(notationElementId);
			notationElement.setSemanticElement(semanticElement);
			notationElement.register();
			node.addLeavingEdge((Edge) notationElement);
			semanticElement.addPropertyChangeListener(notationElement);
		}
		processEdge((Edge) notationElement, notationInfoElement);
	}

	protected void addDimension(RootContainer processDefinitionNotationElement,
			Element processDiagramInfo) {
		String width = processDiagramInfo.attributeValue("width");
		String height = processDiagramInfo.attributeValue("height");
		Dimension dimension = new Dimension(width == null ? 0 : Integer
				.valueOf(width).intValue(), height == null ? 0 : Integer
				.valueOf(height).intValue());
		processDefinitionNotationElement.setDimension(dimension);
	}

	protected void processNode(Node node, Element notationInfoElement) {
		addConstraint(node, notationInfoElement);
		addEdges(node, notationInfoElement);
	}

	protected void processEdge(Edge edge, Element edgeInfo) {
		processLabel(edge, edgeInfo);
		addBendpoints(edge, edgeInfo);
	}

	protected void addBendpoints(Edge edge, Element edgeInfo) {
		if (edgeInfo != null) {
			List list = edgeInfo.elements("bendpoint");
			for (int i = 0; i < list.size(); i++) {
				addBendpoint(edge, (Element) list.get(i), i);
			}
		}
	}

	protected BendPoint addBendpoint(Edge edge, Element bendpointInfo, int index) {
		BendPoint result = new BendPoint();
		processBendpoint(result, bendpointInfo);
		edge.addBendPoint(result);
		return result;
	}

	protected void processBendpoint(BendPoint bendPoint, Element bendpointInfo) {
		int w1 = Integer.valueOf(bendpointInfo.attributeValue("w1")).intValue();
		int h1 = Integer.valueOf(bendpointInfo.attributeValue("h1")).intValue();
		int w2 = Integer.valueOf(bendpointInfo.attributeValue("w2")).intValue();
		int h2 = Integer.valueOf(bendpointInfo.attributeValue("h2")).intValue();
		Dimension d1 = new Dimension(w1, h1);
		Dimension d2 = new Dimension(w2, h2);
		bendPoint.setRelativeDimensions(d1, d2);
	}

	private void processLabel(Edge edge, Element edgeInfo) {
		Element label = null;
		if (edgeInfo != null) {
			label = edgeInfo.element("label");
		}
		if (label != null) {
			Point offset = new Point();
			offset.x = Integer.valueOf(label.attributeValue("x")).intValue();
			offset.y = Integer.valueOf(label.attributeValue("y")).intValue();
			edge.getLabel().setOffset(offset);
		}
	}

	private void addConstraint(Node node, Element nodeInfo) {
		Rectangle constraint = node.getConstraint().getCopy();
		Dimension initialDimension = NotationMapping.getInitialDimension(node
				.getSemanticElement().getElementId());
		if (initialDimension != null) {
			constraint.setSize(initialDimension);
		}
		if (nodeInfo != null) {
			constraint.x = Integer.valueOf(nodeInfo.attributeValue("x"))
					.intValue();
			constraint.y = Integer.valueOf(nodeInfo.attributeValue("y"))
					.intValue();
			constraint.width = Integer
					.valueOf(nodeInfo.attributeValue("width")).intValue();
			constraint.height = Integer.valueOf(
					nodeInfo.attributeValue("height")).intValue();
		}
		node.setConstraint(constraint);
	}

	protected void postProcess(NodeContainer nodeContainer) {
		List nodes = nodeContainer.getNodes();
		for (int i = 0; i < nodes.size(); i++) {
			Node node = (Node) nodes.get(i);
			List edges = node.getLeavingEdges();
			for (int j = 0; j < edges.size(); j++) {
				Edge edge = (Edge) edges.get(j);
				SemanticElement destination = findDestination(edge, node);
				Node target = (Node) edge.getFactory()
						.getRegisteredNotationElementFor(destination);
				if ((target != null) && (edge.getTarget() == null)) {
					target.addArrivingEdge(edge);
				}
			}
			if ((node instanceof NodeContainer)) {
				postProcess((NodeContainer) node);
			}
		}
	}

	public boolean saveToInput(IEditorInput input, RootContainer rootContainer) {
		boolean result = true;
		try {
			IFile file = getNotationInfoFile(((IFileEditorInput) input)
					.getFile());
			InputStreamReader reader = new InputStreamReader(file.getContents(), "UTF-8");
			Element notationInfo = new SAXReader().read(reader)
					.getRootElement();
			if (upToDateCheck(notationInfo)) {
				getNotationInfoFile(((IFileEditorInput) input).getFile())
						.setContents(
								new ByteArrayInputStream(toNotationInfoXml(
										rootContainer).getBytes("UTF-8")),
								true, true, null);
			} else {
				result = false;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	protected String toNotationInfoXml(RootContainer rootContainer) {
		StringWriter writer = new StringWriter();
		write(rootContainer, writer);
		return writer.toString();
	}

	protected void write(RootContainer rootContainer, Writer writer) {
		try {
			Document document = DocumentHelper.createDocument();
			Element root = document.addElement("root-container");
			write(rootContainer, root);
			XMLWriter xmlWriter = new XMLWriter(writer,
					OutputFormat.createPrettyPrint());
			xmlWriter.write(document);
		} catch (IOException e) {
			e.printStackTrace(new PrintWriter(writer));
		}
	}

	protected void write(RootContainer rootContainer, Element element) {
		addAttribute(element, "name",
				((NamedElement) rootContainer.getSemanticElement()).getName());
		addAttribute(element, "width",
				Integer.toString(rootContainer.getDimension().width));
		addAttribute(element, "height",
				Integer.toString(rootContainer.getDimension().height));
		Iterator iter = rootContainer.getNodes().iterator();
		while (iter.hasNext()) {
			write((Node) iter.next(), element);
		}
	}

	protected void write(Node node, Element element) {
		Element newElement = null;
		if ((node instanceof AbstractNodeContainer)) {
			newElement = addElement(element, "node-container");
		} else {
			newElement = addElement(element, "node");
		}
		addAttribute(newElement, "name",
				((NamedElement) node.getSemanticElement()).getName());
		addAttribute(newElement, "x", String.valueOf(node.getConstraint().x));
		addAttribute(newElement, "y", String.valueOf(node.getConstraint().y));
		addAttribute(newElement, "width",
				String.valueOf(node.getConstraint().width));
		addAttribute(newElement, "height",
				String.valueOf(node.getConstraint().height));
		if ((node instanceof AbstractNodeContainer)) {
			Iterator nodes = ((AbstractNodeContainer) node).getNodes()
					.iterator();
			while (nodes.hasNext()) {
				write((Node) nodes.next(), newElement);
			}
		}
		Iterator edges = node.getLeavingEdges().iterator();
		while (edges.hasNext()) {
			Edge edge = (Edge) edges.next();
			write(edge, addElement(newElement, "edge"));
		}
	}

	protected void write(Edge edge, Element element) {
		Point offset = edge.getLabel().getOffset();
		if (offset != null) {
			Element label = addElement(element, "label");
			addAttribute(label, "x", String.valueOf(offset.x));
			addAttribute(label, "y", String.valueOf(offset.y));
		}
		Iterator bendpoints = edge.getBendPoints().iterator();
		while (bendpoints.hasNext()) {
			write((BendPoint) bendpoints.next(),
					addElement(element, "bendpoint"));
		}
	}

	protected void write(BendPoint bendpoint, Element bendpointElement) {
		addAttribute(bendpointElement, "w1",
				String.valueOf(bendpoint.getFirstRelativeDimension().width));
		addAttribute(bendpointElement, "h1",
				String.valueOf(bendpoint.getFirstRelativeDimension().height));
		addAttribute(bendpointElement, "w2",
				String.valueOf(bendpoint.getSecondRelativeDimension().width));
		addAttribute(bendpointElement, "h2",
				String.valueOf(bendpoint.getSecondRelativeDimension().height));
	}

	protected Element addElement(Element element, String elementName) {
		Element newElement = element.addElement(elementName);
		return newElement;
	}

	protected void addAttribute(Element e, String attributeName, String value) {
		if (value != null) {
			e.addAttribute(attributeName, value);
		}
	}

	private void createNotationInfoFile(IFile notationInfoFile) {
		try {
			notationInfoFile.create(new ByteArrayInputStream(
					createInitialNotationInfo().toString().getBytes("UTF-8")),
					true, null);
		} catch (CoreException e) {
			Logger.logError(e);
		} catch (UnsupportedEncodingException e) {
			Logger.logError(e);
		}
	}

	protected IFile getNotationInfoFile(IFile semanticInfoFile) {
		IProject project = semanticInfoFile.getProject();
		IPath semanticInfoPath = semanticInfoFile.getProjectRelativePath();
		IPath notationInfoPath = semanticInfoPath.removeLastSegments(1).append(
				getNotationInfoFileName(semanticInfoFile.getName()));
		IFile notationInfoFile = project.getFile(notationInfoPath);
		if (!notationInfoFile.exists()) {
			createNotationInfoFile(notationInfoFile);
		}
		return notationInfoFile;
	}

	public void addNotationInfo(RootContainer rootContainer, IEditorInput input) {
		try {
			IFile file = getNotationInfoFile(((FileEditorInput) input)
					.getFile());
			if (file.exists()) {
				InputStreamReader reader = new InputStreamReader(
						file.getContents(),"UTF-8");
				Element notationInfo = new SAXReader().read(reader)
						.getRootElement();
				boolean changed = convertCheck(notationInfo);
				processRootContainer(rootContainer, notationInfo);
				if (changed) {
					file.setContents(
							new ByteArrayInputStream(toNotationInfoXml(
									rootContainer).getBytes("UTF-8")), true,
							true, null);
				}
			} else {
				file.create(new ByteArrayInputStream(
						createInitialNotationInfo().toString()
								.getBytes("UTF-8")), true, null);
			}
		} catch (DocumentException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} catch (CoreException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	private boolean convertCheck(Element notationInfo) {
		boolean changed = false;
		if (("process-diagram".equals(notationInfo.getName()))
				|| ("pageflow-diagram".equals(notationInfo.getName()))) {
			MessageDialog dialog = new MessageDialog(
					null,
					"Convert To 3.1.x Format",
					null,
					"A file created with an older GPD version was detected. If you open this file it will be converted to the 3.1.x format and overwritten.\nDo you want to continue?",
					3, new String[] { "Convert And Open",
							"Continue Without Converting" }, 0);
			if (dialog.open() == 0) {
				convertToRootContainer(notationInfo);
				changed = true;
			}
		}
		return changed;
	}

	private boolean upToDateCheck(Element notationInfo) {
		if (("process-diagram".equals(notationInfo.getName()))
				|| ("pageflow-diagram".equals(notationInfo.getName()))) {
			MessageDialog dialog = new MessageDialog(
					null,
					"GPD 3.0.x Format Detected",
					null,
					"The file you are trying to save contains GPD 3.0.x information.Saving the file will result in an automatic conversion into the 3.1.x format.It will be impossible to open it with the old GPD.\nDo you want to continue?",
					3, new String[] { "Save And Convert", "Cancel" }, 0);
			return dialog.open() == 0;
		}
		return true;
	}

	private void convertToRootContainer(Element notationInfo) {
		notationInfo.setName("root-container");
		convertChildrenToEdge(notationInfo);
	}

	private void convertChildrenToEdge(Element element) {
		List list = element.elements();
		for (int i = 0; i < list.size(); i++) {
			convertToEdge((Element) list.get(i));
		}
	}

	private void convertToEdge(Element element) {
		if ("transition".equals(element.getName())) {
			element.setName("edge");
		}
		convertChildrenToEdge(element);
	}
}

结语:
1、jbpm如果要用起来,还是要有很大的工作量的,而且早期版本对中文支持并不友好
2、openkm的工作流,整体来说太弱了,不够假单和直观
3、这种流程式管理方式,并不符合我们的管理方式
4、先用openkm的文档管理功能就好了
5、如果openkm再不升级框架,估计后面我们要找其他文档管理工具了。

Spring Boot各模块作用

1、spring-boot
web容器整合,快速开发,上下文、外部配置、日志的统一管理

2、spring-boot-autoconfigure
自动配置,自动判断需要的jar包

3、spring-boot-actuator
生产环境管理,用rest方式给出多种接口:
mappings/autoconfig/configprops/beans
env/info/health/heapdump/metrics
loggers/logfile/dump/trace
shutdown/auditevents

4、spring-boot-starters
各种配置好的功能模块,用于快速拼装各种需要的功能

5、spring-boot-loader
用于加载jar包中的jar,可以实现单个jar/war的运行
注意:这种jar包放到jar包时,不要再次压缩

6、spring-boot-cli
快速开发groovy

7、spring-boot-devtools
方便调试,远程调试

Spring Cloud Netflix入门(Feign)

Feign是一个很好用的REST通讯包,十分强大。

1、MyFeignClient

package com.neohope.springcloud.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import feign.hystrix.FallbackFactory;


import org.springframework.stereotype.Component;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(clients = {MyFeignClient.IServiceClient.class})
@RestController
public class MyFeignClient 
{
	@Value("${spring.application.name}")
	private String appName;
	
	@RequestMapping("/appName")
	public String appName() {
		return appName;
	}
	
	public static void main(String[] args)
	{
		new SpringApplicationBuilder(MyFeignClient.class).web(true).run(args);
	}
	
	@RequestMapping("/")
	public String hello() {
		return "It works";
	}
	
	@Autowired
	private IServiceClient aClient;
	
	@RequestMapping("/invokeC")
	public String invokeC() {
		return aClient.invokeA();
	}
	
	@RequestMapping("/invokeC/{clientName}")
    String invokeC(@PathVariable("clientName") String clientName)
    {
		if(aClient==null)
		{
			return "aClient is null";
		}
		else
		{
			//return aClient.invokeA(clientName);
			return aClient.invokeA();
		}
    }
	
	//primary = false
	@FeignClient(name="netflix-feign-server", configuration = MyFeignClientConfiguration.class, fallbackFactory=HystrixClientFallbackFactory.class)
	public interface IServiceClient {
	    @RequestMapping(method = RequestMethod.GET, value = "/invokeA")
	    String invokeA();

	    //@RequestMapping(method = RequestMethod.GET, value = "/invokeA/{clientName}", consumes = "application/json")
	    //String invokeA(@PathVariable("clientName") String clientName);
	}
	
	@Component
    public static class HystrixClientFallbackFactory implements FallbackFactory<IServiceClient>{
    	@Override
    	public IServiceClient create(final Throwable cause) {
    		return new IServiceClient() {
    			@Override
    		    public String invokeA()
    		    {
    				return "invokeA failed: "+cause.getMessage();
    		    }

    			//@Override
    			//public String invokeA(String clientName)
    		    //{
    			//	return "invokeA with clientName "+clientName+" failed: "+cause.getMessage();
    		    //}
    		};
    	}
    }
}

2、MyFeignClientConfiguration

package com.neohope.springcloud.test;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.Logger;
import feign.auth.BasicAuthRequestInterceptor;

@Configuration
public class MyFeignClientConfiguration {
	@Bean
	public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
		return new BasicAuthRequestInterceptor("user", "hystrix");
	}

	/*
	@Bean
	Logger.Level feignLoggerLevel() {
		return Logger.Level.FULL;
	}
	*/
}

3、pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.cloud.stream.app</groupId>
		<artifactId>app-starters-build</artifactId>
		<version>1.1.3.M1</version>
	</parent>

	<groupId>com.neohope.springcloud.test</groupId>
	<artifactId>netflix-feign-client</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>netflix-feign-client</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-feign</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
			<version>1.3.0.M1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/libs-milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

4、application.properties

spring.application.name=feign-client
server.port=7002
security.user.name=user
security.user.password=hystrix
eureka.client.serviceUrl.defaultZone=http://user:hystrix@localhost:7000/eureka/
eureka.client.preferSameZoneEureka=true
eureka.client.healthcheck.enabled=true
eureka.instance.metadataMap.zone = zone1
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health
eureka.instance.lease-renewal-interval-in-seconds=5
eureka.instance.lease-expiration-duration-in-seconds=10
feign.hystrix.enabled=true
#feign.compression.request.enabled=true
#feign.compression.response.enabled=true
#feign.compression.request.enabled=true
#feign.compression.request.mime-types=text/xml,application/xml,application/json
#feign.compression.request.min-request-size=2048
#NONE,BASIC,HEADERS,FULL
logging.level.com.neohope.springcloud.test.MyFeignClient.IServiceClient=BASIC

Spring Cloud Netflix入门(Sidecar)

SideCar主要是用于将非JVM语言的服务,代理为Netflix服务

1、SideCarApp

package com.neohope.springcloud.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.sidecar.EnableSidecar;
import org.springframework.web.bind.annotation.RequestMapping;

//作用一、注册到eureka并提供服务健康状态
//其他语言的服务,首先服务封装为REST或JSON通信,并提供一个health路径,反馈服务是否正常
//sidecar会将这个服务注册到eureka
//其他服务,可以通过eureka查询并调用该服务

//作用二、从sidecar获取服务地址,并调用
//访问sidecar,获取服务地址  http://localhost:port/hosts/{serviceId}.
//分析返回结果,得到服务地址
//调用需要的服务
@EnableSidecar
public class SideCarApp 
{
	@Value("${spring.application.name}")
	private String appName;
	
	@RequestMapping("/appName")
	public String appName() {
		return appName;
	}
	
	@RequestMapping("/")
	public String hello() {
		return "It works";
	}
	
    public static void main( String[] args )
    {
    	new SpringApplicationBuilder(SideCarApp.class).web(true).run(args);
    }
}

2、pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.cloud.stream.app</groupId>
		<artifactId>app-starters-build</artifactId>
		<version>1.1.3.M1</version>
	</parent>

	<groupId>com.neohope.springcloud.test</groupId>
	<artifactId>netflix-sidecar-test</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>netflix-sidecar-test</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-netflix-sidecar</artifactId>
			<version>1.3.0.M1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/libs-milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

3、application.properties

spring.application.name=zuul-gateway-app
server.port=7001
security.user.name=user
security.user.password=hystrix

eureka.client.serviceUrl.defaultZone=http://user:hystrix@localhost:7000/eureka/
eureka.client.preferSameZoneEureka=true
eureka.client.healthcheck.enabled=true
eureka.instance.metadataMap.zone = zone1
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health
eureka.instance.lease-renewal-interval-in-seconds=5
eureka.instance.lease-expiration-duration-in-seconds=10

sidecar.port=9999
sidecar.health-uri=http://localhost:9999/health.json

Spring Cloud Netflix入门(Zuul)

Zuul就是一个API网关。

1、ZuulGatewayApp.java

package com.neohope.springcloud.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
@RestController
@EnableZuulProxy
//@EnableZuulServer
public class ZuulGatewayApp 
{
	@Value("${spring.application.name}")
	private String appName;
	
	@RequestMapping("/appName")
	public String appName() {
		return appName;
	}
	
	public static void main(String[] args)
	{
		new SpringApplicationBuilder(ZuulGatewayApp.class).web(true).run(args);
	}
	
	@RequestMapping("/")
	public String hello() {
		return "It works";
	}
}

2、pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.cloud.stream.app</groupId>
		<artifactId>app-starters-build</artifactId>
		<version>1.1.3.M1</version>
	</parent>

	<groupId>com.neohope.springcloud.test</groupId>
	<artifactId>netflix-zuul-gateway</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>netflix-zuul-gateway</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-zuul</artifactId>
		</dependency>
		<dependency>
			<groupId>com.netflix.feign</groupId>
			<artifactId>feign-okhttp</artifactId>
			<version>8.18.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/libs-milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

3、application.properties

spring.application.name=zuul-gateway-app
server.port=7001
security.user.name=user
security.user.password=hystrix

eureka.client.serviceUrl.defaultZone=http://user:hystrix@localhost:7000/eureka/
eureka.client.preferSameZoneEureka=true
eureka.client.healthcheck.enabled=true
eureka.instance.metadataMap.zone = zone1
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health
eureka.instance.lease-renewal-interval-in-seconds=5
eureka.instance.lease-expiration-duration-in-seconds=10

zuul.host.maxTotalConnections=200
zuul.host.maxPerRouteConnections=20
#zuul.ignoredServices='*'
zuul.routes.netflix-feign-server.sensitiveHeaders
zuul.routes.netflix-feign-server.path=/invokeA/**
zuul.routes.netflix-feign-server.serviceId=netflix-feign-server
#zuul.routes.netflix-feign-server.url=
#zuul.ribbonIsolationStrategy=THREAD
#zuul.prefix=api
zuul.stripPrefix=false
#zuul.addProxyHeaders=false
#zuul.ignoredHeaders=
#zuul.forceOriginalQueryStringEncoding=true
#zuul.SendResponseFilter.post.disable=true.

#ribbon.eureka.enabled=false
#ribbon.restclient.enabled=true
#ribbon.okhttp.enabled=true
#ribbon.ConnectTimeout=3000
#ribbon.ReadTimeout=60000
#users.ribbon.listOfServers=172.16.172.63

#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000

Spring Cloud Netflix入门(Turbine)

Turbine是一个日志收集器,用于聚合Hystrix中的日志。

1、TurbineServer

package com.neohope.springcloud.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.stream.EnableTurbineStream;
import org.springframework.web.bind.annotation.RequestMapping;

@SpringBootApplication
@EnableCircuitBreaker
@EnableEurekaServer
@EnableHystrixDashboard
@EnableTurbineStream
public class TurbineServer 
{
	@Value("${spring.application.name}")
	private String appName;
	
	@RequestMapping("/appName")
	public String appName() {
		return appName;
	}
	
	public static void main(String[] args) {
        new SpringApplicationBuilder(TurbineServer.class).web(true).run(args);
    }
}

2、pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.cloud.stream.app</groupId>
		<artifactId>app-starters-build</artifactId>
		<version>1.1.3.M1</version>
	</parent>

	<groupId>com.neohope.springcloud.test</groupId>
	<artifactId>netflix-turbine-server</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>netflix-turbine-server</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-turbine-stream</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-stream-kafka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka-server</artifactId>
			<version>1.3.0.M1</version>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/libs-milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

3、application.properties

spring.application.name=turbine-server
server.port=7000
security.user.name=user
security.user.password=hystrix
eureka.instance.leaseRenewalIntervalInSeconds=5
eureka.instance.metadataMap.zone = zone1
eureka.server.enable-self-preservation=false
eureka.server.eviction-interval-timer-in-ms=5000
eureka.client.serviceUrl.defaultZone=http://user:hystrix@localhost:7000/eureka/
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.preferSameZoneEureka=true
manager.port=7000
turbine.stream.port=8989

4、TurbineClient

package com.neohope.springcloud.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@SpringBootApplication
@EnableEurekaClient
@RestController
@EnableCircuitBreaker
public class TurbineClient 
{
	@Value("${spring.application.name}")
	private String appName;
	
	@RequestMapping("/appName")
	public String appName() {
		return appName;
	}
	
	@RequestMapping("/")
	public String hello() {
		return "It works";
	}
	
	@RequestMapping("/invokeA")
	@HystrixCommand(fallbackMethod = "invoekD")
	public String invokeA() {
		if(Math.random()>0.2)
		{
			throw new RuntimeException("Hystrix Test A thrown");
		}
		else
		{
			return "Hystrix Test A";
		}
	}
	
	@RequestMapping("/invokeB")
	@HystrixCommand(fallbackMethod = "invoekD")
	public String invokeB() {
		if(Math.random()>0.5)
		{
			throw new RuntimeException("Hystrix Test A thrown");
		}
		else
		{
			return "Hystrix Test A";
		}
	}
	
	@RequestMapping("/invokeD")
	public String invoekD() {
		return "Hystrix Test D";
	}
	
	public static void main(String[] args)
	{
		new SpringApplicationBuilder(TurbineClient.class).web(true).run(args);
	}
}

5、pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.cloud.stream.app</groupId>
		<artifactId>app-starters-build</artifactId>
		<version>1.1.3.M1</version>
	</parent>

	<groupId>com.neohope.springcloud.test</groupId>
	<artifactId>netflix-turbine-client</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>netflix-turbine-client</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-stream-kafka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
			<version>1.3.0.M1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/libs-milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

6、application.properties

spring.application.name=turbine-client
server.port=7001
security.user.name=user
security.user.password=hystrix
eureka.client.serviceUrl.defaultZone=http://user:hystrix@localhost:7000/eureka/
eureka.client.preferSameZoneEureka=true
eureka.client.healthcheck.enabled=true
eureka.instance.metadataMap.zone = zone1
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health
eureka.instance.lease-renewal-interval-in-seconds=5
eureka.instance.lease-expiration-duration-in-seconds=10
#eureka.instance.metadata-map.management.port=7001
#management.port=7002
turbine.aggregator.clusterConfig=default
turbine.appConfig=turbine-client
turbine.stream.port=8989

7、在以下地址可以访问
http://172.16.172.63:7001/hystrix.stream
http://user:hystrix@172.16.172.63:7002/turbine.stream

Spring Cloud Netflix入门(Hystrix)

HyStrix主要负责日志收集。同时是一个很棒的断路器。

1、HystrixServer

package com.neohope.springcloud.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
import org.springframework.web.bind.annotation.RequestMapping;

@SpringBootApplication
@EnableCircuitBreaker
@EnableEurekaServer
@EnableHystrixDashboard
@EnableTurbine
public class HystrixServer 
{
	@Value("${spring.application.name}")
	private String appName;
	
	@RequestMapping("/appName")
	public String appName() {
		return appName;
	}
	
	public static void main(String[] args) {
        new SpringApplicationBuilder(HystrixServer.class).web(true).run(args);
    }
}

2、pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.cloud.stream.app</groupId>
		<artifactId>app-starters-build</artifactId>
		<version>1.1.3.M1</version>
	</parent>

	<groupId>com.neohope.springcloud.test</groupId>
	<artifactId>netflix-hystrix-server</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>netflix-hystrix-server</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka-server</artifactId>
			<version>1.3.0.M1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-turbine</artifactId>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/libs-milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

3、application.properties

spring.application.name=hystrix-server
server.port=7000
security.user.name=user
security.user.password=hystrix
eureka.instance.leaseRenewalIntervalInSeconds=5
eureka.instance.metadataMap.zone = zone1
eureka.server.enable-self-preservation=false
eureka.server.eviction-interval-timer-in-ms=5000
eureka.client.serviceUrl.defaultZone=http://user:hystrix@localhost:7000/eureka/
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.preferSameZoneEureka=true
#turbine.instanceUrlSuffix=/hystrix.stream
turbine.aggregator.clusterConfig=default
turbine.appConfig=hystrix-client01,hystrix-client02
#eureka.instance.metadata-map.management.port=${management.port:7000}
#management.port=7000

4、HystrixClient01

package com.neohope.springcloud.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@SpringBootApplication
@EnableEurekaClient
@RestController
@EnableCircuitBreaker
public class HystrixClient01 {
	
	@Value("${spring.application.name}")
	private String appName;
	
	@RequestMapping("/appName")
	public String appName() {
		return appName;
	}
	
	@RequestMapping("/")
	public String hello() {
		return "It works";
	}
	
	@RequestMapping("/invokeA")
	//@HystrixCommand(fallbackMethod = "invoekD", 
	//commandProperties = {@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")})
	@HystrixCommand(fallbackMethod = "invoekD")
	public String invokeA() {
		if(Math.random()>0.2)
		{
			throw new RuntimeException("Hystrix Test A thrown");
		}
		else
		{
			return "Hystrix Test A";
		}
	}
	
	@RequestMapping("/invokeB")
	@HystrixCommand(fallbackMethod = "invoekB")
	public String invokeB() {
		if(Math.random()>0.5)
		{
			throw new RuntimeException("Hystrix Test B thrown");
		}
		else
		{
			return "Hystrix Test B";
		}
	}
	
	@RequestMapping("/invokeD")
	public String invoekD() {
		return "Hystrix Test D";
	}
	
	public static void main(String[] args)
	{
		new SpringApplicationBuilder(HystrixClient01.class).web(true).run(args);
	}
}

5、pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.cloud.stream.app</groupId>
		<artifactId>app-starters-build</artifactId>
		<version>1.1.3.M1</version>
	</parent>

	<groupId>com.neohope.springcloud.test</groupId>
	<artifactId>netflix-hystrix-client01</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>netflix-hystrix-client01</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
			<version>1.3.0.M1</version>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/libs-milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

6、application.properties

spring.application.name=hystrix-client01
server.port=7001
security.user.name=user
security.user.password=hystrix
eureka.client.serviceUrl.defaultZone=http://user:hystrix@localhost:7000/eureka/
eureka.client.preferSameZoneEureka=true
eureka.client.healthcheck.enabled=true
eureka.instance.metadataMap.zone = zone1
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health
eureka.instance.lease-renewal-interval-in-seconds=5
eureka.instance.lease-expiration-duration-in-seconds=10

7、HystrixClient02

package com.neohope.springcloud.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//import org.springframework.cloud.netflix.turbine.EnableTurbine;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@SpringBootApplication
@EnableEurekaClient
@RestController
@EnableCircuitBreaker
//@EnableTurbine
public class HystrixClient02
{
	@Value("${spring.application.name}")
	private String appName;
	
	@RequestMapping("/appName")
	public String appName() {
		return appName;
	}
	
	@RequestMapping("/")
	public String hello() {
		return "It works";
	}
	
	@RequestMapping("/invokeA")
	@HystrixCommand(fallbackMethod = "invoekD")
	public String invokeA() {
		if(Math.random()>0.2)
		{
			throw new RuntimeException("Hystrix Test A thrown");
		}
		else
		{
			return "Hystrix Test A";
		}
	}
	
	@RequestMapping("/invokeB")
	@HystrixCommand(fallbackMethod = "invoekB")
	public String invokeB() {
		if(Math.random()>0.5)
		{
			throw new RuntimeException("Hystrix Test B thrown");
		}
		else
		{
			return "Hystrix Test A";
		}
	}
	
	@RequestMapping("/invokeD")
	public String invoekD() {
		return "Hystrix Test D";
	}
	
	public static void main(String[] args)
	{
		new SpringApplicationBuilder(HystrixClient02.class).web(true).run(args);
	}
}

8、pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.cloud.stream.app</groupId>
		<artifactId>app-starters-build</artifactId>
		<version>1.1.3.M1</version>
	</parent>

	<groupId>com.neohope.springcloud.test</groupId>
	<artifactId>netflix-hystrix-client02</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>netflix-hystrix-client02</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>
		<!--dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-turbine</artifactId>
		</dependency-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
			<version>1.3.0.M1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/libs-milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

9、application.properties

spring.application.name=hystrix-client02
server.port=7002
security.user.name=user
security.user.password=hystrix
eureka.client.serviceUrl.defaultZone=http://user:hystrix@localhost:7000/eureka/
eureka.client.preferSameZoneEureka=true
eureka.client.healthcheck.enabled=true
eureka.instance.metadataMap.zone = zone1
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health
eureka.instance.lease-renewal-interval-in-seconds=5
eureka.instance.lease-expiration-duration-in-seconds=10


10、可以在下面的地址,查看日志输出
http://172.16.172.63:7001/hystrix.stream