SpringCloud
1.单体应用
项目所有的资源、模块、业务等都在一个应用中,整个项目打包成一个War包,通过一个tomcat运行,这样的应用就是单体应用,多模块应用如果最终仍然打成一个war包,那么他仍然是一个单体应用。
缺点:
1.一个模块挂掉,整个项目挂掉
2.一个模块升级,整个项目重启
3.项目开发越久,代码越臃肿,维护困难/开发困难
4.单体tomcat对高并发的支持有限,顶不住高并发(可以通过集群解决—不能局部扩展)
5.技术选型单一
6.数据库选型一般比较单一
优点:
1.技术体系简单
2.初期开发成本低
3.初期开发效率高-项目搭建快
4.项目小使用单体应用性能很好
2.微服务(分布式)架构
微服务是一种架构思想,它提倡把一个大的系统拆分成多个小的服务(独立的项目),这些微服务都有自己的容器(Tomcat)/进程,他们是独立的,他们之间通过网络协议进行数据交互,共同协作。
优点:
1.一个微服务死掉/升级,不会影响到其他服务
2.单个微服务,只关心一个业务即可,代码量少,维护/开发都方便
3.每个微服务有自己的数据库—按照业务分库
4.技术选型多样化
5.处理高并发问题比较好,局部拓展方便(单模块集群方便)
6.数据库选型多样化
缺点:
1.数据通信性能不好
2.技术成本高
3.开发麻烦,难度大
4.成本高!!!
MicroService(微服务)架构
服务注册
项目形式
1.parent管理
这里主要是pom.xml中
一方面管理springboot依赖
<!--springboot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
另一方面管理springcloud依赖–控制版本等参数
<!--springcloud-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
还有子模块共用的配置和一些共用的依赖也可以在这里导入
<!--所有模块都需要的配置-可以继承-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!--测试类,所有模块都会用到-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
Eureka–server 服务端–注册中心
1.导包
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
2.主配置类
@SpringBootApplication
@EnableEurekaServer//开启服务
public class Application {
/**
* 启动方法
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(Application.class);
//new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
3application.yml
server:
port: 1000 #端口
eureka:
instance:
hostname: localhost #主机名称
client:
registerWithEureka: false #禁止将自己注册到自己
fetchRegistry: false #禁止获取注册清单
serviceUrl: #获取上面的hostname 以及 port
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #-这里配置的这个主要是客户端要连接到服务端的连接
order-server
现在把他当做消费者端 -后续会配置其他东西
配置方式类似于注册中心,一些细小的差别
1.导包—这里导包是client客户端
<dependencies>
<!--客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.主配置文件ApplicationConfig
因为这里用到了控制层,所以前面导入依赖的时候导入了spring-boot-starter-web
@SpringBootApplication
@RestController
public class ApplicationConfig {
@RequestMapping("/")
public String home() {
return "Hello world";
}
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class);
}
}
3.application.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1000/eureka/ #连接服务器端
instance:
instance-id: order-server #当前客户端服务的id
prefer-ip-address: true # 使用ip进行注册
server:
port: 2000 #当前服务端口号
spring:
application:
name: order-server #当前应用名称
微服务之间的通信
1.返回Json字符串的方式
2.公共模块,添加依赖-------推荐
公共模块方式实现
1.准备domain
1.新建模块
2.新建domain对象
3.需要用到的模块,依赖(先打jar包–install一下)注意:父模块引用管理版本
2.准备Controller
1.UserServerController–@GetMapping
@RestController
@RequestMapping("/userserver")
public class UserServerController {
//get请求
@GetMapping("/user/{id}")//这里是被Order服务调用
public User getUserById(@PathVariable Long id){
//模拟数据库返回假数据
return new User(id,"蔡徐坤","唱跳rap篮球");
}
}
2.OrderServerController
@RequestMapping("/orderserver")
@RestController
public class OrderServerController {
@Autowired
private RestTemplate restTemplate;
//get请求
@GetMapping("/user/{id}")//这里是被Order服务调用
public User getUserById(@PathVariable Long id){
return restTemplate.getForObject("http://localhost:3000/userserver/user/"+id,User.class);
}
}
//这里我们准备了一个配置类,将RestTemplate交给spring管理
@Configuration
public class BeanConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
----调用用户服务的接口
RestTemplate.getForObject(url,User.class);
----浏览器调用订单服务提供的接口
Eureka集群–高可用
1.hosts文件添加peer1,peer2
127.0.0.1 peer1
127.0.0.1 peer1
2.yml配置文件
spring:
profiles:
active: peer1
---
spring:
profiles: peer1
server:
port: 1000 #端口
eureka:
instance:
hostname: peer1 #主机名称
client:
registerWithEureka: false #禁止将自己注册到自己
fetchRegistry: false #禁止获取注册清单
serviceUrl: #获取上面的hostname 以及 port
defaultZone: http://peer2:1001/eureka/ #-这里配置的这个主要是客户端要连接到服务端的连接
---
spring:
profiles: peer2
server:
port: 1001 #端口
eureka:
instance:
hostname: peer2 #主机名称
client:
registerWithEureka: false #禁止将自己注册到自己
fetchRegistry: false #禁止获取注册清单
serviceUrl: #获取上面的hostname 以及 port
defaultZone: http://peer1:1000/eureka/ #-这里配置的这个主要是客户端要连接到服务端的连接
其他微服务yml改变
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #连接服务器端
客户端负载均衡
1.Ribbon
Ribbon通过算法调用端口
1.集群user-server
c-v 改东西
user-server-pom.xml
<artifactId>user-server-3001</artifactId>
<name>user-server-3001</name>
parent-pm.xml
<module>user-server-3001</module>
application.yml
eureka:
client:
serviceUrl:
defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #连接服务器端
instance:
instance-id: user-server:3001 #当前客户端服务的id
prefer-ip-address: true # 使用ip进行注册
server:
port: 3001 #端口号 端口号不同
spring:
application:
name: user-server #当前应用名称 应用名称相同
2.ribbon实现
导入依赖
<!--导入ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
RestTemplate上打@LoadBalanced:让RestTemplate有负载均衡的能力
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
3.Ribbon底层源码分析
1.@LoadBalanced赋予RestTemplate有负载均衡的能力
2.RestTemplate底层通过RibbonLoadBalancerClient客户端工具类实现
3.RibbonLoadBalancerClient.execute的操作
1.通过服务名获取一个LoadBalancer
2.通过LoadBalancer.chooseServer选中服务(会加载所有端口)
3.在LoadBalancer.chooseServer方法中会调用IRule.choose方法进行服务的选择
4.IRule.choose方法按照某种算法选择服务并返回
5.然后使用Http调用服务
4.配置负载均衡算法
配置Bean
@Bean
public IRule randomRule(){
return new RandomRule();
}
2.Feign
调用更像本地调用
1.导包
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.主配置类上打注解@EnableFeignClients–开启
@SpringBootApplication
@EnableFeignClients//开启feign
public class OrderApplicationConfig {
public static void main(String[] args) {
SpringApplication.run(OrderApplicationConfig.class);
}
}
3.写接口
/**
* feign的接口,这样写一个接口类
* 在控制层就像调用自己的东西一样
* 底层和ribbon类似
* @FeignClient("user-server")
* 以及下面的
* @GetMapping("/userserver/user/{id}")
* 会进行拼接
* http://user-server/userserver/user/999
* 然后还是类似于ribbon
*/
@FeignClient("user-server")
@Component
public interface UserFeignClient {
//调用根据id获取用户的接口
@GetMapping("/userserver/user/{id}")
User getUserById(@PathVariable("id")Long id);
}
接口要注意
-
@GetMapping("/userserver/user/{id}")里是我们要访问的路径,要写全
-
User getUserById(@PathVariable(“id”)Long id);返回值类型,参数列表要一致,方法名可以不同
-
@FeignClient(“user-server”)这里指向的是我们将要调用的服务,调用的是服务名
spring: application: name: user-server #当前应用名称
4.控制层调用
//自动注入失败就去接口添加@Component
@Autowired
private UserFeignClient userFeignClient;//这里实际上是代理类在运行不是接口
//浏览器调用,根据id获取用户 : 有负载均衡
@GetMapping("/user/{id}")
public User getUserById(@PathVariable("id")Long id){
//调用我们写的feign的接口
return userFeignClient.getUserById(id);
}
注意服务器先启动被调用的服务,也就是提供者,再启动调用的服务,也就是消费者
Hystrix 断路器
Ribbon
1.导包
<!--Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.注解—主配置类开启注解
@EnableCircuitBreaker
3.Controller回调方法
@RequestMapping("/orderserver")
@RestController
public class OrderServerController {
@Autowired
private RestTemplate restTemplate;
//浏览器调用,根据id获取用户 : 有负载均衡
@GetMapping("/user/{id}")
@HystrixCommand(fallbackMethod = "getUserByIdFallBack")//托底方法
public AjaxResult getUserById(@PathVariable("id")Long id){
//RestTemplate 调用用户服务的获取user的接口
String url = "http://user-server/userserver/user/"+id;
//调用方法获取用户
User user = restTemplate.getForObject(url, User.class);
//AjaxResult对象
AjaxResult ajaxResult = new AjaxResult();
//设置要传递的参数对象
ajaxResult.setData(user);
//返回
return ajaxResult;
}
//这里我们返回AjaxResult,后面都会用这种形式进行规范
public AjaxResult getUserByIdFallBack(@PathVariable("id")Long id){
//AjaxResult对象
AjaxResult ajaxResult = new AjaxResult();
ajaxResult.setSuccess(false);
//设置错误信息
ajaxResult.setMsg("服务器开小差了!");
return ajaxResult;
}
}
备注----准备AjaxResult
public class AjaxResult {
//默认为true
private boolean success = true;
private String msg;
private Object data;
public AjaxResult() {
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
Feign–本身就集成了Hystrix
1.开启Hystrix feign.hystrix.enabled = true
feign:
hystrix:
enabled: true
2.方法
@FeignClient( value = "user-server" , fallback = UserFeignClientFallback.class)
//fallback = UserFeignClientFallback.class 引入托底类
@Component
public interface UserFeignClient {
//调用根据id获取用户的接口
@GetMapping("/userserver/user/{id}")
User getUserById(@PathVariable("id")Long id);
}
1.实现本身
@Component//fallback方法类
public class UserFeignClientFallback implements UserFeignClient {
@Override
public User getUserById(Long id) {
System.out.println("1111111111111");
return null;
}
}
2.实现工厂—一般不用这种方式 这是官网的案例
@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello iFailSometimes();
}
@Component
static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
@Override
public HystrixClient create(Throwable cause) {
return new HystrixClient() {
@Override
public Hello iFailSometimes() {
return new Hello("fallback; reason was: " + cause.getMessage());
}
};
}
}
网关(Zuul)
1.导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.开启Zuul代理 ----- 主配置类注解
@EnableZuulProxy//开启网关代理
3.配置文件application.yml
server:
port: 4000
spring:
application:
name: zuul
eureka:
client:
service-url:
defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #连接服务器端
instance:
instance-id: zuul
prefer-ip-address: true
zuul:
ignored-services: "*" #禁止所有服务使用默认的服务名进行访问
routes:
order-server: "/order/**" #order-server服务使用 "/order/**"路径访问
# prefix: "/dsq" #前缀前要加 /
执行流程
loginFilter
@Component
public class LoginFilter extends ZuulFilter {
/**
* filter类型--前置后置等
* @return
*/
@Override
public String filterType() {
return PRE_TYPE;//"pre"
}
/**
* 执行顺序
* @return
*/
@Override
public int filterOrder() {
return 1;
}
/**
* 可以理解为放行资源的方法
* 返回true是表示要拦截
* 返回false表示放行
* @return
*/
@Override
public boolean shouldFilter() {
//获取上下文对象
RequestContext currentContext = RequestContext.getCurrentContext();
//获取请求对象
HttpServletRequest request = currentContext.getRequest();
//获取uri
String requestURI = request.getRequestURI();
//判断请求--后续可以放一个配置文件中然后遍历进行放行
if(requestURI.toUpperCase().contains("LOGIN")){
return false;
}
return true;//执行下面的run拦截
}
/**
* 拦截器的主要逻辑代码
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
//获取上下文对象
RequestContext currentContext = RequestContext.getCurrentContext();
//获取请求对象
HttpServletRequest request = currentContext.getRequest();
//获取响应对象
HttpServletResponse response = currentContext.getResponse();
//设置响应的数据的格式
response.setContentType("application/json;charset=utf-8");
//获取请求头信息
String token = request.getHeader("x-token");
//为空表示没有登陆
if (StringUtils.isBlank(token)){
//准备map
Map<String,Object> map =new HashMap<>();
//放信息进去
map.put("success",false);
map.put("msg","没有登陆");
//fastjson转为json字符串
String s = JSON.toJSONString(map);
PrintWriter writer = null;
try {
//获取writer对象
writer = response.getWriter();
//将前面的json字符串打印出去
writer.print(s);
} catch (IOException e) {
e.printStackTrace();
}
//阻止放行
currentContext.setSendZuulResponse(false);
}
return null;
}
}
SpringCloud Config
概念
架构
1.码云-新建配置文件application-zuul-dev.yml
2.新建模块config-server-5000
1.注册到Eureka注册中心
2.集成Config
1.导包
<!--config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2.配置类打标签@EnableConfigServer
@EnableConfigServer
3.yml
server:
port: 5000
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/dsqzzz/pro4.git #码云仓库
username: 2219726571@qq.com #用户名
password: zzz12345 #密码
eureka:
client:
service-url:
defaultZone: http://peer1:1000/eureka/,http://peer2:1001/eureka/ #连接服务器端
instance:
instance-id: config-server:5000
prefer-ip-address: true #使用ip注册
客户端
1.导包
<!--config-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
2.yml文件改名 —bootstrap.yml 优先级提高
#执行配置中心
spring:
cloud:
config:
uri: http://localhost:5000 #配置中心地址
name: application-order
profile: dev