微服务是一种架构模式或者一种架构风格,提倡将单一应用程序划分成一组小的服务,每个服务独立部署,服务之间相互配合、相互协调,每个服务运行于自己的进程中。服务与服务间采用轻量级通讯模式,如HTTP的RESTful API等。避免统一的、集中式的服务管理机制。
单体应用与微服务应用对比:
优缺点:
微服务架构需要的功能
SpringBoot版本 | SpringCloud版本 |
---|---|
1.2.x | Angel版本 |
1.3.x | Brixton版本 |
1.4.x | Camden版本 |
1.5.x | Dalston版本、Edgware版本 |
1.5.x | Dalston版本、Edgware版本 |
2.0.x | Finchley版本 |
2.1.x | Greenwich版本 |
… | …H |
<properties>
<spring-cloud.version>Greenwich.RELEASEspring-cloud.version>
properties>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.3.RELEASEversion>
<relativePath/>
parent>
<packaging>pompackaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
@SpringBootApplication
@EnableEurekaServer // 开启EurekaServer 启用注册中心服务
public class YoulexuanReistyApplication {
public static void main(String[] args) {
SpringApplication.run(YoulexuanReistyApplication.class, args);
}
}
server:
port: 8761 #注册服务端口 8761
eureka:
instance:
#当前ip名称
hostname: localhost
client:
#是否将自己注册到Eureka服务中,本身就是,所以无需注册
registerWithEureka: false
#是否从Eureka中获取注册信息
fetchRegistry: false
#EurekaServer注册中心地址
serviceUrl:
#注册中心地址 localhost:8761/eureka/ 以逗号分割,可以写多个注册中心
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
#当前工程的名称
name: eurka-server
其中:通过eureka.client.registerWithEureka:false和fetchRegistry:false来表明自己是一个eureka server(注册中心)。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.0.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
@SpringBootApplication
@EnableEurekaClient
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
server:
# 指定服务端口
port: 8763
spring:
application:
# 需要指明spring.application.name,这个很重要!!!,
# 这在以后的服务与服务之间相互调用一般都是根据这个name,name不区分大小写
name: youlexuan-user
eureka:
client:
serviceUrl:
#注册中心地址 localhost:8761/eureka/ 以逗号分割,可以写多个注册中心
defaultZone: http://localhost:8761/eureka/
@RestController
public class UserService {
@Value("${server.port}")
String port ;
@GetMapping("/user/{id}")
public String getUserById(@PathVariable String id){
User user = new User();
user.setId(id);
user.setName("test :" + port);
return user.toString();
}
@DeleteMapping("/user/{id}")
public String deleteByid(@PathVariable String id){
return "根据id删除用户 = "+id + "," + port;
}
}
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.0.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
<version>2.0.1.RELEASEversion>
dependency>
server:
port: 8765
spring:
application:
name: youlexuan-order
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
@Configuration
public class OrderServiceConfig {
@Bean //注入bean
@LoadBalanced //restTemplate在请求某一个rest接口时,会进行负载均衡。
RestTemplate restTemplate() {
return new RestTemplate();
}
}
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class YoulexuanOrderApplication {
public static void main(String[] args) {
SpringApplication.run(YoulexuanOrderApplication.class, args);
}
}
@RestController
public class OrderService {
@Autowired
RestTemplate restTemplate;
@Value("${server.port}")
String port;
@GetMapping("/orders")
public String getOrders(){
return "查询所有订单"+port;
}
//get的方式调用 getForobject
@GetMapping("/orders/{oid}/{uid}")
public String getOrderAndUser(@PathVariable(name = "oid") String oid,@PathVariable String uid){
String str = restTemplate.getForObject("http://YOULEXUAN-USER/user/" + uid, String.class);
return "查询指定用户的订单"+oid+","+uid+","+str;
}
//post的方式调用 postForObject
@PostMapping("/orders")
public String getOrderAndUser2(User user){
MultiValueMap<String, Object> param = new LinkedMultiValueMap<String, Object>();
param.add("id", user.getId());
param.add("name", user.getName());
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/x-www-form-urlencoded");
HttpEntity<MultiValueMap<String, Object>> formEntity =
new HttpEntity<MultiValueMap<String, Object>>(param, headers);
//含有请求头
String result = restTemplate.postForObject("http://YOULEXUAN-USER/user/", formEntity, String.class);
return result;
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--每一个服务都要加一个client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
eureka:
client:
serviceUrl:
# 服务注册地址
defaultZone: http://localhost:8761/eureka/
server:
# 端口号
port: 8765
spring:
application:
# 消费者名称
name: youlexuan-product
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class YoulexuanProductApplication {
public static void main(String[] args) {
SpringApplication.run(YoulexuanProductApplication.class, args);
}
}
//@ FeignClient(“服务名”) //远程服务调用,指定名称
@FeignClient(value = "YOULEXUAN-ORDER")
public interface OrderService {
////远程服务的方法
@GetMapping("/orders")
public String getAllOrders();
@GetMapping("/orders/{oid}/{uid}")
public String getOrderAndUser(@PathVariable(name = "oid") String oid, @PathVariable String uid);
}
@Autowired
OrderService orderService; //忽略此处的编译错误!!
@GetMapping("/product")
public String getProduct(){
String allOrders = orderService.getAllOrders();
return "Product中getProduct调用订单服务:"+allOrders;
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
<version>2.0.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.0.1.RELEASEversion>
dependency>
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
public class YoulexuanApigatewayApplication {
public static void main(String[] args) {
SpringApplication.run(YoulexuanApigatewayApplication.class, args);
}
}
eureka:
client:
serviceUrl:
# 指定服务注册中心的地址为http://localhost:8761/eureka/
defaultZone: http://localhost:8761/eureka/
server:
# 服务的端口为8767
port: 8767
spring:
application:
name: youlexuan-apigateway
zuul:
routes:
# 服务名为YOULEXUAN-USER;以/service-user/ 开头的请求都转发给YOULEXUAN-USER服务;
# 以/service-order/开头的请求都转发给YOULEXUAN-ORDER服务; ..
api-user:
path: /service-user/**
serviceId: YOULEXUAN-USER
api-order:
path: /service-order/**
serviceId: YOULEXUAN-ORDER
api-product:
path: /service-product/**
serviceId: YOULEXUAN-PRODUCT
/**
* 本示例演示 传递token,等于1才可以进行访问
* 验证客户端http请求是否具有权限
* 获取每一个请求的token参数,token=1允许请求到目标服务,token=0不允许
*
*/
@Component
public class AuthenticationFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";// pre: 路由之前 前置filter
}
@Override
public int filterOrder() { //filter排序 顺序
return 0;
}
@Override
public boolean shouldFilter() { //filter 开关
return true;
}
@Override
public Object run() throws ZuulException {
//拦截器执行逻辑
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String token = request.getParameter("token");
ctx.getResponse() .setContentType("text/html;charset=utf-8");
if (token!=null&&token.equals("1")){
System.out.println("验证成功");
}else {
ctx.setSendZuulResponse(false); //不允许访问 不允许请求到目标服务
ctx.setResponseStatusCode(401);
System.out.println("验证失败");
try {
ctx.getResponse().getWriter().write("{'code':401,'msg':'token不合法','token':'"+token+"'}");
}catch (Exception e){
}
}
return null;
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.0.1.RELEASEversion>
dependency>
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class YoulexuanConfigserverApplication {
public static void main(String[] args) {
SpringApplication.run(YoulexuanConfigserverApplication.class, args);
}
}
server:
port: 8768
spring:
application:
name: youlexuan-configserver
cloud:
config:
server:
git:
# 项目地址(http) 先在github创建一个仓库名叫youlexuan.git
uri: https://github.com/Tang-Cyi/youlexuan.git
# github账号
username: [email protected]
# github密码
password: xxxxxxxxx
# 设置超时时间
timeout: 60000
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: configclient
cloud:
config:
label: master
profile: dev
uri: http://localhost:8767/
server:
port: 8768
@Value("${redis.port}")
String redis_port;
@GetMapping("/getRedisPort")
public String getRedis_port(){
return " redis_port : " + redis_port;
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
youlexuan-product bootstrap.yml
server:
port: 8081
spring:
application:
# spring.application.name,这个很重要!!!,这在以后的服务与服务之间相互调用一般都是根据这个name
name: youlexuan-product
cloud:
config:
label: master
profile: dev
discovery:
# 从配置中心读取文件
enabled: true
# 指定配置中心的服务名
service-id: YOULEXUAN-CONFIGSERVER
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
#
management:
endpoints:
web:
exposure:
# 暴露端点接口 refresh 在post方式请求http://localhost:8081/actuator/refresh时刷新配置文件
include: refresh,health,info
@RestController
@RefreshScope
public class ProductService {
@Value("${spring.redis.host}")
String redisHost;
@Value("${spring.redis.port}")
String redisPort;
@Autowired
OrderService orderService;
@GetMapping("/product")
public String getProduct(){
String allOrders = orderService.getAllOrders();
return "Product中getProduct调用订单服务:"+allOrders+",redisPort:"+redisPort+",redisHost:"+redisHost;
}
}
spring:
application:
name: configclient
cloud:
config:
label: master
profile: dev
discovery:
# 从配置中心读取文件
enabled: true
# 指定配置中心的服务名
service-id: YOULEXUAN-CONFIGSERVER
# uri只能找到唯一实例,对高可用配置中心配置不可用 所以注释掉,
# 此时改为service-id,根据service-id来找配置中心
# uri: http://localhost:8767/
# 在读取配置文件不再写ip地址,而是配置中心的服务名,
# 将配置中心多运行几个实例8082/8083,形成集群,通过负载均衡,从而高可用。
server:
port: 8768
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
#注册服务端口 8761 8082 8083
port: 8761
eureka:
instance:
# 当前ip名称
hostname: localhost
# **记录实例所在的ip**
prefer-ip-address: true
client:
# 是否将自己注册到Eureka服务中,本身就是所以无需注册
registerWithEureka: false
# 是否从Eureka中获取注册信息
fetchRegistry: false
# EurekaServer注册中心地址
serviceUrl:
# 注册中心地址 localhost:8761/eureka/
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# defaultZone: http://localhost:8761/eureka/,http://localhost:8083/eureka/
defaultZone: http://localhost:8761/eureka/
server:
enable-self-preservation: false
spring:
application:
# 当前工程的名称
name: eurka-server
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/,http://localhost:8763/eureka/
feign.hystrix.enabled: true
server:
# 8766 8081
port: 8081
spring:
application:
# 需要指明spring.application.name,这个很重要!!!,这在以后的服务与服务之间相互调用一般都是根据这个name
name: youlexuan-product
cloud:
config:
label: master
profile: dev
discovery:
# 从配置中心读取文件
enabled: true
# 指定配置中心的服务名
service-id: YOULEXUAN-CONFIGSERVER
# uri只能找到唯一实例 所以注释掉
# uri: http://localhost:8768/
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/,http://localhost:8082/eureka/,http://localhost:8083/eureka/
management:
endpoints:
web:
exposure:
include: refresh,health,info
# 开启断路器
feign:
hystrix:
enabled: true
//远程服务调用,指定名称
@FeignClient(value = "YOULEXUAN-ORDER",fallback = NativeOrderService.class)
public interface OrderService {
////远程服务的方法
@GetMapping("/orders")
public String getAllOrders();
@GetMapping("/orders/{oid}/{uid}")
public String getOrderAndUser(@PathVariable(name = "oid") String oid, @PathVariable String uid);
}
其中注意:NativeOrderService需要实现OrderService 接口,并注入到Ioc容器中:
@Component
public class NativeOrderService implements OrderService {
////远程服务的方法
@Override
public String getAllOrders() {
return "{'code':401,'msg':'远程服务不可用,执行本地代码','method':'getAllOrders'}";
}
@Override
public String getOrderAndUser(String oid, String uid) {
return "{'code':402,'msg':'远程服务方法getOrderAndUser不可用,执行本地代码','method':'getOrderAndUser'}";
}
}