好久没写东西分享最近看的东西了,一直看的比较杂什么都在看,但没有深层次的理解。也不拿不出手来分享,一般都是感觉项目用的上这个东西就去看,最后决定上不上。
最近在微信公众号,想做下前后端分离,但是遇到个问题就是前后端分离后Ajax调用存在跨域的风险,当然解决跨域的方法很多,包括使用jsonp返回的时候带上callback等等。由于一直在看Springboot,发现了Hystrix开源出来的hystrix dashboard可以监控服务,最后在加上SpringMVC中的@CrossOrigin。即完美的解决了跨域的问题,还能监控各个服务的调用情况。这里就不在介绍dashboard了,写的也很粗,有问题的地方可以多指教。
1.创建SpringBoot项目
2.引入Maven依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
3.创建SpringBoot启动类以及注册Hystrix Servlet
GatewayApplication.java
@SpringBootApplication
@RestController
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class);
}
@RequestMapping("/")
public String index() {
return "Hello Word!";
}
}
HystrixConfig.java
@Configuration
public class HystrixConfig {
@Bean
public HystrixMetricsStreamServlet hystrixMetricsStreamServlet(){
return new HystrixMetricsStreamServlet();
}
@Bean
public ServletRegistrationBean registration(HystrixMetricsStreamServlet servlet){
ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.setServlet(servlet);
registrationBean.setEnabled(true);//是否启用该registrationBean
registrationBean.addUrlMappings("/hystrix.stream");
return registrationBean;
}
@Bean(initMethod="start")
public UrlConfigThread urlThread(){
UrlConfigThread urlConfig=new UrlConfigThread();
return urlConfig;
}
}
4.创建访问入口Controller
这里使用@CrossOrigin解决ajax跨域的问题,spring4.2增加的特性,并且区分get与post请求,以此来传递参数
HystrixController.java
@CrossOrigin
@RestController
@RequestMapping("/hystrix")
public class HystrixController {
@Autowired
private HystrixUtil hystrixUtil;
@RequestMapping(value = "/{ServiceName}/{ServiceMethod}", method = { RequestMethod.GET, RequestMethod.POST })
public ResultInfo HystrixData(@PathVariable String ServiceName,@PathVariable String ServiceMethod, HttpServletRequest request,HttpServletResponse response) throws InterruptedException, ExecutionException{
boolean isGet = request.getMethod().toLowerCase().equals("get");
Map<String, String> Parameter=getParameter(request);
String url=UrlProperties.get(ServiceMethod);
ResultInfo info=new ResultInfo();
if(isGet){
info= hystrixUtil.execute(ServiceName, ServiceMethod,url,Parameter,"GET");
}else{
info= hystrixUtil.execute(ServiceName, ServiceMethod,url,Parameter,"POST");
}
return info;
}
/**
* 取出请求的参数
*/
public Map<String, String> getParameter(HttpServletRequest request){
Enumeration<String> param = request.getParameterNames();
Map<String, String> map = new HashMap<String,String>();
while(param.hasMoreElements()){
String key = param.nextElement();
String value = request.getParameter(key);
map.put(key, value);
}
return map;
}
}
5.创建HystrixUtil和CommandHttpCall
这里就是主要的走Hystrix的类,创建HystrixUtil转一次主要为了区分各个服务的名称
HystrixUtil.java
@Component
public class HystrixUtil {
public ResultInfo execute(String ServiceName, String ServiceMethod,String url, Map<String, String> Parameter,String RequestType) throws InterruptedException, ExecutionException {
Setter setter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(ServiceName));//被调用服务
setter.andCommandKey(HystrixCommandKey.Factory.asKey(ServiceMethod));//被调用服务的一个被调用方法
setter.andCommandPropertiesDefaults( HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(2)
.withCircuitBreakerSleepWindowInMilliseconds(60 * 1000).
withFallbackEnabled(true).
withExecutionIsolationThreadInterruptOnTimeout(true).withExecutionTimeoutInMilliseconds(5000));
/* 使用HystrixThreadPoolKey工厂定义线程池名称*/
/*当对同一业务依赖做隔离时使用CommandGroup做区分,
* 但是对同一依赖的不同远程调用如(一个是redis 一个是http),
* 可以使用HystrixThreadPoolKey做隔离区分.
* 最然在业务上都是相同的组,但是需要在资源上做隔离时,s
* 可以使用HystrixThreadPoolKey区分
* */
return new CommandHttpCall(setter, url, Parameter,RequestType).execute();//同步执行
//Future<z> future = new CommandHttpCal(setter, url).queue();//异步执行
//return future.get();//需要时获取
}
}
CommandHttpCall.java
public class CommandHttpCall extends HystrixCommand<ResultInfo>{
private final String url;
private Map<String, String> Parameter;
private String RequestType;
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CommandHttpCall.class);
public CommandHttpCall(Setter setter,String url,Map<String, String> Parameter,String RequestType) {
super(setter);
this.url=url;
this.Parameter=Parameter;
this.RequestType=RequestType;
}
@Override
protected ResultInfo run() throws Exception {
System.out.println("服务正被调用,当前线程:'{}'"+ Thread.currentThread().getName());
ResultInfo info = new ResultInfo(200,"ok");
if(RequestType.equals("GET")){
info=new GetHttpClient(url,Parameter).execute();
}else if(RequestType.equals("POST")){
info=new PostHttpClient(url,Parameter).execute();
}
/*Request request = new Request.Builder().url(url).build();
System.out.println(JSON.toJSONString(new OkHttpClient().newCall(request).execute().body().toString()));
info.setObj(JSON.toJSONString(new OkHttpClient().newCall(request).execute().body().toString()));*/
return info;
}
@Override
protected ResultInfo getFallback() {
return new ResultInfo(500,"服务异常,请稍后重试!");
}
}
到这里基本就是主要的代码了,为了能更灵活的使用它,增加了线程调用解析XML中配置的最终跳转的url
6.创建gateway_url.xml
<?xml version="1.0" encoding="UTF-8"?>
<urls>
<host>
http://192.168.0.56
</host>
<!-- url 路由配置-->
<items>
<item ServiceMethod="countylist" targeturl="/hsWxServices/county/countylist.action">
</items>
</urls>
host节点作为访问的IP加端口域名也是可以的 items下item就是各个服务的路径 ServiceMethod作为key取对应的targeturl
7.创建UrlConfigThread和UrlProperties
使用线程去扫面并放到map中避免造成重复解析url
UrlConfigThread.java
public class UrlConfigThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
String host = "";
try {
SAXReader reader = new SAXReader();
Document document = reader.read(this.getClass().getClassLoader().getResourceAsStream("gateway_url.xml"));
Element root = document.getRootElement();
//先获取host
host = root.elementText("host").trim();
//获取全部的路由
Element items= root.element("items");
for(Iterator it=items.elementIterator();it.hasNext();){
Element element = (Element) it.next();
String ServiceMethod = element.attribute("ServiceMethod").getText();
String targeturl = element.attribute("targeturl").getText();
/*UrlConfig config = new UrlConfig();
config.setServiceMethod(ServiceMethod);
config.setTargeturl(targeturl);*/
UrlProperties.put(ServiceMethod, host+targeturl);
}
} catch (Exception e) {
}
}
}
UrlProperties.java
public class UrlProperties {
private static final Map<String, String> urlconfig = new HashMap<String , String>();
private UrlProperties(){}
public static void put(String key,String url){
UrlProperties.urlconfig.put(key, url);
}
public static String get(String key){
return UrlProperties.urlconfig.get(key);
}
}
8.下载dashboard与启动
下载地址:https://repo1.maven.org/maven2/com/netflix/hystrix/hystrix-dashboard/1.5.9/
下载jar或者war看你自己了,启动hystrix-dashboard和刚刚创建的项目
输入localhost:8001/hystrix.stream,之后点击”Add Stream”,最后点击”Monitor Stream”即可。
至此项目的创建已经完成了,注意我在CommandHttpCall中区分了get和post,这里由于存在多种可能行比如你的post需要提交json参数而非key–value或者form表单等,http的代码我就不贴了。