1.介绍Zuul
Zuul也是Netflix下的一个开源项目,主要为微服务集群提供过代理、过滤、路由等功能。前面的几节学习我们已经搭建起了spring cloud的集群服务,现在我们将使用Zuul来为我们的集群添加一层网关,这样苦户端将不必再纠结于整个集群的内部结构是怎样的,只需关心网关提供的访问借口就行,对于Spring Cloud集群来说,不必过多的暴露服务,提升了集群的安全性。
2.Spring Cloud集群的结构图
加入zuul后的结构:
相信看了这两张图,大家应该对整个集群的架构很清晰了。
3.添加新的服务在provideService
一直使用hello world,现在我们需要加点新的服务了,不能一直写hello,开心不,激动不。为了不再重新建一个项目,我们就只接在provideService里直接写了。增加一个用户服务UserApi,一个订单服务OrderApi。实际开发中这应该是两个服务,会分成两个项目,部署多个实列到多台服务器上,应为时本地玩单机就先这样写了。
添加User:
package com.demo;
public class User {
private int id;
private String name;
private String address;
private String phone;
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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
添加Order:
package com.demo;
public class Order {
private int num;
private String good;
private User user;
private double money;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getGood() {
return good;
}
public void setGood(String good) {
this.good = good;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
添加用户服务:
package com.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserApi {
@GetMapping("/api/user/{id}")
public User getUser(@PathVariable(name="id")int id){
User u1 = new User();
u1.setId(1);
u1.setAddress("北京");
u1.setName("张三");
u1.setPhone("123456");
User u2 = new User();
u2.setId(2);
u2.setAddress("河南");
u2.setName("李四");
u2.setPhone("456789");
System.out.println("请求用户服务");
if(1==id){
return u1;
}else if(2==id){
return u2;
}else{
return new User();
}
}
}
添加订单服务:
package com.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderApi {
@GetMapping("/api/order/{num}")
public Order getOrder(@PathVariable(name="num")int num){
User u1 = new User();
u1.setId(1);
u1.setAddress("北京");
u1.setName("张三");
u1.setPhone("123456");
User u2 = new User();
u2.setId(2);
u2.setAddress("河南");
u2.setName("李四");
u2.setPhone("456789");
Order o1 = new Order();
o1.setNum(1);
o1.setMoney(200);
o1.setGood("spring cloud");
o1.setUser(u1);
Order o2 = new Order();
o2.setNum(2);
o2.setMoney(89);
o2.setGood("vue");
o2.setUser(u2);
System.out.println("请求订单服务");
if(1==num){
return o1;
}else if(2==num){
return o2;
}else{
return new Order();
}
}
}
4.在服务消费者李调用新加的服务
package com.demo;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.demo.CallClient.MyFallBack;
@Component
@FeignClient(name="provider-server",fallback = MyFallBack.class) //申明调用服务
public interface CallClient {
@CacheResult
@HystrixCommand
@GetMapping("/api/hello")
String hello();
@CacheResult
@HystrixCommand
@GetMapping("/api/user/{id}")
String user(@PathVariable(name="id") int id);
@CacheResult
@HystrixCommand
@GetMapping("/api/order/{num}")
String order(@PathVariable(name="num")int num);
@Component
static class MyFallBack implements CallClient{
public String hello() {
System.out.println("调用hello api出现错误,执行回退方法");
return "error";
}
@Override
public String user(int id) {
System.out.println("调用user api出现错误,执行回退方法");
return "error";
}
@Override
public String order(int num) {
System.out.println("调用order api出现错误,执行回退方法");
return "error";
}
}
}
package com.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.HystrixCircuitBreaker;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
@RestController
@Configuration
public class CallService {
@Autowired
private CallClient ccl;
@GetMapping("/call")
public String call(){
String s = ccl.hello();
// 获取断路器
HystrixCircuitBreaker breaker = HystrixCircuitBreaker.Factory
.getInstance(HystrixCommandKey.Factory
.asKey("CallClient#hello()"));
System.out.println("断路器状态:" + breaker.isOpen());
return s;
}
@GetMapping("/user/{id}")
public String user(@PathVariable(name="id")int id){
String s = ccl.user(id);
// 获取断路器
/*HystrixCircuitBreaker breaker = HystrixCircuitBreaker.Factory
.getInstance(HystrixCommandKey.Factory
.asKey("CallClient#user()"));
System.out.println("断路器状态:" + breaker.isOpen());*/
return s;
}
@GetMapping("/order/{num}")
public String order(@PathVariable(name="num")int num){
String s = ccl.order(num);
// 获取断路器
/* HystrixCircuitBreaker breaker = HystrixCircuitBreaker.Factory
.getInstance(HystrixCommandKey.Factory
.asKey("CallClient#order()"));
System.out.println("断路器状态:" + breaker.isOpen());*/
return s;
}
}
不知道为什么新加的两个服务,无法获取断路器的状态,根据key获取的结果是null,但是根据command的默认规则就是类名#方法名,这个不晓得什么原因。修改invokerService的application.yml.command使用默认配置。
spring:
application:
name: invoker-server1
server:
port: 8093
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
circuitBreaker:
requestVolumeThreshold: 3
5. 增加一个Zuul的路由工程
spring:
application:
name: zuul-gateway
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
zuul:
routes:
service:
path: /service/**
serviceId: invoker-server1
里面我们配置了service/**的请求路径到我们的服务消费者,这里可以分开配置你的多个服务,这个service可以根据你的实际改成其他名字,比如本项目应该是user和order但是我把他们写道一个服务里去了 ,所以就用了service.
package com.demo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class App {
public static void main(String[] args) {
new SpringApplicationBuilder(App.class).run(args);
}
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</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>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
这样访问:
http://localhost:8080/service/call,
http://localhost:8080/service/order/2,
http://localhost:8080/service/user/2。
就可以访问刚才我们新加的用户,订单服务。不过缓存好像实效了,这个需要研究下,毕竟也是学习别人的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。